Bug fixes and new biome lint update

This commit is contained in:
Sean Edmunds
2025-06-17 17:00:29 -05:00
parent 4a77340ba5
commit 7570c287c8
26 changed files with 459 additions and 500 deletions

View File

@@ -1,19 +1,19 @@
import {
DEFAULT_THEME,
MantineProvider,
colorsTuple,
createTheme,
DEFAULT_THEME,
MantineProvider,
mergeMantineTheme,
} from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import { useCommonHooks, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import {
createBrowserRouter,
createRoutesFromElements,
Navigate,
Route,
RouterProvider,
createBrowserRouter,
createRoutesFromElements,
} from 'react-router-dom';
import { Loading, MainLayout } from './components';
import '@mantine/core/styles.css';

View File

@@ -1,8 +1,8 @@
import { QueryClient } from '@tanstack/react-query';
import {
PersistQueryClientProvider,
type PersistedClient,
type Persister,
PersistQueryClientProvider,
} from '@tanstack/react-query-persist-client';
import { del, get, set } from 'idb-keyval';
import React, { type ComponentType } from 'react';

View File

@@ -32,7 +32,7 @@ export default function CurrentWeather({ current }: { current: CW }) {
}, [sunStateData]);
return (
<>
{current ? (
{current && (
<Stack
gap={0}
justify='space-between'
@@ -250,8 +250,6 @@ export default function CurrentWeather({ current }: { current: CW }) {
</Stack>
</Group>
</Stack>
) : (
<></>
)}
</>
);

View File

@@ -6,7 +6,9 @@ import WeatherIcon from './WeatherIcon';
export default function ExtendedForecastCard({
forecast,
}: { forecast: ExtendedForecast[] }) {
}: {
forecast: ExtendedForecast[];
}) {
const { isMobile } = useCommonHooks();
const day = forecast.slice(2, 14).filter((item: ExtendedForecast) => item.isDaytime);
const night = forecast.slice(2, 14).filter((item: ExtendedForecast) => !item.isDaytime);

View File

@@ -1,4 +1,4 @@
import { Stack, Text } from '@mantine/core';
import { Stack } from '@mantine/core';
import {
Area,
AreaChart,
@@ -12,34 +12,15 @@ import {
YAxis,
} from 'recharts';
import type { HourlyForecast } from 'types';
import WeatherTooltip from './WeatherTooltip';
export default function WeatherGraphs({
hourlyForecast,
}: { hourlyForecast: HourlyForecast[] }) {
function CustomTooltip({ payload, active }: any) {
if (active) {
return (
<Stack
style={{
backgroundColor: 'var(--mantine-color-background-0)',
padding: '1rem',
borderRadius: '8px',
}}
>
{payload.map((item: any) => (
<Text
key={item.name}
size='md'
c={item.stroke}
>{`${item.name} - ${item.value}${item.unit}`}</Text>
))}
</Stack>
);
}
return null;
}
}: {
hourlyForecast: HourlyForecast[];
}) {
const colorPrecipitation = 'colorPrecipitation';
const colorHumidity = 'colorHumidity';
return (
<Stack
style={{
@@ -68,7 +49,7 @@ export default function WeatherGraphs({
}}
/>
<CartesianGrid stroke='#f5f5f522' />
<Tooltip content={<CustomTooltip />} />
<Tooltip content={<WeatherTooltip />} />
<Line
unit={'°F'}
type='monotone'
@@ -103,7 +84,7 @@ export default function WeatherGraphs({
<ResponsiveContainer width={'100%'} height={150}>
<AreaChart data={hourlyForecast}>
<defs>
<linearGradient id='colorPrecipitation' x1='0' y1='0' x2='0' y2='1'>
<linearGradient id={colorPrecipitation} x1='0' y1='0' x2='0' y2='1'>
<stop
offset='40%'
stopColor='var(--mantine-color-primary-0)'
@@ -115,7 +96,7 @@ export default function WeatherGraphs({
stopOpacity={0}
/>
</linearGradient>
<linearGradient id='colorHumidity' x1='0' y1='0' x2='0' y2='1'>
<linearGradient id={colorHumidity} x1='0' y1='0' x2='0' y2='1'>
<stop
offset='20%'
stopColor='var(--mantine-color-accent-0)'
@@ -140,7 +121,7 @@ export default function WeatherGraphs({
/>
<YAxis stroke={'var(--mantine-color-primary-0)'} unit={'%'} domain={[0, 100]} />
<CartesianGrid stroke='#f5f5f522' />
<Tooltip content={<CustomTooltip />} />
<Tooltip content={<WeatherTooltip />} />
<Area
unit={'%'}
type='monotone'
@@ -150,7 +131,7 @@ export default function WeatherGraphs({
}}
stroke={'var(--mantine-color-primary-0)'}
strokeWidth={2}
fill={'url(#colorPrecipitation)'}
fill={`url(#${colorPrecipitation})`}
dot={{
stroke: 'var(--mantine-color-primary-0)',
strokeWidth: 0,
@@ -166,7 +147,7 @@ export default function WeatherGraphs({
}}
stroke={'var(--mantine-color-accent-0)'}
strokeWidth={2}
fill={'url(#colorHumidity)'}
fill={`url(#${colorHumidity})`}
dot={{
stroke: 'var(--mantine-color-accent-0)',
strokeWidth: 0,

View File

@@ -15,259 +15,167 @@ export default function WeatherIcon({ textDescription }: { textDescription: stri
? textDescription.substring(0, textDescription.trim().indexOf('then'))
: textDescription;
const Icon = () => {
const lookupSource = (icon: any) => {
switch (icon.trim()) {
case 'Ash':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Broken Clouds':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
return {
src: PartlyCloudyIcon,
alt: 'PartlyCloudyIcon',
};
case 'Chance Rain Showers':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Chance Showers':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Slight Chance Showers':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
case 'Clear':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SunnyIcon}
alt='SunnyIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Cloudy':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={CloudyIcon}
alt='CloudyIcon'
/>
);
return {
src: CloudyIcon,
alt: 'CloudyIcon',
};
case 'Dust':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Drizzle':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Fog':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Freezing Rain':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SleetIcon}
alt='SleetIcon'
/>
);
return {
src: SleetIcon,
alt: 'SleetIcon',
};
case 'Haze':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Heavy Rain':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HeavyRainIcon}
alt='HeavyRainIcon'
/>
);
return {
src: HeavyRainIcon,
alt: 'HeavyRainIcon',
};
case 'Heavy Snow':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SnowIcon}
alt='SnowIcon'
/>
);
return {
src: SnowIcon,
alt: 'SnowIcon',
};
case 'Light Rain':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Mist':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Moderate Rain':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Mostly Cloudy':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={CloudyIcon}
alt='CloudyIcon'
/>
);
return {
src: CloudyIcon,
alt: 'CloudyIcon',
};
case 'Overcast Clouds':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
return {
src: PartlyCloudyIcon,
alt: 'PartlyCloudyIcon',
};
case 'Patchy Fog':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Partly Sunny':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
return {
src: PartlyCloudyIcon,
alt: 'PartlyCloudyIcon',
};
case 'Rain':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Scattered Clouds':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={PartlyCloudyIcon}
alt='PartlyCloudyIcon'
/>
);
return {
src: PartlyCloudyIcon,
alt: 'PartlyCloudyIcon',
};
case 'Showers':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={RainIcon}
alt='RainIcon'
/>
);
return {
src: RainIcon,
alt: 'RainIcon',
};
case 'Sleet':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SleetIcon}
alt='SleetIcon'
/>
);
return {
src: SleetIcon,
alt: 'SleetIcon',
};
case 'Smoke':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={HazeIcon}
alt='HazeIcon'
/>
);
return {
src: HazeIcon,
alt: 'HazeIcon',
};
case 'Snow':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SnowIcon}
alt='SnowIcon'
/>
);
return {
src: SnowIcon,
alt: 'SnowIcon',
};
case 'Squall':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={WindyIcon}
alt='WindyIcon'
/>
);
return {
src: WindyIcon,
alt: 'WindyIcon',
};
case 'Thunderstorm':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={ThunderstormIcon}
alt='ThunderstormIcon'
/>
);
return {
src: ThunderstormIcon,
alt: 'ThunderstormIcon',
};
case 'Tornado':
return (
<Image
style={{ width: '75px', height: '75px' }}
src={WindyIcon}
alt='WindyIcon'
/>
);
return {
src: WindyIcon,
alt: 'WindyIcon',
};
default:
return (
<Image
style={{ width: '75px', height: '75px' }}
src={SunnyIcon}
alt='SunnyIcon'
/>
);
return {
src: SunnyIcon,
alt: 'SunnyIcon',
};
}
};
return <Icon />;
return (
<Image
style={{ width: '75px', height: '75px' }}
src={lookupSource(icon).src}
alt={lookupSource(icon).alt}
/>
);
}

View File

@@ -1,8 +1,7 @@
import { Center, Group, Modal, Stack, Text } from '@mantine/core';
import { modalClasses } from 'styles';
import { useCommonHooks, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { modalClasses } from 'styles';
import type { CurrentWeather as CW, ExtendedForecast, HourlyForecast } from 'types';
import CurrentWeather from './CurrentWeather';

View File

@@ -0,0 +1,27 @@
import { Stack, Text } from '@mantine/core';
function WeatherTooltip({ payload, active }: any) {
if (active) {
return (
<Stack
style={{
backgroundColor: 'var(--mantine-color-background-0)',
padding: '1rem',
borderRadius: '8px',
}}
>
{payload.map((item: any) => (
<Text
key={item.name}
size='md'
c={item.stroke}
>{`${item.name} - ${item.value}${item.unit}`}</Text>
))}
</Stack>
);
}
return null;
}
export default WeatherTooltip;

View File

@@ -1,4 +1,4 @@
export { HelpModal } from './HelpModal/HelpModal';
export { WeatherModal } from './WeatherModal/WeatherModal';
export { MainLayout } from './MainLayout';
export { Loading } from './Loading';
export { MainLayout } from './MainLayout';
export { WeatherModal } from './WeatherModal/WeatherModal';

View File

@@ -1,7 +1,7 @@
export {
useQueryAPI,
useWeatherAPI,
useMutationAPI,
useOSMAPI,
useQueryAPI,
useWeatherAPI,
} from './useAPI';
export { useCommonHooks } from './useCommonHooks';

View File

@@ -6,7 +6,10 @@ import BookmarkItem from './BookmarkItem';
function BookmarkGroups({
bookmarks,
category,
}: { bookmarks: Bookmark[]; category: string }) {
}: {
bookmarks: Bookmark[];
category: string;
}) {
return (
<Stack
style={{

View File

@@ -1,17 +1,9 @@
import {
ActionIcon,
Group,
type OptionsFilter,
Select,
Stack,
Text,
} from '@mantine/core';
import { type OptionsFilter, Select, Stack, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { HelpModal, WeatherModal } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { FiEdit, FiTrash } from 'react-icons/fi';
import { actionIconClasses, selectClasses } from 'styles';
import { selectClasses } from 'styles';
import type {
Application,
CurrentWeather,
@@ -19,6 +11,7 @@ import type {
Provider,
SearchHistoryItem,
} from 'types';
import SearchSelectItem from './SearchHistoryItem';
export default function Search({
current,
@@ -53,7 +46,6 @@ export default function Search({
const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure();
const [listIndex, setListIndex] = useState(0);
const {
theme,
isMobile,
urlPattern,
providerLabel,
@@ -272,112 +264,6 @@ export default function Search({
.slice(0, 10);
};
function RenderOption(item: any) {
const [showFillButton, setShowFillButton] = useState(false);
const m = new Date(item.option.dateTimeStamp);
const year = `${m.getUTCFullYear()}`.slice(-2);
const month = `0${m.getUTCMonth() + 1}`.slice(-2);
const day = `0${m.getUTCDate()}`.slice(-2);
const hours = `0${m.getUTCHours()}`.slice(-2);
const minutes = `0${m.getUTCMinutes()}`.slice(-2);
const dateString = isMobile
? `${month}/${day}/${year}`
: `${month}/${day}/${year} ${hours}:${minutes}`;
return (
<Group
w={'100%'}
display={'flex'}
justify={'space-between'}
gap={0}
onClick={() => {
handleSearchHistoryItem(item);
}}
onMouseEnter={() => {
setShowFillButton(true);
}}
onMouseLeave={() => {
setShowFillButton(false);
}}
>
<Group m={0} p={0} miw={'160px'} style={{ overflow: 'hidden' }}>
<Text
style={{
backgroundColor: `${theme.primaryColor}33`,
fontWeight: 600,
borderRadius: '15px',
padding: '.25rem',
}}
>
{item.option.provider}
</Text>
{showFillButton && (
<ActionIcon
className={actionIconClasses.selectOptionActions}
classNames={actionIconClasses}
onClick={(e) => {
e.stopPropagation();
setSearchValue(`${item.option.prefix} ${item.option.searchTerm}`);
}}
>
<FiEdit />
</ActionIcon>
)}
</Group>
<Group
m={0}
p={0}
gap={0}
justify='space-between'
w={'65%'}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
boxSizing: 'border-box',
position: 'relative',
}}
>
<Text
w={'75%'}
c={'var(--mantine-color-primary-0)'}
style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{item.option.searchTerm}
</Text>
<Text
w={'25%'}
style={{
color: `${theme.primaryColor}55`,
textAlign: 'right',
}}
>
{dateString}
</Text>
</Group>
<Group m={0} p={0} right={'1rem'} w={'24px'}>
{showFillButton && (
<ActionIcon
className={actionIconClasses.selectOptionActions}
classNames={actionIconClasses}
onClick={(e) => {
e.stopPropagation();
mutateAsync({
method: 'DELETE',
endpoint: searchEndpoint,
body: { id: item.option.id },
});
}}
>
<FiTrash />
</ActionIcon>
)}
</Group>
</Group>
);
}
useEffect(() => {
if (providerData && providerDataIsFetched) {
setProviders(providerData);
@@ -460,7 +346,13 @@ export default function Search({
autoFocus={!isMobile}
leftSection={providerLabel}
rightSection={<Stack />}
renderOption={RenderOption}
renderOption={(item: any) => (
<SearchSelectItem
item={item}
handleSearchHistoryItem={handleSearchHistoryItem}
searchEndpoint={searchEndpoint}
/>
)}
onBlur={() => {
setPlaceholderValue('');
setListIndex(0);

View File

@@ -0,0 +1,115 @@
import { ActionIcon, Group, Text } from '@mantine/core';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useState } from 'react';
import { FiEdit, FiTrash } from 'react-icons/fi';
import { actionIconClasses } from 'styles';
function SearchSelectItem({ item, handleSearchHistoryItem, searchEndpoint }: any) {
const { isMobile, theme, setSearchValue } = useCommonHooks();
const { mutateAsync } = useMutationAPI();
const [showFillButton, setShowFillButton] = useState(false);
const m = new Date(item.option.dateTimeStamp);
const year = `${m.getUTCFullYear()}`.slice(-2);
const month = `0${m.getUTCMonth() + 1}`.slice(-2);
const day = `0${m.getUTCDate()}`.slice(-2);
const hours = `0${m.getUTCHours()}`.slice(-2);
const minutes = `0${m.getUTCMinutes()}`.slice(-2);
const dateString = isMobile
? `${month}/${day}/${year}`
: `${month}/${day}/${year} ${hours}:${minutes}`;
return (
<Group
w={'100%'}
display={'flex'}
justify={'space-between'}
gap={0}
onClick={() => {
handleSearchHistoryItem(item);
}}
onMouseEnter={() => {
setShowFillButton(true);
}}
onMouseLeave={() => {
setShowFillButton(false);
}}
>
<Group m={0} p={0} miw={'160px'} style={{ overflow: 'hidden' }}>
<Text
style={{
backgroundColor: `${theme.primaryColor}33`,
fontWeight: 600,
borderRadius: '15px',
padding: '.25rem',
}}
>
{item.option.provider}
</Text>
{showFillButton && (
<ActionIcon
className={actionIconClasses.selectOptionActions}
classNames={actionIconClasses}
onClick={(e) => {
e.stopPropagation();
setSearchValue(`${item.option.prefix} ${item.option.searchTerm}`);
}}
>
<FiEdit />
</ActionIcon>
)}
</Group>
<Group
m={0}
p={0}
gap={0}
justify='space-between'
w={'65%'}
style={{
whiteSpace: 'nowrap',
overflow: 'hidden',
boxSizing: 'border-box',
position: 'relative',
}}
>
<Text
w={'75%'}
c={'var(--mantine-color-primary-0)'}
style={{
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{item.option.searchTerm}
</Text>
<Text
w={'25%'}
style={{
color: `${theme.primaryColor}55`,
textAlign: 'right',
}}
>
{dateString}
</Text>
</Group>
<Group m={0} p={0} right={'1rem'} w={'24px'}>
{showFillButton && (
<ActionIcon
className={actionIconClasses.selectOptionActions}
classNames={actionIconClasses}
onClick={(e) => {
e.stopPropagation();
mutateAsync({
method: 'DELETE',
endpoint: searchEndpoint,
body: { id: item.option.id },
});
}}
>
<FiTrash />
</ActionIcon>
)}
</Group>
</Group>
);
}
export default SearchSelectItem;

View File

@@ -23,7 +23,11 @@ function DebouncedApplication({
application,
setIsOpen,
allIcons,
}: { application: Application; setIsOpen: (isOpen: boolean) => void; allIcons: any }) {
}: {
application: Application;
setIsOpen: (isOpen: boolean) => void;
allIcons: any;
}) {
const { handleDelete, isMobile } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const { mutateAsync } = useMutationAPI();

View File

@@ -10,6 +10,7 @@ import AddItemModal from '../Shared/AddItemModal';
import NoItemsFound from '../Shared/NoItemsFound';
import SettingsHeader from '../Shared/SettingsHeader';
import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem';
const BookmarkSettings = () => {
const defaultGroup = {
id: null,

View File

@@ -10,7 +10,10 @@ import DeleteItemModal from '../Shared/DeleteItemModal';
function DebouncedBookmarkItem({
bookmarkItem,
setIsOpen,
}: { bookmarkItem: Bookmark; setIsOpen: (isOpen: boolean) => void }) {
}: {
bookmarkItem: Bookmark;
setIsOpen: (isOpen: boolean) => void;
}) {
const { handleDelete, isMobile } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const { mutateAsync } = useMutationAPI();

View File

@@ -33,7 +33,11 @@ function DebouncedGroupSettingsItem({
bookmarkGroup,
bookmarks,
searchTerm,
}: { bookmarkGroup: BookmarkGroup; bookmarks: Bookmark[]; searchTerm: string }) {
}: {
bookmarkGroup: BookmarkGroup;
bookmarks: Bookmark[];
searchTerm: string;
}) {
const { isMobile, handleDelete, reorder } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const [popoverOpened, setPopoverOpened] = useState(false);

View File

@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react';
import { cardClasses, textInputClasses } from 'styles';
import type { PersonalData } from 'types';
import SettingsHeader from '../Shared/SettingsHeader';
const PersonalizationSettings = () => {
const endpoint = 'api/personalData';
const {

View File

@@ -11,7 +11,7 @@ import {
} from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useState } from 'react';
import { FiHelpCircle, FiLoader, FiPlus } from 'react-icons/fi';
import { FiHelpCircle, FiLoader, FiPlus, FiX } from 'react-icons/fi';
import {
actionIconClasses,
buttonClasses,
@@ -137,13 +137,23 @@ function SettingsHeader({
zIndex: 100,
}}
rightSection={
isLoading && (
isLoading ? (
<ActionIcon
className={actionIconClasses.loadingSpinner}
classNames={actionIconClasses}
>
<FiLoader />
</ActionIcon>
) : (
searchValue && (
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
onClick={() => setSearchValue('')}
>
<FiX />
</ActionIcon>
)
)
}
/>

View File

@@ -0,0 +1,69 @@
import { Box, Stack, Text } from '@mantine/core';
import { useState } from 'react';
import { FiCheck } from 'react-icons/fi';
import { stackClasses } from 'styles';
import type { Theme } from 'types';
function ExampleApp({ selectedTheme }: { selectedTheme: Theme }) {
const [textDecoration, setTextDecoration] = useState<string>('none');
return (
<Box
component='a'
onMouseOver={() => setTextDecoration('underline')}
onMouseOut={() => setTextDecoration('none')}
className={stackClasses.appItemBox}
>
<Stack
style={{
height: '64px',
marginRight: '1rem',
marginLeft: '1rem',
paddingTop: '1rem',
}}
>
<FiCheck style={{ color: selectedTheme.primaryColor }} height={32} width={32} />
</Stack>
<Stack
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
flex: 1,
overflow: 'hidden',
gap: 0,
}}
>
<Text
style={{
fontWeight: 400,
fontSize: '.625rem',
fontStyle: 'normal',
color: selectedTheme.primaryColor,
fontFamily:
'-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0,
}}
>
{'Example App'}
</Text>
<Text
style={{
fontWeight: 400,
fontSize: '.625rem',
fontStyle: 'italic',
color: selectedTheme.accentColor,
fontFamily:
'-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0,
textDecoration: textDecoration,
}}
>
{'https://www.example.com'}
</Text>
</Stack>
</Box>
);
}
export default ExampleApp;

View File

@@ -1,12 +1,7 @@
import { Group, Text } from '@mantine/core';
import type { Theme } from 'types';
function ThemeMiniCard({
allThemes,
item,
}: {
allThemes: Theme[];
item: number;
}) {
function ThemeMiniCard({ allThemes, item }: { allThemes: Theme[]; item: number }) {
const theme = allThemes?.find((theme: Theme) => theme.id === item);
return (
@@ -19,9 +14,10 @@ function ThemeMiniCard({
w={'175px'}
h={'60px'}
style={{
background: `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), no-repeat center url(/backgrounds/${theme?.backgroundImage})`,
backgroundImage: `linear-gradient(${theme?.backgroundColor}33, ${theme?.backgroundColor}33), url(/backgrounds/${theme?.backgroundImage})`,
backgroundSize: 'cover',
backgroundColor: theme?.backgroundColor,
backgroundPosition: 'center',
border: `solid ${theme?.borderColor}`,
borderRadius: '8px',
}}

View File

@@ -1,84 +1,22 @@
import { Box, Group, Stack, Text } from '@mantine/core';
import { useState } from 'react';
import { FiCheck } from 'react-icons/fi';
import { stackClasses } from 'styles';
import { Group, Stack, Text } from '@mantine/core';
import type { Theme } from 'types';
import ThemeExampleApp from './ThemeExampleApp';
export default function ThemeModalContent({ selectedTheme }: { selectedTheme: Theme }) {
const ExampleApp = () => {
const [textDecoration, setTextDecoration] = useState<string>('none');
return (
<Box
component='a'
onMouseOver={() => setTextDecoration('underline')}
onMouseOut={() => setTextDecoration('none')}
className={stackClasses.appItemBox}
>
<Stack
style={{
height: '64px',
marginRight: '1rem',
marginLeft: '1rem',
paddingTop: '1rem',
}}
>
<FiCheck style={{ color: selectedTheme.primaryColor }} height={32} width={32} />
</Stack>
<Stack
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
flex: 1,
overflow: 'hidden',
gap: 0,
}}
>
<Text
style={{
fontWeight: 400,
fontSize: '.625rem',
fontStyle: 'normal',
color: selectedTheme.primaryColor,
fontFamily:
'-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0,
}}
>
{'Example App'}
</Text>
<Text
style={{
fontWeight: 400,
fontSize: '.625rem',
fontStyle: 'italic',
color: selectedTheme.accentColor,
fontFamily:
'-apple-system, Jost, BlinkMacSystemFont, Helvetica Neue, Roboto, sans-serif !important',
margin: 0,
textDecoration: textDecoration,
}}
>
{'https://www.example.com'}
</Text>
</Stack>
</Box>
);
};
return (
<Stack
style={{
width: '100%',
background:
backgroundImage:
selectedTheme.backgroundImage !== 'none'
? `linear-gradient(${selectedTheme.backgroundColor}33, ${selectedTheme.backgroundColor}33), no-repeat center url(/backgrounds/${selectedTheme.backgroundImage})`
? `linear-gradient(${selectedTheme?.backgroundColor}33, ${selectedTheme?.backgroundColor}33), url(/backgrounds/${selectedTheme?.backgroundImage})`
: undefined,
backgroundSize: selectedTheme.backgroundImage !== 'none' ? 'cover' : undefined,
backgroundColor:
selectedTheme.backgroundImage === 'none'
? selectedTheme.backgroundColor
: undefined,
backgroundPosition: 'center',
borderRadius: '10px',
border: '2px solid',
}}
@@ -124,9 +62,9 @@ export default function ThemeModalContent({ selectedTheme }: { selectedTheme: Th
alignContent: 'center',
}}
>
<ExampleApp />
<ExampleApp />
<ExampleApp />
<ThemeExampleApp selectedTheme={selectedTheme} />
<ThemeExampleApp selectedTheme={selectedTheme} />
<ThemeExampleApp selectedTheme={selectedTheme} />
</Stack>
<Text
pl={'1.5rem'}

View File

@@ -57,8 +57,10 @@ function ThemePlaylists() {
useEffect(() => {
if (themePlaylistData && isThemePlaylistDataFetched) {
setThemePlaylists(
themePlaylistData.filter((a: ThemePlaylist) =>
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()),
themePlaylistData.filter(
(a: ThemePlaylist) =>
a.name.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
a.name.includes('Time of Day'),
),
);
}
@@ -95,7 +97,7 @@ function ThemePlaylists() {
isLoading={isLoading}
setIsLoading={setIsLoading}
/>
{allThemeData ? (
{allThemeData && (
<>
<Group mt={'6rem'} justify='center' align='center' pos={'relative'}>
<Text
@@ -293,8 +295,6 @@ function ThemePlaylists() {
)}
</Group>
</>
) : (
<></>
)}
</Stack>
</Stack>

View File

@@ -55,7 +55,6 @@ function ThemesSettings() {
currentThemePlaylist,
setCurrentThemePlaylist,
} = useCommonHooks();
const handleOpen = (theme: Theme) => {
setSelectedTheme(theme);
open();
@@ -267,7 +266,7 @@ function ThemesSettings() {
navigate(`/${import.meta.env.VITE_BASE_URL}/settings/themePlaylists`);
} else {
setCurrentThemePlaylist('All');
setCurrentTheme('random');
setCurrentTheme(theme.name);
}
}}
>

View File

@@ -1,18 +1,18 @@
export type {
CurrentWeather,
OutlookWeather,
ExtendedForecast,
HourlyForecast,
SunState,
} from './weather';
export type { Application, AppCategory } from './application';
export type { AppCategory, Application } from './application';
export type { Bookmark } from './bookmark';
export type { BookmarkGroup } from './bookmarkGroup';
export type { PersonalData } from './personalData';
export type { Message } from './message';
export type { Provider } from './provider';
export type { Theme, ThemePlaylist } from './theme';
export type { WolClient } from './wolClient';
export type { SearchHistoryItem, SearchShortcut } from './search';
export type { User } from './user';
export type { Location } from './location';
export type { Message } from './message';
export type { PersonalData } from './personalData';
export type { Provider } from './provider';
export type { SearchHistoryItem, SearchShortcut } from './search';
export type { Theme, ThemePlaylist } from './theme';
export type { User } from './user';
export type {
CurrentWeather,
ExtendedForecast,
HourlyForecast,
OutlookWeather,
SunState,
} from './weather';
export type { WolClient } from './wolClient';