From c3495bd6744f780aabd43c9e8524778f64a2da4d Mon Sep 17 00:00:00 2001 From: Sean Edmunds Date: Mon, 21 Apr 2025 17:27:26 -0500 Subject: [PATCH] Typing and Density settings --- src/hooks/index.ts | 7 +- src/hooks/useCommonHooks.ts | 20 +- .../components/Applications/Applications.tsx | 6 +- .../components/Bookmarks/Bookmarks.tsx | 6 +- .../homepage/components/Search/Search.tsx | 16 +- .../homepage/components/SettingsButton.tsx | 332 +++++++++++------- .../components/Applications/Applications.tsx | 2 +- .../components/Shared/SettingsHeader.tsx | 6 +- .../components/Theming/ThemePlaylistCard.tsx | 4 +- .../components/Theming/ThemePlaylistModal.tsx | 262 ++++---------- .../components/Theming/ThemePlaylists.tsx | 8 +- .../components/Theming/ThemeSelect.tsx | 99 ++++++ .../settings/components/Theming/TimeOfDay.tsx | 49 +++ .../components/WoL/DebouncedWolClient.tsx | 4 +- src/styles/Button.module.css | 15 + src/styles/Slider.module.css | 19 + src/styles/Stack.module.css | 1 - src/styles/index.ts | 2 + src/types/index.ts | 2 + src/types/location.ts | 8 + src/types/user.ts | 6 + 21 files changed, 531 insertions(+), 343 deletions(-) create mode 100644 src/pages/settings/components/Theming/ThemeSelect.tsx create mode 100644 src/pages/settings/components/Theming/TimeOfDay.tsx create mode 100644 src/styles/Slider.module.css create mode 100644 src/types/location.ts create mode 100644 src/types/user.ts diff --git a/src/hooks/index.ts b/src/hooks/index.ts index f397aac..0f784af 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,2 +1,7 @@ -export { useQueryAPI, useWeatherAPI, useMutationAPI, useOSMAPI } from './useAPI'; +export { + useQueryAPI, + useWeatherAPI, + useMutationAPI, + useOSMAPI, +} from './useAPI'; export { useCommonHooks } from './useCommonHooks'; diff --git a/src/hooks/useCommonHooks.ts b/src/hooks/useCommonHooks.ts index 4ce53dd..aad101b 100644 --- a/src/hooks/useCommonHooks.ts +++ b/src/hooks/useCommonHooks.ts @@ -2,7 +2,7 @@ import { useLocalStorage } from '@mantine/hooks'; import { appTheme } from 'contexts'; import { useAtom } from 'jotai'; import { useState } from 'react'; -import type { Theme } from 'types'; +import type { Location, Theme, User } from 'types'; import { useMutationAPI } from './useAPI'; export function useCommonHooks() { @@ -21,6 +21,10 @@ export function useCommonHooks() { key: 'appSort', defaultValue: 'A>Z', }); + const [appDensity, setAppDensity] = useLocalStorage({ + key: 'appDensity', + defaultValue: 6, + }); const [bookmarkSort, setBookmarkSort] = useLocalStorage({ key: 'bookmarkSort', defaultValue: 'A>Z', @@ -29,6 +33,10 @@ export function useCommonHooks() { key: 'bookmarkFilter', defaultValue: ['All'], }); + const [bookmarkDensity, setBookmarkDensity] = useLocalStorage({ + key: 'bookmarkDensity', + defaultValue: 5, + }); const [currentTheme, setCurrentTheme] = useLocalStorage({ key: 'theme', defaultValue: 'blackboard', @@ -43,14 +51,16 @@ export function useCommonHooks() { const [providerLabel, setProviderLabel] = useState('Google Search'); const [providerURL, setProviderURL] = useState('https://www.google.com/search?q='); const [providerPrefix, setProviderPrefix] = useState('/g'); - const [currentUser, setCurrentUser] = useLocalStorage({ + const [currentUser, setCurrentUser] = useLocalStorage({ key: 'user', defaultValue: { firstName: 'First', lastName: 'Last', + email: 'triggerUser@default.com', + physicalAddress: '1510 Polk St, Houston, TX 77002', }, }); - const [currentLocation, setCurrentLocation] = useLocalStorage({ + const [currentLocation, setCurrentLocation] = useLocalStorage({ key: 'location', defaultValue: { office: 'HGX', @@ -133,10 +143,14 @@ export function useCommonHooks() { setAppFilter, appSort, setAppSort, + appDensity, + setAppDensity, bookmarkSort, setBookmarkSort, bookmarkFilter, setBookmarkFilter, + bookmarkDensity, + setBookmarkDensity, currentTheme, setCurrentTheme, currentThemePlaylist, diff --git a/src/pages/homepage/components/Applications/Applications.tsx b/src/pages/homepage/components/Applications/Applications.tsx index f2c3bf4..806b03c 100644 --- a/src/pages/homepage/components/Applications/Applications.tsx +++ b/src/pages/homepage/components/Applications/Applications.tsx @@ -9,7 +9,7 @@ function Applications({ contentType }: { contentType: [string] }) { const { data: apps, isFetching: isAppsFetching } = useQueryAPI({ endpoint: 'api/apps', }); - const { isMobile, appSort } = useCommonHooks(); + const { isMobile, appSort, appDensity } = useCommonHooks(); const [applicationData, setApplicationData] = useState([]); useEffect(() => { @@ -49,7 +49,9 @@ function Applications({ contentType }: { contentType: [string] }) { display: 'grid', gridColumnGap: '5px', gridRowGap: '5px', - gridTemplateColumns: isMobile ? '1fr 1fr 1fr' : '1fr 1fr 1fr 1fr 1fr 1fr', + gridTemplateColumns: isMobile + ? '1fr 1fr 1fr' + : `repeat(${appDensity}, 1fr)`, gridTemplateRows: '64px', }} > diff --git a/src/pages/homepage/components/Bookmarks/Bookmarks.tsx b/src/pages/homepage/components/Bookmarks/Bookmarks.tsx index 2d27b9f..93eb53e 100644 --- a/src/pages/homepage/components/Bookmarks/Bookmarks.tsx +++ b/src/pages/homepage/components/Bookmarks/Bookmarks.tsx @@ -12,7 +12,7 @@ function Links({ contentType }: { contentType: [string] }) { const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({ endpoint: 'api/categories', }); - const { isMobile, bookmarkSort } = useCommonHooks(); + const { isMobile, bookmarkSort, bookmarkDensity } = useCommonHooks(); const [bookmarkData, setBookmarkData] = useState([]); const [groups, setGroups] = useState([]); @@ -62,7 +62,9 @@ function Links({ contentType }: { contentType: [string] }) { flexWrap: 'nowrap', gridColumnGap: '20px', gridRowGap: '0px', - gridTemplateColumns: isMobile ? '1fr 1fr' : '1fr 1fr 1fr 1fr 1fr', + gridTemplateColumns: isMobile + ? '1fr 1fr' + : `repeat(${bookmarkDensity}, 1fr)`, gridTemplateRows: 'auto', }} > diff --git a/src/pages/homepage/components/Search/Search.tsx b/src/pages/homepage/components/Search/Search.tsx index 0582697..6dcb44a 100644 --- a/src/pages/homepage/components/Search/Search.tsx +++ b/src/pages/homepage/components/Search/Search.tsx @@ -12,7 +12,13 @@ import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; import { FiEdit, FiTrash } from 'react-icons/fi'; import { actionIconClasses, selectClasses } from 'styles'; -import type { Application, CurrentWeather, ExtendedForecast, Provider } from 'types'; +import type { + Application, + CurrentWeather, + ExtendedForecast, + Provider, + SearchHistoryItem, +} from 'types'; export default function Search({ current, @@ -38,8 +44,8 @@ export default function Search({ const { data: history, isFetched: historyIsFetched } = useQueryAPI({ endpoint: 'api/searchHistory', }); - const [searchHistory, setSearchHistory] = useState([]); - const [filteredHistory, setFilteredHistory] = useState([]); + const [searchHistory, setSearchHistory] = useState([]); + const [filteredHistory, setFilteredHistory] = useState([]); const { mutateAsync } = useMutationAPI(); const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] = useDisclosure(false); @@ -169,7 +175,7 @@ export default function Search({ ? `${searchHistory[listIndex].prefix} ${searchHistory[listIndex].searchTerm}` : '', ); - setValue(searchHistory.length > 0 ? searchHistory[listIndex].id : 0); + setValue(searchHistory[listIndex].id ?? 0); } } else if (searchValue !== '') { if (item.code === 'ArrowUp') { @@ -193,7 +199,7 @@ export default function Search({ setSearchValue( `${filteredHistory[listIndex].prefix} ${filteredHistory[listIndex].searchTerm}`, ); - setValue(filteredHistory[listIndex].id); + setValue(filteredHistory[listIndex].id ?? 0); } } } diff --git a/src/pages/homepage/components/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton.tsx index 6fca31a..a24f0a5 100644 --- a/src/pages/homepage/components/SettingsButton.tsx +++ b/src/pages/homepage/components/SettingsButton.tsx @@ -6,13 +6,15 @@ import { MultiSelect, Overlay, Select, + Slider, Stack, Text, Tooltip, } from '@mantine/core'; +import { useQueryClient } from '@tanstack/react-query'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FaDice } from 'react-icons/fa'; +import { FaDice, FaSync } from 'react-icons/fa'; import { FiMenu, FiPower, FiSettings, FiShuffle } from 'react-icons/fi'; import { useNavigate } from 'react-router-dom'; import { toast } from 'react-toastify'; @@ -22,6 +24,7 @@ import { menuClasses, multiSelectClasses, selectClasses, + sliderClasses, stackClasses, } from 'styles'; import type { AppCategory, Theme, ThemePlaylist, WolClient } from 'types'; @@ -55,10 +58,14 @@ function SettingsButton() { setAppFilter, appSort, setAppSort, + appDensity, + setAppDensity, bookmarkSort, setBookmarkSort, bookmarkFilter, setBookmarkFilter, + bookmarkDensity, + setBookmarkDensity, isMobile, currentThemePlaylist, setCurrentThemePlaylist, @@ -71,6 +78,23 @@ function SettingsButton() { }); }; + const marks = [ + { value: 1, label: '1' }, + { value: 2, label: '2' }, + { value: 3, label: '3' }, + { value: 4, label: '4' }, + { value: 5, label: '5' }, + { value: 6, label: '6' }, + { value: 7, label: '7' }, + { value: 8, label: '8' }, + ]; + + const queryClient = useQueryClient(); + + const handleRefresh = () => { + queryClient.refetchQueries(); + }; + useEffect(() => { if (data) { const list: [any] = data.map((category: AppCategory) => { @@ -97,7 +121,7 @@ function SettingsButton() { {overlayOpened && } - - - navigate(`/${import.meta.env.VITE_BASE_URL}/settings`)} - style={{ - cursor: 'pointer', - position: 'absolute', - top: '.5rem', - right: '.5rem', - }} - /> - - + + Wake on LAN + + diff --git a/src/pages/settings/components/Applications/Applications.tsx b/src/pages/settings/components/Applications/Applications.tsx index 5d66747..c5fea80 100644 --- a/src/pages/settings/components/Applications/Applications.tsx +++ b/src/pages/settings/components/Applications/Applications.tsx @@ -263,7 +263,7 @@ const ApplicationSettings = () => { 'filter', ], description: - 'Applications contain a name, base url, url, icon and filter. Currently the icon must be the mdi icon name. URLs can contain either http(s):// or not. Base URLs should start with /, otherwise they will cause errors.', + 'Applications contain a name, base url, url, icon and filter. URLs can contain either http(s):// or not. Base URLs should start with /, otherwise they will cause errors.', }} searchValue={searchValue} setSearchValue={setSearchValue} diff --git a/src/pages/settings/components/Shared/SettingsHeader.tsx b/src/pages/settings/components/Shared/SettingsHeader.tsx index fa2edb0..bfb2a0c 100644 --- a/src/pages/settings/components/Shared/SettingsHeader.tsx +++ b/src/pages/settings/components/Shared/SettingsHeader.tsx @@ -33,9 +33,9 @@ function SettingsHeader({ openModal: () => void; title: string; tooltip: any; - searchValue: string; + searchValue: string | undefined; setSearchValue: (searchValue: string) => void; - searchPlaceholder: string; + searchPlaceholder: string | undefined; }) { const { isMobile, theme, modalStyle } = useCommonHooks(); return ( @@ -123,7 +123,7 @@ function SettingsHeader({ className={textInputClasses.settingsItem} classNames={textInputClasses} autoFocus={!isMobile} - placeholder={searchPlaceholder || 'Search...'} + placeholder={searchPlaceholder ?? 'Search...'} value={searchValue} onInput={(event: any) => { setSearchValue(event.target.value); diff --git a/src/pages/settings/components/Theming/ThemePlaylistCard.tsx b/src/pages/settings/components/Theming/ThemePlaylistCard.tsx index 1162462..3777e5b 100644 --- a/src/pages/settings/components/Theming/ThemePlaylistCard.tsx +++ b/src/pages/settings/components/Theming/ThemePlaylistCard.tsx @@ -221,7 +221,7 @@ function ThemePlaylistCard({ )} + + + )} - - - - - diff --git a/src/pages/settings/components/Theming/ThemePlaylists.tsx b/src/pages/settings/components/Theming/ThemePlaylists.tsx index 8713680..3375a0f 100644 --- a/src/pages/settings/components/Theming/ThemePlaylists.tsx +++ b/src/pages/settings/components/Theming/ThemePlaylists.tsx @@ -20,7 +20,7 @@ function ThemePlaylists() { }); const { mutateAsync } = useMutationAPI(); const [opened, { open, close }] = useDisclosure(false); - const [selectedPlaylist, setSelectedPlaylist] = useState({}); + const [selectedPlaylist, setSelectedPlaylist] = useState(); const { currentThemePlaylist, theme, isMobile } = useCommonHooks(); const [searchValue, setSearchValue] = useState(''); const [debouncedSearch] = useDebouncedValue(searchValue, 500); @@ -31,15 +31,15 @@ function ThemePlaylists() { }; const handleClose = () => { - setSelectedPlaylist({}); + setSelectedPlaylist(undefined); close(); }; const handleSavePlaylist = () => { mutateAsync({ - method: selectedPlaylist.id === undefined ? 'POST' : 'PATCH', + method: selectedPlaylist?.id === undefined ? 'POST' : 'PATCH', endpoint: themePlaylistsEndpoint, - body: selectedPlaylist, + body: selectedPlaylist ?? {}, }); close(); }; diff --git a/src/pages/settings/components/Theming/ThemeSelect.tsx b/src/pages/settings/components/Theming/ThemeSelect.tsx new file mode 100644 index 0000000..37b76dd --- /dev/null +++ b/src/pages/settings/components/Theming/ThemeSelect.tsx @@ -0,0 +1,99 @@ +import { Group, Select, Text } from '@mantine/core'; +import type { Theme } from 'types'; + +function ThemeSelect({ + allThemes, + createPlaylist, + themeList, + selectedPlaylist, + index, + timeIndex, +}: { + allThemes: Theme[]; + createPlaylist: any; + themeList: any; + selectedPlaylist: any; + index: string; + timeIndex: string; +}) { + const theme = allThemes.find((theme: Theme) => `${theme.id}` === themeList[index]); + + return ( + + {timeIndex} +