From f5e193853f459fbdd7b8cb6942f76384f9000235 Mon Sep 17 00:00:00 2001 From: Sean Edmunds Date: Thu, 3 Apr 2025 10:55:19 -0500 Subject: [PATCH] Added SettingsHeader --- src/components/WeatherModal/WeatherIcon.tsx | 16 ++ .../components/Applications/Applications.tsx | 129 +++------------ .../components/Bookmarks/Bookmarks.tsx | 130 +++------------ .../settings/components/Messages/Messages.tsx | 97 ++--------- .../components/Search/SearchProviders.tsx | 94 ++--------- .../components/Shared/SettingsHeader.tsx | 113 +++++++++++++ .../components/Theming/ThemeModal.tsx | 13 ++ .../components/Theming/ThemePlaylistModal.tsx | 9 +- .../components/Theming/ThemePlaylists.tsx | 109 +++---------- .../settings/components/Theming/Themes.tsx | 151 +++++------------- .../settings/components/WoL/WolClients.tsx | 91 ++--------- 11 files changed, 304 insertions(+), 648 deletions(-) create mode 100644 src/pages/settings/components/Shared/SettingsHeader.tsx diff --git a/src/components/WeatherModal/WeatherIcon.tsx b/src/components/WeatherModal/WeatherIcon.tsx index adf74df..92cf5ef 100644 --- a/src/components/WeatherModal/WeatherIcon.tsx +++ b/src/components/WeatherModal/WeatherIcon.tsx @@ -41,6 +41,22 @@ export default function WeatherIcon({ textDescription }: { textDescription: stri alt='RainIcon' /> ); + case 'Chance Showers': + return ( + RainIcon + ); + case 'Slight Chance Showers': + return ( + RainIcon + ); case 'Clear': return ( { - - - - Applications - - - - - - - - - - - {'Applications are the apps that will be displayed on the homepage.'} - - - { - '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.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - Reorder} - labelPosition='left' - pos={'absolute'} - right={isMobile ? '2rem' : '1rem'} - top={isMobile ? '10rem' : undefined} - onChange={(e) => { - setDragging(e.target.checked); - }} - color={theme.primaryColor} - styles={{ - label: { - color: theme.primaryColor, - top: isMobile ? -22 : 0, - left: isMobile ? -8 : 0, - position: isMobile ? 'absolute' : undefined, - }, - }} - /> - + {dragging ? ( diff --git a/src/pages/settings/components/Bookmarks/Bookmarks.tsx b/src/pages/settings/components/Bookmarks/Bookmarks.tsx index 0eb4e71..4cd5cf3 100644 --- a/src/pages/settings/components/Bookmarks/Bookmarks.tsx +++ b/src/pages/settings/components/Bookmarks/Bookmarks.tsx @@ -1,32 +1,21 @@ import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'; import { - ActionIcon, Box, Button, Card, Divider, Group, - Highlight, Modal, - Popover, Stack, - Switch, Text, TextInput, } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FiHelpCircle, FiPlus } from 'react-icons/fi'; -import { - actionIconClasses, - buttonClasses, - cardClasses, - modalClasses, - textClasses, - textInputClasses, -} from 'styles'; +import { cardClasses, modalClasses, textInputClasses } from 'styles'; import type { Bookmark, BookmarkGroup } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem'; const BookmarkSettings = () => { const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({ @@ -190,99 +179,28 @@ const BookmarkSettings = () => { - - - - Bookmarks - - - - - - - - - - - {'Bookmarks are the links that will be displayed on the homepage.'} - - - { - 'Bookmarks can only belong to a single category, and will be displayed under that category on the homepage.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - Reorder} - labelPosition='left' - pos={'absolute'} - right={isMobile ? '2rem' : '1rem'} - top={isMobile ? '10rem' : undefined} - onChange={(e) => { - setDragging(e.target.checked); - }} - color={theme.primaryColor} - styles={{ - label: { - color: theme.primaryColor, - top: isMobile ? -22 : 0, - left: isMobile ? -8 : 0, - position: isMobile ? 'absolute' : undefined, - }, - }} - /> - + {dragging ? ( diff --git a/src/pages/settings/components/Messages/Messages.tsx b/src/pages/settings/components/Messages/Messages.tsx index 21d562f..53a2c8c 100644 --- a/src/pages/settings/components/Messages/Messages.tsx +++ b/src/pages/settings/components/Messages/Messages.tsx @@ -1,38 +1,27 @@ import { - ActionIcon, Button, Card, Divider, Grid, GridCol, Group, - Highlight, Modal, - Popover, Stack, - Text, TextInput, } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import DebouncedMessage from 'pages/settings/components/Messages/DebouncedMessage'; import { useEffect, useState } from 'react'; -import { FiHelpCircle, FiPlus } from 'react-icons/fi'; -import { - actionIconClasses, - buttonClasses, - cardClasses, - modalClasses, - textClasses, - textInputClasses, -} from 'styles'; +import { cardClasses, modalClasses, textInputClasses } from 'styles'; import type { Message } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; const MessageSettings = () => { const endpoint = 'api/messages'; const { data: messageData, isFetched: isMessagesFetched } = useQueryAPI({ endpoint: endpoint, }); - const { theme, isMobile, modalStyle } = useCommonHooks(); + const { isMobile, modalStyle } = useCommonHooks(); const { mutateAsync } = useMutationAPI(); const [messages, setMessages] = useState([]); const [newMessage, setNewMessage] = useState(''); @@ -129,73 +118,19 @@ const MessageSettings = () => { - - - - Messages - - - - - - - - - - - { - 'These custom messages will be displayed in the header on the homepage.' - } - - - { - 'Built-in variables include $firstName and $lastName. To force the text to upper case, use a carat (^), ie, $^firstName. Messages may contain any unicode text.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - + {messages?.map((message: Message) => ( diff --git a/src/pages/settings/components/Search/SearchProviders.tsx b/src/pages/settings/components/Search/SearchProviders.tsx index b7949f9..2f3e408 100644 --- a/src/pages/settings/components/Search/SearchProviders.tsx +++ b/src/pages/settings/components/Search/SearchProviders.tsx @@ -1,5 +1,4 @@ import { - ActionIcon, Box, Button, Card, @@ -7,26 +6,17 @@ import { Grid, GridCol, Group, - Highlight, Modal, Popover, Stack, - Text, TextInput, } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FiHelpCircle, FiPlus } from 'react-icons/fi'; -import { - actionIconClasses, - buttonClasses, - cardClasses, - modalClasses, - textClasses, - textInputClasses, -} from 'styles'; +import { cardClasses, modalClasses, textInputClasses } from 'styles'; import type { Provider } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; import DebouncedSearchProvider from './DebouncedSearchProvider'; import SearchProviderSettingsItem from './SearchProviderSettingsItem'; @@ -36,7 +26,7 @@ const SearchProviderSettings = () => { endpoint: endpoint, }); const [providerData, setProviderData] = useState([]); - const { theme, isMobile, modalStyle } = useCommonHooks(); + const { isMobile, modalStyle } = useCommonHooks(); const { mutateAsync } = useMutationAPI(); const [newProvider, setNewProvider] = useState({ id: undefined, @@ -160,71 +150,19 @@ const SearchProviderSettings = () => { - - - - Search Providers - - - - - - - - - - - {'Search providers and shortcuts can be configured here.'} - - - { - 'Providers can be used in the search bar. By starting a search with the prefix, it will be redirected to the url of the provider to complete the search.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - + {providerData?.map((provider: Provider) => ( diff --git a/src/pages/settings/components/Shared/SettingsHeader.tsx b/src/pages/settings/components/Shared/SettingsHeader.tsx new file mode 100644 index 0000000..55b2092 --- /dev/null +++ b/src/pages/settings/components/Shared/SettingsHeader.tsx @@ -0,0 +1,113 @@ +import { + ActionIcon, + Button, + Group, + Highlight, + Popover, + Stack, + Switch, + Text, + TextInput, +} from '@mantine/core'; +import { useCommonHooks } from 'hooks'; +import { FiHelpCircle, FiPlus } from 'react-icons/fi'; +import { actionIconClasses, buttonClasses, textClasses, textInputClasses } from 'styles'; + +function SettingsHeader({ + setDragging, + openModal, + title, + tooltip, + searchValue, + setSearchValue, +}: any) { + const { isMobile, theme } = useCommonHooks(); + return ( + + + + {title} + + + + + + + + + + + {tooltip.title} + + + {tooltip.description} + + + + + + + { + setSearchValue(event.target.value); + }} + /> + {setDragging && ( + Reorder} + labelPosition='left' + pos={'absolute'} + right={isMobile ? '2rem' : '1rem'} + top={isMobile ? '10rem' : undefined} + onChange={(e) => { + setDragging(e.target.checked); + }} + color={theme.primaryColor} + styles={{ + label: { + color: theme.primaryColor, + top: isMobile ? -22 : 0, + left: isMobile ? -8 : 0, + position: isMobile ? 'absolute' : undefined, + }, + }} + /> + )} + + ); +} + +export default SettingsHeader; diff --git a/src/pages/settings/components/Theming/ThemeModal.tsx b/src/pages/settings/components/Theming/ThemeModal.tsx index 401ca40..3e0bd5a 100644 --- a/src/pages/settings/components/Theming/ThemeModal.tsx +++ b/src/pages/settings/components/Theming/ThemeModal.tsx @@ -39,6 +39,19 @@ function ThemeModal({ const filtered = backgroundList.filter((item: any) => item.toLowerCase().includes(query.toLowerCase()), ); + if (selectedTheme.name === undefined) { + setSelectedTheme({ + id: undefined, + name: 'default', + backgroundImage: 'none', + backgroundColor: '#EEEEEE', + searchColor: '#00000033', + primaryColor: '#222222', + accentColor: '#5c5c5c', + borderColor: '#5c5c5c', + }); + } + const re = /(?:\.([^.]+))?$/; const items = filtered.map((item: any, index: any) => ( { handleClose(); setThemeList('[]'); - setSelectedPlaylist({}); }} style={{ '--color-text-pri': theme.primaryColor, diff --git a/src/pages/settings/components/Theming/ThemePlaylists.tsx b/src/pages/settings/components/Theming/ThemePlaylists.tsx index e83829a..6c28edd 100644 --- a/src/pages/settings/components/Theming/ThemePlaylists.tsx +++ b/src/pages/settings/components/Theming/ThemePlaylists.tsx @@ -1,20 +1,11 @@ -import { - ActionIcon, - Box, - Button, - Group, - Highlight, - Popover, - Stack, - Text, - TextInput, -} from '@mantine/core'; +import { Box, Group, Stack, Text } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useState } from 'react'; -import { FiChevronRight, FiHelpCircle, FiPlus } from 'react-icons/fi'; -import { actionIconClasses, buttonClasses, textClasses, textInputClasses } from 'styles'; +import { FiChevronRight } from 'react-icons/fi'; +import { textClasses } from 'styles'; import type { ThemePlaylist } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; import ThemePlaylistCard from './ThemePlaylistCard'; import ThemePlaylistModal from './ThemePlaylistModal'; @@ -28,8 +19,7 @@ function ThemePlaylists() { const { mutateAsync } = useMutationAPI(); const [opened, { open, close }] = useDisclosure(false); const [selectedPlaylist, setSelectedPlaylist] = useState({}); - const { currentThemePlaylist, setCurrentThemePlaylist, theme, isMobile } = - useCommonHooks(); + const { currentThemePlaylist, setCurrentThemePlaylist, theme } = useCommonHooks(); const [searchValue, setSearchValue] = useState(''); const [debouncedSearch] = useDebouncedValue(searchValue, 500); @@ -39,6 +29,7 @@ function ThemePlaylists() { }; const handleClose = () => { + setSelectedPlaylist({}); close(); }; @@ -52,7 +43,7 @@ function ThemePlaylists() { }; return ( - <> + - - - - Theme Playlists - - - - - - - - - - - {'Theme playlists can be created here and applied to the homepage.'} - - - { - 'Playlists are lists of themes. They can be enabled either here or in the settings button on the homepage. When a playlist is enabled, it will shuffle the included themes on the homepage. If a theme is part of a playlist, it will appear while the playlist is active, regardless of random/shuffle.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - + Defaults @@ -222,7 +155,7 @@ function ThemePlaylists() { })} - + ); } diff --git a/src/pages/settings/components/Theming/Themes.tsx b/src/pages/settings/components/Theming/Themes.tsx index 348e3bd..f0ce445 100644 --- a/src/pages/settings/components/Theming/Themes.tsx +++ b/src/pages/settings/components/Theming/Themes.tsx @@ -1,25 +1,13 @@ -import { - ActionIcon, - Box, - Button, - Group, - Highlight, - Overlay, - Popover, - Stack, - Switch, - Text, - TextInput, -} from '@mantine/core'; +import { Box, Group, Overlay, Stack, Switch, Text } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FiChevronRight, FiHelpCircle, FiPlus } from 'react-icons/fi'; +import { FiChevronRight } from 'react-icons/fi'; import '@mantine/core/styles/ModalBase.css'; import '@mantine/core/styles/Modal.css'; import { FaDice } from 'react-icons/fa'; -import { actionIconClasses, buttonClasses, textClasses, textInputClasses } from 'styles'; import type { Theme } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; import ThemeCard from './ThemeCard'; import ThemeModal from './ThemeModal'; function ThemesSettings() { @@ -49,6 +37,7 @@ function ThemesSettings() { const handleClose = () => { close(); closePicker(); + setSelectedTheme({}); }; const handleSaveTheme = () => { @@ -66,7 +55,7 @@ function ThemesSettings() { } }, [backgroundData, isBackgroundFetched]); return ( - <> + - - - - Themes - - - - - - - - - - - {'Themes can be created here and applied to the homepage.'} - - - { - 'Themes are combinations of 5 colors and a background image, and can be added to a playlist. If a theme has been disabled, it will only be disabled with random/shuffle is enabled. If a theme is part of a playlist, it will appear while the playlist is active, regardless of random/shuffle.' - } - - - - - - - { - setSearchValue(event.target.value); - }} - /> - - ) : ( - Random Theme - ) - } - labelPosition='left' - pos={'absolute'} - right={isMobile ? '2rem' : '1rem'} - top={isMobile ? '9.5rem' : undefined} - checked={currentTheme === 'random'} - onChange={(e) => { - setCurrentTheme(e.target.checked ? 'random' : theme.name); - setCurrentThemePlaylist('All'); - }} - color={theme.primaryColor} - /> - + + + ) : ( + Random Theme + ) + } + labelPosition='left' + pos={'absolute'} + right={isMobile ? '2rem' : '1rem'} + top={isMobile ? '9.5rem' : '7rem'} + checked={currentTheme === 'random'} + onChange={(e) => { + setCurrentTheme(e.target.checked ? 'random' : theme.name); + setCurrentThemePlaylist('All'); + }} + color={theme.primaryColor} + /> - + ); } export default ThemesSettings; diff --git a/src/pages/settings/components/WoL/WolClients.tsx b/src/pages/settings/components/WoL/WolClients.tsx index 356b795..452d753 100644 --- a/src/pages/settings/components/WoL/WolClients.tsx +++ b/src/pages/settings/components/WoL/WolClients.tsx @@ -1,31 +1,21 @@ import { - ActionIcon, Button, Card, Divider, Grid, GridCol, Group, - Highlight, Modal, Popover, Stack, - Text, TextInput, } from '@mantine/core'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useEffect, useState } from 'react'; -import { FiHelpCircle, FiPlus } from 'react-icons/fi'; -import { - actionIconClasses, - buttonClasses, - cardClasses, - modalClasses, - textClasses, - textInputClasses, -} from 'styles'; +import { cardClasses, modalClasses, textInputClasses } from 'styles'; import type { WolClient } from 'types'; +import SettingsHeader from '../Shared/SettingsHeader'; import DebouncedWolClient from './DebouncedWolClient'; import WolClientSettingsItem from './WolClientSettingsItem'; @@ -36,7 +26,7 @@ const WolClientSettings = () => { }); const [wolClients, setWolClients] = useState([]); const { mutateAsync } = useMutationAPI(); - const { theme, isMobile, modalStyle } = useCommonHooks(); + const { isMobile, modalStyle } = useCommonHooks(); const [newWolClient, setNewWolClient] = useState({ id: undefined, hostname: '', @@ -141,69 +131,18 @@ const WolClientSettings = () => { - - - - WoL Clients - - - - - - - - - - - {'Wake on Lan clients can be configured here.'} - - - {'Clients can be powered on from the settings button the homepage.'} - - - - - - - { - setSearchValue(event.target.value); - }} - /> - + {wolClients?.map((client: WolClient) => (