diff --git a/src/components/Columns/SearchHistoryColumns.tsx b/src/components/Columns/SearchHistoryColumns.tsx index 359d617..08f0332 100644 --- a/src/components/Columns/SearchHistoryColumns.tsx +++ b/src/components/Columns/SearchHistoryColumns.tsx @@ -5,7 +5,7 @@ import type { SearchHistoryItem } from 'types'; const columnHelper = createColumnHelper(); export const SearchHistoryColumns = ( - setSelectedItem: React.Dispatch>, + setSelectedItem: React.Dispatch>, deleteItemOpen: () => void, filtersEnabled: boolean, setFiltersEnabled: React.Dispatch>, diff --git a/src/components/HelpModal/HelpModal.tsx b/src/components/HelpModal/HelpModal.tsx index 02c6150..a0faa30 100644 --- a/src/components/HelpModal/HelpModal.tsx +++ b/src/components/HelpModal/HelpModal.tsx @@ -6,14 +6,14 @@ import HelpSection from './HelpSection'; export function HelpModal({ handleResetSearch, - providerModalOpened, + modalOpened, closeProviderModal, applicationData, providerData, clientData, }: { handleResetSearch: () => void; - providerModalOpened: boolean; + modalOpened: boolean; closeProviderModal: () => void; applicationData: Application[]; providerData: Provider[]; @@ -25,7 +25,7 @@ export function HelpModal({ size={'60%'} className={modalClasses.helpModal} classNames={modalClasses} - opened={providerModalOpened} + opened={modalOpened} fullScreen={isMobile} centered onClose={() => { diff --git a/src/components/index.ts b/src/components/index.ts index 2e2bab9..47b7d68 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,5 +1,6 @@ export { AddItemModal } from './AddItemModal'; export { AppItem } from './Applications/AppItem'; +export { SearchHistoryColumns } from './Columns/SearchHistoryColumns'; export { DataTable } from './DataTable/DataTable'; export { DebouncedInput } from './DebouncedInput'; export { DeleteItemModal } from './DeleteItemModal'; diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx index 1361006..4f873e4 100644 --- a/src/pages/homepage/Homepage.tsx +++ b/src/pages/homepage/Homepage.tsx @@ -10,12 +10,14 @@ import type { Message, PersonalData, } from 'types'; -import Applications from './components/Applications/Applications'; -import Bookmarks from './components/Bookmarks/Bookmarks'; -import Header from './components/Header/Header'; -import LargeHeader from './components/Header/LargeHeader'; -import Search from './components/Search/Search'; -import SettingsButton from './components/SettingsButton/SettingsButton'; +import { + Applications, + Bookmarks, + Header, + LargeHeader, + Search, + SettingsButton, +} from './components'; export function Homepage() { const { diff --git a/src/pages/homepage/components/Applications/Applications.tsx b/src/pages/homepage/components/Applications/Applications.tsx index 5776f65..c572193 100644 --- a/src/pages/homepage/components/Applications/Applications.tsx +++ b/src/pages/homepage/components/Applications/Applications.tsx @@ -4,7 +4,7 @@ import { useCommonHooks, useQueryAPI } from 'hooks'; import { textClasses } from 'styles'; import type { Application } from 'types'; -function Applications({ contentType }: { contentType: string[] }) { +export function Applications({ contentType }: { contentType: string[] }) { const { data: applicationData } = useQueryAPI({ endpoint: 'api/enabledApplications', }); @@ -76,5 +76,3 @@ function Applications({ contentType }: { contentType: string[] }) { ); } - -export default Applications; diff --git a/src/pages/homepage/components/Bookmarks/Bookmarks.tsx b/src/pages/homepage/components/Bookmarks/Bookmarks.tsx index cdca2d1..7c90d68 100644 --- a/src/pages/homepage/components/Bookmarks/Bookmarks.tsx +++ b/src/pages/homepage/components/Bookmarks/Bookmarks.tsx @@ -4,7 +4,7 @@ import { textClasses } from 'styles'; import type { Bookmark, BookmarkGroup } from 'types'; import BookmarkGroups from './BookmarkGroups'; -function Bookmarks({ contentType }: { contentType: string[] }) { +export function Bookmarks({ contentType }: { contentType: string[] }) { const { data: bookmarkData, isFetched: bookmarksIsFetched } = useQueryAPI({ endpoint: 'api/bookmarks', }); @@ -95,5 +95,3 @@ function Bookmarks({ contentType }: { contentType: string[] }) { ); } - -export default Bookmarks; diff --git a/src/pages/homepage/components/Header/Header.tsx b/src/pages/homepage/components/Header/Header.tsx index 9e2b6f3..c2b3bcd 100644 --- a/src/pages/homepage/components/Header/Header.tsx +++ b/src/pages/homepage/components/Header/Header.tsx @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'; import { textClasses } from 'styles'; import type { CurrentWeather } from 'types'; -function Header({ +export function Header({ greet, date, current, @@ -140,5 +140,3 @@ function Header({ ); } - -export default Header; diff --git a/src/pages/homepage/components/Header/LargeHeader.tsx b/src/pages/homepage/components/Header/LargeHeader.tsx index 1c2a760..55157bb 100644 --- a/src/pages/homepage/components/Header/LargeHeader.tsx +++ b/src/pages/homepage/components/Header/LargeHeader.tsx @@ -3,7 +3,13 @@ import { useCommonHooks } from 'hooks'; import { useEffect, useState } from 'react'; import { textClasses } from 'styles'; -function LargeHeader({ greet, date }: { greet: string | undefined; date: string }) { +export function LargeHeader({ + greet, + date, +}: { + greet: string | undefined; + date: string; +}) { const [time, setTime] = useState(new Date()); const { isMobile, @@ -79,5 +85,3 @@ function LargeHeader({ greet, date }: { greet: string | undefined; date: string ); } - -export default LargeHeader; diff --git a/src/pages/homepage/components/Search/Search.tsx b/src/pages/homepage/components/Search/Search.tsx index a168345..39cfb69 100644 --- a/src/pages/homepage/components/Search/Search.tsx +++ b/src/pages/homepage/components/Search/Search.tsx @@ -30,7 +30,7 @@ import RecommendedBookmarks from './components/RecommendedBookmarks'; import RecommendedClients from './components/RecommendedClients'; import RenderItem from './components/RenderItem'; -export default function Search({ +export function Search({ current, extendedForecast, weatherModalOpened, @@ -634,21 +634,25 @@ export default function Search({ )} - - + {providerModalOpened && ( + + )} + {weatherModalOpened && ( + + )} ); } diff --git a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx index c8224b4..9192045 100644 --- a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx +++ b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx @@ -9,7 +9,7 @@ import SettingsCard from './SettingsCard'; import ThemingCard from './ThemingCard'; import WakeOnLan from './WakeOnLanCard'; -function SettingsButton({ openProviderModal }: { openProviderModal: () => void }) { +export function SettingsButton({ openProviderModal }: { openProviderModal: () => void }) { const { data: appCategoryData } = useQueryAPI({ endpoint: 'api/appCategories', }); @@ -171,5 +171,3 @@ function SettingsButton({ openProviderModal }: { openProviderModal: () => void } ); } - -export default SettingsButton; diff --git a/src/pages/homepage/components/index.ts b/src/pages/homepage/components/index.ts new file mode 100644 index 0000000..0f3be51 --- /dev/null +++ b/src/pages/homepage/components/index.ts @@ -0,0 +1,6 @@ +export { Applications } from './Applications/Applications'; +export { Bookmarks } from './Bookmarks/Bookmarks'; +export { Header } from './Header/Header'; +export { LargeHeader } from './Header/LargeHeader'; +export { Search } from './Search/Search'; +export { SettingsButton } from './SettingsButton/SettingsButton'; diff --git a/src/pages/settings/Settings.tsx b/src/pages/settings/Settings.tsx index dc0cc59..8d2c319 100644 --- a/src/pages/settings/Settings.tsx +++ b/src/pages/settings/Settings.tsx @@ -13,16 +13,18 @@ import { import { useNavigate, useParams } from 'react-router-dom'; import { actionIconClasses, scrollAreaClasses, tabClasses, textClasses } from 'styles'; import type { PersonalData } from 'types'; -import Applications from './components/Applications/Applications'; -import Bookmarks from './components/Bookmarks/Bookmarks'; -import GuacamoleClients from './components/GuacamoleClients/GuacamoleClients'; -import Messages from './components/Messages/Messages'; -import Personalization from './components/Personalization/Personalization'; -import SearchHistory from './components/Search/SearchHistory'; -import SearchProviders from './components/Search/SearchProviders'; -import { ThemePlaylists } from './components/Theming/ThemePlaylists'; -import { Themes } from './components/Theming/Themes'; -import WolClients from './components/WoL/WolClients'; +import { + Applications, + Bookmarks, + GuacamoleClients, + Messages, + Personalization, + SearchHistory, + SearchProviders, + ThemePlaylists, + Themes, + WolClients, +} from './components'; export function Settings() { const endpoint = 'api/personalData'; diff --git a/src/pages/settings/components/Applications/Applications.tsx b/src/pages/settings/components/Applications/Applications.tsx index f2f2c7a..54382f8 100644 --- a/src/pages/settings/components/Applications/Applications.tsx +++ b/src/pages/settings/components/Applications/Applications.tsx @@ -26,7 +26,7 @@ import { buttonClasses, popoverClasses, selectClasses, textInputClasses } from ' import type { Application } from 'types'; import DebouncedApplication from './DebouncedApplication'; -const ApplicationSettings = () => { +export function Applications() { const allIcons: any = { ...fa6, ...vsc, ...si }; const defaultApplication: Application = { id: null, @@ -141,165 +141,167 @@ const ApplicationSettings = () => { isLoading={isLoading} setIsLoading={setIsLoading} /> - { - mutateAsync({ - method: 'POST', - endpoint: appsEndpoint, - body: { - ...values, - index: applications.length, - }, - }); - handleClose(); - })} - > - - - - - - - - - - { + return { value: icon, label: icon }; + })} + comboboxProps={{ + zIndex: 3000, + }} + styles={ + theme.textShadowsEnabled + ? { + label: { boxShadow: '2px 2px var(--mantine-color-border-0)' }, + } + : undefined + } + renderOption={(item: ComboboxLikeRenderOptionInput) => { + const Icon = allIcons[item.option.value]; + return ( + + {item.option.value} + + + ); + }} + searchable + maxDropdownHeight={175} + onDropdownOpen={() => setIsOpen(true)} + onDropdownClose={() => setIsOpen(false)} + withAsterisk + /> + + + + + + + + + } + /> + )} { ); -}; -export default ApplicationSettings; +} diff --git a/src/pages/settings/components/Applications/DebouncedApplication.tsx b/src/pages/settings/components/Applications/DebouncedApplication.tsx index 219c3b0..589983d 100644 --- a/src/pages/settings/components/Applications/DebouncedApplication.tsx +++ b/src/pages/settings/components/Applications/DebouncedApplication.tsx @@ -30,7 +30,7 @@ function DebouncedApplication({ const { mutateAsync } = useMutationAPI(); const [app, setApp] = useState(application); - const [update, setUpdate] = useState(); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const handleClose = () => { setIsOpen(false); @@ -53,22 +53,24 @@ function DebouncedApplication({ className={cardClasses.settingsItem} classNames={cardClasses} > - handleDelete(app, 'api/applications')} - title='Delete Application' - body={ - - Are you sure you want to delete this application? - {`"${app.name}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(app, 'api/applications')} + title='Delete Application' + body={ + + Are you sure you want to delete this application? + {`"${app.name}"`} + This action cannot be undone! + + } + /> + )} { setUpdate({ ...app, - icon: value, + icon: value as Application['icon'], }); }} renderOption={(item: ComboboxLikeRenderOptionInput) => { diff --git a/src/pages/settings/components/Bookmarks/Bookmarks.tsx b/src/pages/settings/components/Bookmarks/Bookmarks.tsx index cb45d7f..139b487 100644 --- a/src/pages/settings/components/Bookmarks/Bookmarks.tsx +++ b/src/pages/settings/components/Bookmarks/Bookmarks.tsx @@ -18,7 +18,7 @@ import { buttonClasses, textInputClasses } from 'styles'; import type { Bookmark, BookmarkGroup } from 'types'; import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem'; -const BookmarkSettings = () => { +export function Bookmarks() { const defaultGroup = { id: null, categoryName: '', @@ -68,7 +68,7 @@ const BookmarkSettings = () => { ), ), ]; - const foundGroups: BookmarkGroup[] = groupData.filter((a: BookmarkGroup) => + const foundGroups: BookmarkGroup[] = groupData?.filter((a: BookmarkGroup) => a.categoryName.toLowerCase().includes((debouncedGroupSearch as string).toLowerCase()), ); const handleClose = () => { @@ -121,79 +121,81 @@ const BookmarkSettings = () => { return ( - { - mutateAsync({ - method: 'POST', - endpoint: categoriesEndpoint, - body: { - ...values, - categoryIndex: groups.length, - }, - }); - handleClose(); - })} - > - - - - - - - - - - - - } - /> + {modalOpened && ( + { + mutateAsync({ + method: 'POST', + endpoint: categoriesEndpoint, + body: { + ...values, + categoryIndex: groups.length, + }, + }); + handleClose(); + })} + > + + + + + + + + + + + + } + /> + )} 0 ? setDragging : undefined} openModal={openModal} @@ -337,5 +339,4 @@ const BookmarkSettings = () => { )} ); -}; -export default BookmarkSettings; +} diff --git a/src/pages/settings/components/Bookmarks/DebouncedBookmarkItem.tsx b/src/pages/settings/components/Bookmarks/DebouncedBookmarkItem.tsx index 51fc736..9ff98cd 100644 --- a/src/pages/settings/components/Bookmarks/DebouncedBookmarkItem.tsx +++ b/src/pages/settings/components/Bookmarks/DebouncedBookmarkItem.tsx @@ -25,20 +25,20 @@ function DebouncedBookmarkItem({ (a: BookmarkGroup, b: BookmarkGroup) => a.categoryName.toLocaleLowerCase() > b.categoryName.toLocaleLowerCase(), ) || []; - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const handleClose = () => { setIsOpen(false); closeModal(); }; useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: 'api/bookmarks', body: debounced }); } }, [debounced]); useEffect(() => { - if (update !== bookmark && Object.keys(update).length) { + if (update && update !== bookmark && Object.keys(update).length) { setBookmark(update); } }, [update]); @@ -49,22 +49,24 @@ function DebouncedBookmarkItem({ className={cardClasses.settingsItem} classNames={cardClasses} > - handleDelete(bookmark, 'api/bookmarks')} - body={ - - Are you sure you want to delete this bookmark? - {`"${bookmark.name}"`} - This action cannot be undone! - - } - title='Delete Bookmark' - /> + {modalOpened && ( + handleDelete(bookmark, 'api/bookmarks')} + body={ + + Are you sure you want to delete this bookmark? + {`"${bookmark.name}"`} + This action cannot be undone! + + } + title='Delete Bookmark' + /> + )} { setUpdate({ ...bookmark, - category: value, + category: value as BookmarkGroup['categoryName'], }); }} searchable diff --git a/src/pages/settings/components/Bookmarks/DebouncedGroupSettingsItem.tsx b/src/pages/settings/components/Bookmarks/DebouncedGroupSettingsItem.tsx index 1760b35..1392b24 100644 --- a/src/pages/settings/components/Bookmarks/DebouncedGroupSettingsItem.tsx +++ b/src/pages/settings/components/Bookmarks/DebouncedGroupSettingsItem.tsx @@ -48,7 +48,7 @@ function DebouncedGroupSettingsItem({ const [popoverOpened, setPopoverOpened] = useState(false); const { mutateAsync } = useMutationAPI(); const [group, setGroup] = useState(bookmarkGroup); - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const [newBookmark, setNewBookmark] = useState({ id: undefined, @@ -89,18 +89,18 @@ function DebouncedGroupSettingsItem({ } useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: 'api/bookmarkCategories', body: debounced, }); - setUpdate({}); + setUpdate(undefined); } }, [debounced]); useEffect(() => { - if (update !== group && Object.keys(update).length) { + if (update && update !== group && Object.keys(update).length) { setGroup(update); } }, [update]); @@ -113,28 +113,30 @@ function DebouncedGroupSettingsItem({ return ( - { - bookmarks.length > 0 && handleDelete(bookmarks, 'api/bookmarks'); - handleDelete(group, 'api/bookmarkCategories'); - }} - title='Delete message' - body={ - - Are you sure you want to delete this bookmark group? - {`"${group.categoryName}"`} - - This will also delete all bookmarks in this group! - - This action cannot be undone! - - } - /> + {modalOpened && ( + { + bookmarks.length > 0 && handleDelete(bookmarks, 'api/bookmarks'); + handleDelete(group, 'api/bookmarkCategories'); + }} + title='Delete message' + body={ + + Are you sure you want to delete this bookmark group? + {`"${group.categoryName}"`} + + This will also delete all bookmarks in this group! + + This action cannot be undone! + + } + /> + )} (guacamoleClient); - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const handleClose = () => { setIsOpen(false); @@ -46,13 +46,13 @@ function DebouncedGuacamoleClient({ }; useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: 'api/guacamoleClients', body: debounced }); } }, [debounced]); useEffect(() => { - if (update !== client && Object.keys(update).length) { + if (update && update !== client && Object.keys(update).length) { setClient(update); } }, [update]); @@ -62,22 +62,24 @@ function DebouncedGuacamoleClient({ className={cardClasses.settingsItem} classNames={cardClasses} > - handleDelete(client, 'api/guacamoleClients')} - title={'Delete Guacamole Client'} - body={ - - Are you sure you want to delete this WoL Client? - {`"${client.clientName}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(client, 'api/guacamoleClients')} + title={'Delete Guacamole Client'} + body={ + + Are you sure you want to delete this WoL Client? + {`"${client.clientName}"`} + This action cannot be undone! + + } + /> + )} { setUpdate({ ...guacamoleClient, - type: value, + type: value as GuacamoleClient['type'], }); }} searchable diff --git a/src/pages/settings/components/GuacamoleClients/GuacamoleClientSettingsItem.tsx b/src/pages/settings/components/GuacamoleClients/GuacamoleClientSettingsItem.tsx index 6151d37..0e0d841 100644 --- a/src/pages/settings/components/GuacamoleClients/GuacamoleClientSettingsItem.tsx +++ b/src/pages/settings/components/GuacamoleClients/GuacamoleClientSettingsItem.tsx @@ -1,6 +1,5 @@ import { Card, Stack, Text } from '@mantine/core'; -import { FaDesktop, FaTerminal } from 'react-icons/fa'; -import { FaComputer } from 'react-icons/fa6'; +import { FaComputer, FaDesktop, FaTerminal } from 'react-icons/fa6'; import { cardClasses } from 'styles'; import type { GuacamoleClient } from 'types'; diff --git a/src/pages/settings/components/GuacamoleClients/GuacamoleClients.tsx b/src/pages/settings/components/GuacamoleClients/GuacamoleClients.tsx index 0b6f77b..c8e3e45 100644 --- a/src/pages/settings/components/GuacamoleClients/GuacamoleClients.tsx +++ b/src/pages/settings/components/GuacamoleClients/GuacamoleClients.tsx @@ -16,14 +16,13 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { AddItemModal, NoItemsFound, SettingsHeader } from 'components'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FaTerminal } from 'react-icons/fa'; -import { FaComputer, FaDesktop } from 'react-icons/fa6'; +import { FaComputer, FaDesktop, FaTerminal } from 'react-icons/fa6'; import { buttonClasses, popoverClasses, selectClasses, textInputClasses } from 'styles'; import type { GuacamoleClient } from 'types'; import DebouncedGuacamoleClient from './DebouncedGuacamoleClient'; import GuacamoleClientSettingsItem from './GuacamoleClientSettingsItem'; -const GuacamoleClients = () => { +export function GuacamoleClients() { const defaultGuacamoleClient: GuacamoleClient = { id: undefined, clientName: '', @@ -73,127 +72,129 @@ const GuacamoleClients = () => { return ( - { - mutateAsync({ - method: 'POST', - endpoint: endpoint, - body: { ...values, enabled: 1 }, - }); - handleClose(); - })} - > - - - - - - - { + return { + label: type, + value: type, + }; + })} + comboboxProps={{ + zIndex: 3000, + }} + styles={ + theme.textShadowsEnabled + ? { + label: { boxShadow: '2px 2px var(--mantine-color-border-0)' }, + } + : undefined + } + renderOption={(item: ComboboxLikeRenderOptionInput) => { + return ( + + {item.option.value} + {item.option.value === 'SSH' && } + {item.option.value === 'RDP' && } + {item.option.value === 'VNC' && } + + ); + }} + searchable + maxDropdownHeight={175} + onDropdownOpen={() => setIsOpen(true)} + onDropdownClose={() => setIsOpen(false)} + withAsterisk + /> + + + + + + + + } + /> + )} { ); -}; -export default GuacamoleClients; +} diff --git a/src/pages/settings/components/Messages/DebouncedMessage.tsx b/src/pages/settings/components/Messages/DebouncedMessage.tsx index d4361b4..46d6286 100644 --- a/src/pages/settings/components/Messages/DebouncedMessage.tsx +++ b/src/pages/settings/components/Messages/DebouncedMessage.tsx @@ -23,22 +23,24 @@ function DebouncedMessage({ w={'100%'} style={{ alignItems: 'center' }} > - handleDelete(message, 'api/messages')} - title='Delete message' - body={ - - Are you sure you want to delete this message? - {`"${message.message}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(message, 'api/messages')} + title='Delete message' + body={ + + Are you sure you want to delete this message? + {`"${message.message}"`} + This action cannot be undone! + + } + /> + )} { +export function Messages() { const endpoint = 'api/messages'; const { data: messageData, isFetching: messageDataIsFetching } = useQueryAPI({ endpoint: endpoint, @@ -83,60 +83,62 @@ const MessageSettings = () => { isLoading={isLoading} setIsLoading={setIsLoading} /> - { - mutateAsync({ - method: 'POST', - endpoint: endpoint, - body: { message: values.message }, - }); - handleClose(); - })} - > - - - - - - - - - } - /> + {modalOpened && ( + { + mutateAsync({ + method: 'POST', + endpoint: endpoint, + body: { message: values.message }, + }); + handleClose(); + })} + > + + + + + + + + + } + /> + )} { ); -}; -export default MessageSettings; +} diff --git a/src/pages/settings/components/Personalization/Personalization.tsx b/src/pages/settings/components/Personalization/Personalization.tsx index 830b9a5..860751f 100644 --- a/src/pages/settings/components/Personalization/Personalization.tsx +++ b/src/pages/settings/components/Personalization/Personalization.tsx @@ -16,7 +16,7 @@ import FontPicker from 'react-fontpicker-ts'; import { cardClasses, switchClasses, textClasses, textInputClasses } from 'styles'; import type { PersonalData } from 'types'; -const PersonalizationSettings = () => { +export function Personalization() { const endpoint = 'api/personalData'; const { data: personalData, isFetching: personalDataIsFetching } = useQueryAPI({ endpoint: endpoint, @@ -27,14 +27,14 @@ const PersonalizationSettings = () => { personal?.enableGuacamole === 1, ); const [defaultFont, setDefaultFont] = useState(personal?.defaultFont); - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const { mutateAsync } = useMutationAPI(); const { isMobile, theme } = useCommonHooks(); useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced }); - setUpdate({}); + setUpdate(undefined); } }, [debounced]); @@ -322,5 +322,4 @@ const PersonalizationSettings = () => { ); -}; -export default PersonalizationSettings; +} diff --git a/src/pages/settings/components/Search/DebouncedSearchProvider.tsx b/src/pages/settings/components/Search/DebouncedSearchProvider.tsx index 142f4f1..b17c8e8 100644 --- a/src/pages/settings/components/Search/DebouncedSearchProvider.tsx +++ b/src/pages/settings/components/Search/DebouncedSearchProvider.tsx @@ -30,7 +30,7 @@ function DebouncedSearchProvider({ const { mutateAsync } = useMutationAPI(); const [provider, setProvider] = useState(searchProvider); - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const handleClose = () => { @@ -39,13 +39,13 @@ function DebouncedSearchProvider({ }; useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: 'api/providers', body: debounced }); } }, [debounced]); useEffect(() => { - if (update !== provider && Object.keys(update).length) { + if (update && update !== provider && Object.keys(update).length) { setProvider(update); } }, [update]); @@ -56,22 +56,24 @@ function DebouncedSearchProvider({ className={cardClasses.settingsItem} classNames={cardClasses} > - handleDelete(provider, 'api/providers')} - title={'Delete Search Provider'} - body={ - - Are you sure you want to delete this Search Provider? - {`"${provider.name}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(provider, 'api/providers')} + title={'Delete Search Provider'} + body={ + + Are you sure you want to delete this Search Provider? + {`"${provider.name}"`} + This action cannot be undone! + + } + /> + )} { +export function SearchHistory() { const searchEndpoint = 'api/searchHistory'; const { data: historyData, isFetching: historyDataIsFetching } = useQueryAPI({ endpoint: searchEndpoint, @@ -41,28 +45,30 @@ const SearchHistory = () => { return ( - handleDelete(searchHistoryItem, 'api/searchHistory')} - title={'Delete Search History Item'} - body={ - - Are you sure you want to delete this Search History Item? - - {`${searchHistoryItem?.provider}`} - {`"${searchHistoryItem?.searchTerm}"`} - - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(searchHistoryItem, 'api/searchHistory')} + title={'Delete Search History Item'} + body={ + + Are you sure you want to delete this Search History Item? + + {`${searchHistoryItem?.provider}`} + {`"${searchHistoryItem?.searchTerm}"`} + + This action cannot be undone! + + } + /> + )} { ); -}; - -export default SearchHistory; +} diff --git a/src/pages/settings/components/Search/SearchProviders.tsx b/src/pages/settings/components/Search/SearchProviders.tsx index 9a242f4..4245c18 100644 --- a/src/pages/settings/components/Search/SearchProviders.tsx +++ b/src/pages/settings/components/Search/SearchProviders.tsx @@ -35,7 +35,7 @@ import type { Provider } from 'types'; import DebouncedSearchProvider from './DebouncedSearchProvider'; import SearchProviderSettingsItem from './SearchProviderSettingsItem'; -const SearchProviderSettings = () => { +export function SearchProviders() { const allIcons: any = { ...fa6, ...vsc, ...si }; const defaultProvider: Provider = { id: undefined, @@ -88,30 +88,120 @@ const SearchProviderSettings = () => { return ( - { - mutateAsync({ - method: 'POST', - endpoint: endpoint, - body: values, - }); - handleClose(); - })} - > - - - + {modalOpened && ( + { + mutateAsync({ + method: 'POST', + endpoint: endpoint, + body: values, + }); + handleClose(); + })} + > + + + + + + { - return { value: icon, label: icon }; - })} - comboboxProps={{ - zIndex: 3000, - }} - styles={ - theme.textShadowsEnabled - ? { - label: { boxShadow: '2px 2px var(--mantine-color-border-0)' }, - } - : undefined - } - renderOption={(item: ComboboxLikeRenderOptionInput) => { - const Icon = allIcons[item.option.value]; - return ( - - {item.option.value} - - - ); - }} - searchable - clearable - maxDropdownHeight={175} - onDropdownOpen={() => setIsOpen(true)} - onDropdownClose={() => setIsOpen(false)} /> + + + + } + /> + - - - - - } - /> - - - - - - - - - } - /> + + + + + + + } + /> + )} { ); -}; -export default SearchProviderSettings; +} diff --git a/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylists.tsx b/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylists.tsx index 1ff13c5..90a7e24 100644 --- a/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylists.tsx +++ b/src/pages/settings/components/Theming/ThemePlaylists/ThemePlaylists.tsx @@ -67,16 +67,18 @@ export function ThemePlaylists() { return ( - + {opened && ( + + )} setHover(true)} onMouseLeave={() => setHover(false)} > - handleDelete(cardPlaylist, 'api/themePlaylists')} - title='Delete Theme Playlist' - body={ - - Are you sure you want to delete this Theme Playlist? - {`"${cardPlaylist.name}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(cardPlaylist, 'api/themePlaylists')} + title='Delete Theme Playlist' + body={ + + Are you sure you want to delete this Theme Playlist? + {`"${cardPlaylist.name}"`} + This action cannot be undone! + + } + /> + )} ([]); - useEffect(() => { if (selectedPlaylist?.themeList) { setThemeList(JSON.parse(selectedPlaylist?.themeList)); @@ -49,7 +48,7 @@ export function ThemePlaylistModal({ centered onClose={() => { handleClose(); - setThemeList('[]'); + setThemeList([]); }} keepMounted={false} > diff --git a/src/pages/settings/components/Theming/Themes/ThemeCard/components/ThemeManagementPill.tsx b/src/pages/settings/components/Theming/Themes/ThemeCard/components/ThemeManagementPill.tsx index e99f10b..e3f7299 100644 --- a/src/pages/settings/components/Theming/Themes/ThemeCard/components/ThemeManagementPill.tsx +++ b/src/pages/settings/components/Theming/Themes/ThemeCard/components/ThemeManagementPill.tsx @@ -1,8 +1,7 @@ import { ActionIcon, Group, isLightColor, Stack, Text } from '@mantine/core'; import { useCommonHooks, useMutationAPI } from 'hooks'; import { useState } from 'react'; -import { FaTrashAlt } from 'react-icons/fa'; -import { FaPencil } from 'react-icons/fa6'; +import { FaPencil, FaTrash } from 'react-icons/fa6'; import { FiEdit, FiPower, FiShuffle } from 'react-icons/fi'; import { actionIconClasses } from 'styles'; import type { Theme, ThemePlaylist } from 'types'; @@ -238,7 +237,7 @@ export function ThemeManagementPill({ onClick={openModal} disabled={cardTheme.name === theme.name} > - diff --git a/src/pages/settings/components/Theming/Themes/ThemeModal/components/NewThemeStarter.tsx b/src/pages/settings/components/Theming/Themes/ThemeModal/components/NewThemeStarter.tsx index 1aa436b..4f94250 100644 --- a/src/pages/settings/components/Theming/Themes/ThemeModal/components/NewThemeStarter.tsx +++ b/src/pages/settings/components/Theming/Themes/ThemeModal/components/NewThemeStarter.tsx @@ -27,7 +27,7 @@ export function NewThemeStarter({ setSelectedTheme: (selectedTheme: Theme) => void; setDefaultTheme: (selectedTheme: Theme) => void; }) { - const [pressed, setPressed] = useState(false); + const [pressed, setPressed] = useState(false); const handlers = useLongPress(() => { setPressed(true); }); diff --git a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeChanges.tsx b/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeChanges.tsx index 225da2c..33008f1 100644 --- a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeChanges.tsx +++ b/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeChanges.tsx @@ -10,8 +10,7 @@ import { } from '@mantine/core'; import { notifications } from '@mantine/notifications'; import { useCommonHooks } from 'hooks'; -import { FaClipboard } from 'react-icons/fa'; -import { FiX } from 'react-icons/fi'; +import { FaClipboard, FaX } from 'react-icons/fa6'; import { scrollAreaClasses, toolTipClasses } from 'styles'; import { type Theme, ThemeProperties } from 'types'; @@ -104,7 +103,7 @@ export default function ThemeChanges({ pos={'absolute'} right={0} > - + diff --git a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeExampleApp.tsx b/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeExampleApp.tsx deleted file mode 100644 index ffa5f6b..0000000 --- a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeExampleApp.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Box, Stack, Text } from '@mantine/core'; -import { useState } from 'react'; -import { FiCheck } from 'react-icons/fi'; -import { boxClasses, textClasses } from 'styles'; -import type { Theme } from 'types'; - -export function ThemeExampleApp({ selectedTheme }: { selectedTheme: Theme }) { - const [textDecoration, setTextDecoration] = useState('none'); - - return ( - setTextDecoration('underline')} - onMouseOut={() => setTextDecoration('none')} - className={boxClasses.appItemBox} - h={'64px'} - > - - - - - - {'Example App'} - - - {'https://www.example.com'} - - - - ); -} diff --git a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeOptions.tsx b/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeOptions.tsx index ba436b0..4be5f35 100644 --- a/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeOptions.tsx +++ b/src/pages/settings/components/Theming/Themes/ThemeModal/components/ThemeOptions.tsx @@ -11,9 +11,7 @@ import { } from '@mantine/core'; import { useCommonHooks } from 'hooks'; import FontPicker from 'react-fontpicker-ts'; -import { FaFont, FaGoogle } from 'react-icons/fa'; -import { FaShuffle } from 'react-icons/fa6'; -import { FiX } from 'react-icons/fi'; +import { FaFont, FaGoogle, FaShuffle, FaX } from 'react-icons/fa6'; import { textClasses, toolTipClasses } from 'styles'; import type { Theme } from 'types'; import ThemeChanges from './ThemeChanges'; @@ -192,7 +190,7 @@ export default function ThemeOptions({ borderRadius: '0px', }} > - + diff --git a/src/pages/settings/components/Theming/Themes/ThemePreviewMiddleware.tsx b/src/pages/settings/components/Theming/Themes/ThemePreviewMiddleware.tsx index d6c84b4..8341238 100644 --- a/src/pages/settings/components/Theming/Themes/ThemePreviewMiddleware.tsx +++ b/src/pages/settings/components/Theming/Themes/ThemePreviewMiddleware.tsx @@ -17,7 +17,7 @@ export function ThemePreviewMiddleware({ selectedPlaylist: any; setSelectedPlaylist: (selectedPlaylist: any) => void; }) { - const [pressed, setPressed] = useState(false); + const [pressed, setPressed] = useState(false); const handlers = useLongPress(() => { setPressed(true); }); diff --git a/src/pages/settings/components/WoL/DebouncedWolClient.tsx b/src/pages/settings/components/WoL/DebouncedWolClient.tsx index 90f8014..911b953 100644 --- a/src/pages/settings/components/WoL/DebouncedWolClient.tsx +++ b/src/pages/settings/components/WoL/DebouncedWolClient.tsx @@ -3,7 +3,7 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { DeleteItemModal } from 'components'; import { useCommonHooks, useMutationAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FiTrash } from 'react-icons/fi'; +import { FaTrash } from 'react-icons/fa'; import { cardClasses, textClasses, textInputClasses, toolTipClasses } from 'styles'; import type { WolClient } from 'types'; @@ -19,7 +19,7 @@ function DebouncedWolClient({ const { mutateAsync } = useMutationAPI(); const [client, setClient] = useState(wolClient); - const [update, setUpdate] = useState({}); + const [update, setUpdate] = useState(); const [debounced] = useDebouncedValue(update, 1000); const handleClose = () => { setIsOpen(false); @@ -27,13 +27,13 @@ function DebouncedWolClient({ }; useEffect(() => { - if (Object.keys(debounced).length) { + if (debounced && Object.keys(debounced).length) { mutateAsync({ method: 'PATCH', endpoint: 'api/WolClients', body: debounced }); } }, [debounced]); useEffect(() => { - if (update !== client && Object.keys(update).length) { + if (update && update !== client && Object.keys(update).length) { setClient(update); } }, [update]); @@ -44,22 +44,24 @@ function DebouncedWolClient({ className={cardClasses.settingsItem} classNames={cardClasses} > - handleDelete(client, 'api/WolClients')} - title={'Delete WoL Client'} - body={ - - Are you sure you want to delete this WoL Client? - {`"${client.hostname}"`} - This action cannot be undone! - - } - /> + {modalOpened && ( + handleDelete(client, 'api/WolClients')} + title={'Delete WoL Client'} + body={ + + Are you sure you want to delete this WoL Client? + {`"${client.hostname}"`} + This action cannot be undone! + + } + /> + )} - + diff --git a/src/pages/settings/components/WoL/WolClients.tsx b/src/pages/settings/components/WoL/WolClients.tsx index 1883ab1..b7a5634 100644 --- a/src/pages/settings/components/WoL/WolClients.tsx +++ b/src/pages/settings/components/WoL/WolClients.tsx @@ -18,7 +18,7 @@ import type { WolClient } from 'types'; import DebouncedWolClient from './DebouncedWolClient'; import WolClientSettingsItem from './WolClientSettingsItem'; -const WolClientSettings = () => { +export function WolClients() { const defaultWolClient: WolClient = { id: undefined, hostname: '', @@ -65,76 +65,78 @@ const WolClientSettings = () => { return ( - { - mutateAsync({ - method: 'POST', - endpoint: endpoint, - body: values, - }); - handleClose(); - })} - > - - - - - - - - - - - - } - /> + {modalOpened && ( + { + mutateAsync({ + method: 'POST', + endpoint: endpoint, + body: values, + }); + handleClose(); + })} + > + + + + + + + + + + + + } + /> + )} { ); -}; -export default WolClientSettings; +} diff --git a/src/pages/settings/components/index.ts b/src/pages/settings/components/index.ts new file mode 100644 index 0000000..4862b4b --- /dev/null +++ b/src/pages/settings/components/index.ts @@ -0,0 +1,10 @@ +export { Applications } from './Applications/Applications'; +export { Bookmarks } from './Bookmarks/Bookmarks'; +export { GuacamoleClients } from './GuacamoleClients/GuacamoleClients'; +export { Messages } from './Messages/Messages'; +export { Personalization } from './Personalization/Personalization'; +export { SearchHistory } from './Search/SearchHistory'; +export { SearchProviders } from './Search/SearchProviders'; +export { ThemePlaylists } from './Theming/ThemePlaylists'; +export { Themes } from './Theming/Themes'; +export { WolClients } from './WoL/WolClients';