Added SettingsHeader

This commit is contained in:
Sean Edmunds
2025-04-03 10:55:19 -05:00
parent 329a9bbc98
commit f5e193853f
11 changed files with 304 additions and 648 deletions

View File

@@ -1,6 +1,5 @@
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
import {
ActionIcon,
Box,
Button,
Card,
@@ -8,27 +7,18 @@ import {
Grid,
GridCol,
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 { Application } from 'types';
import SettingsHeader from '../Shared/SettingsHeader';
import AppSettingsItem from './AppSettingsItem';
import DebouncedApplication from './DebouncedApplication';
@@ -236,99 +226,28 @@ const ApplicationSettings = () => {
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Applications
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Applications are the apps that will be displayed on the homepage.'}
</Text>
<Highlight
highlight={[
'Applications',
'apps',
'application',
'name',
'base url',
'url',
'icon',
'filter',
]}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'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.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
<Switch
label={<Text c={theme.primaryColor}>Reorder</Text>}
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,
},
}}
/>
</Group>
<SettingsHeader
setDragging={setDragging}
openModal={openModal}
title={'Applications'}
tooltip={{
title: 'Applications are the apps that will be displayed on the homepage.',
highlightList: [
'Applications',
'apps',
'application',
'name',
'base url',
'url',
'icon',
'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.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
{dragging ? (
<DragDropContext onDragEnd={onDragEnd}>
<Stack w={'100%'} align='center'>

View File

@@ -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 = () => {
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Bookmarks
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Bookmarks are the links that will be displayed on the homepage.'}
</Text>
<Highlight
highlight={[
'Bookmark',
'Bookmarks',
'Group',
'Groups',
'Link',
'Links',
'Category',
'Categories',
]}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'Bookmarks can only belong to a single category, and will be displayed under that category on the homepage.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
<Switch
label={<Text c={theme.primaryColor}>Reorder</Text>}
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,
},
}}
/>
</Group>
<SettingsHeader
setDragging={setDragging}
openModal={openModal}
title={'Bookmarks'}
tooltip={{
title: 'Bookmarks are the links that will be displayed on the homepage.',
highlightList: [
'Bookmark',
'Bookmarks',
'Group',
'Groups',
'Link',
'Links',
'Category',
'Categories',
],
description:
'Bookmarks can only belong to a single category, and will be displayed under that category on the homepage.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
{dragging ? (
<DragDropContext onDragEnd={onDragEnd}>
<Stack w={'100%'} align='center'>

View File

@@ -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<Message[]>([]);
const [newMessage, setNewMessage] = useState('');
@@ -129,73 +118,19 @@ const MessageSettings = () => {
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Messages
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{
'These custom messages will be displayed in the header on the homepage.'
}
</Text>
<Highlight
highlight={['$firstName', '$lastName', '$^firstName', '$^lastName']}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'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.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
</Group>
<SettingsHeader
setDragging={undefined}
openModal={openModal}
title={'Messages'}
tooltip={{
title: 'These custom messages will be displayed in the header on the homepage.',
highlightList: ['$firstName', '$lastName', '$^firstName', '$^lastName'],
description:
'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.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Stack w={'100%'} align='center'>
<Grid w={'75%'}>
{messages?.map((message: Message) => (

View File

@@ -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<Provider[]>([]);
const { theme, isMobile, modalStyle } = useCommonHooks();
const { isMobile, modalStyle } = useCommonHooks();
const { mutateAsync } = useMutationAPI();
const [newProvider, setNewProvider] = useState<Provider>({
id: undefined,
@@ -160,71 +150,19 @@ const SearchProviderSettings = () => {
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Search Providers
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Search providers and shortcuts can be configured here.'}
</Text>
<Highlight
highlight={['Providers', 'search']}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'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.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
</Group>
<SettingsHeader
setDragging={undefined}
openModal={openModal}
title={'Search Providers'}
tooltip={{
title: 'Search providers and shortcuts can be configured here.',
highlightList: ['Providers', 'search'],
description:
'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.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Stack w={'100%'} align='center'>
<Grid columns={18} w={'75%'}>
{providerData?.map((provider: Provider) => (

View File

@@ -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 (
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
{title}
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{tooltip.title}
</Text>
<Highlight
highlight={tooltip.highlightList}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{tooltip.description}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
{setDragging && (
<Switch
label={<Text c={theme.primaryColor}>Reorder</Text>}
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,
},
}}
/>
)}
</Group>
);
}
export default SettingsHeader;

View File

@@ -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) => (
<UnstyledButton

View File

@@ -30,6 +30,14 @@ function ThemePlaylistModal({
setThemeList(JSON.parse(selectedPlaylist?.themeList));
}
}, [selectedPlaylist]);
if (selectedPlaylist.name === undefined) {
setSelectedPlaylist({
name: 'New Playlist',
themeList: '[]',
});
}
return (
<Modal.Root
size={'50%'}
@@ -40,7 +48,6 @@ function ThemePlaylistModal({
onClose={() => {
handleClose();
setThemeList('[]');
setSelectedPlaylist({});
}}
style={{
'--color-text-pri': theme.primaryColor,

View File

@@ -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<any>({});
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 (
<>
<Stack>
<ThemePlaylistModal
opened={opened}
handleClose={handleClose}
@@ -63,77 +54,19 @@ function ThemePlaylists() {
createPlaylist={mutateAsync}
/>
<Stack h={'100%'} w={'100%'}>
<Group w={'100%'} justify='center' align='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Theme Playlists
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Theme playlists can be created here and applied to the homepage.'}
</Text>
<Highlight
highlight={['Theme', 'Themes', 'Playlist', 'Playlists']}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'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.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={() => {
setSelectedPlaylist({
name: 'New Playlist',
themeList: '[]',
});
open();
}}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
</Group>
<SettingsHeader
setDragging={undefined}
openModal={open}
title={'Theme Playlists'}
tooltip={{
title: 'Theme playlists can be created here and applied to the homepage.',
highlightList: ['Clients', 'homepage'],
description:
'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.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Group justify='center' align='center' pos={'relative'}>
<Text p={0} className={textClasses.settingsSubTitle} classNames={textClasses}>
Defaults
@@ -222,7 +155,7 @@ function ThemePlaylists() {
})}
</Group>
</Stack>
</>
</Stack>
);
}

View File

@@ -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 (
<>
<Stack>
<ThemeModal
opened={opened}
handleClose={handleClose}
@@ -84,102 +73,38 @@ function ThemesSettings() {
handleSaveTheme={handleSaveTheme}
/>
<Stack h={'100%'} w={'100%'}>
<Group w={'100%'} justify='center' align='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
Themes
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
width: '500px',
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Themes can be created here and applied to the homepage.'}
</Text>
<Highlight
highlight={['Theme', 'Themes', 'Playlist', 'Playlists']}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{
'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.'
}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={() => {
setSelectedTheme({
id: undefined,
name: 'default',
backgroundImage: 'none',
backgroundColor: '#EEEEEE',
searchColor: '#00000033',
primaryColor: '#222222',
accentColor: '#5c5c5c',
borderColor: '#5c5c5c',
});
open();
}}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
<Switch
label={
isMobile ? (
<FaDice size={20} color={theme.primaryColor} />
) : (
<Text c={theme.primaryColor}>Random Theme</Text>
)
}
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}
/>
</Group>
<SettingsHeader
setDragging={undefined}
openModal={open}
title={'Themes'}
tooltip={{
title: 'Themes can be created here and applied to the homepage.',
highlightList: ['Clients', 'homepage'],
description:
'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.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Switch
label={
isMobile ? (
<FaDice size={20} color={theme.primaryColor} />
) : (
<Text c={theme.primaryColor}>Random Theme</Text>
)
}
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}
/>
<Group justify='center' align='center' pos={'relative'} mt={'1rem'}>
<Overlay
display={currentTheme === 'random' ? 'flex' : 'none'}
@@ -233,7 +158,7 @@ function ThemesSettings() {
))}
</Group>
</Stack>
</>
</Stack>
);
}
export default ThemesSettings;

View File

@@ -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<WolClient[]>([]);
const { mutateAsync } = useMutationAPI();
const { theme, isMobile, modalStyle } = useCommonHooks();
const { isMobile, modalStyle } = useCommonHooks();
const [newWolClient, setNewWolClient] = useState<WolClient>({
id: undefined,
hostname: '',
@@ -141,69 +131,18 @@ const WolClientSettings = () => {
</Modal.Body>
</Modal.Content>
</Modal.Root>
<Group align='center' justify='center' mt={'1rem'}>
<Group w={isMobile ? undefined : '100%'}>
<Text className={textClasses.settingsSubTitle} classNames={textClasses}>
WoL Clients
</Text>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<ActionIcon
className={actionIconClasses.settingsTooltip}
classNames={actionIconClasses}
>
<FiHelpCircle />
</ActionIcon>
</Popover.Target>
<Popover.Dropdown
style={{
color: theme.primaryColor,
backgroundColor: theme.backgroundColor,
}}
>
<Stack>
<Text className={textClasses.settingsTooltip} classNames={textClasses}>
{'Wake on Lan clients can be configured here.'}
</Text>
<Highlight
ta={'center'}
highlight={['Clients', 'homepage']}
className={textClasses.settingsTooltip}
classNames={textClasses}
highlightStyles={{
backgroundImage:
'linear-gradient(45deg, var(--color-text-acc), var(--color-text-pri))',
fontWeight: 700,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
{'Clients can be powered on from the settings button the homepage.'}
</Highlight>
</Stack>
</Popover.Dropdown>
</Popover>
<Button
className={buttonClasses.tableButton}
classNames={buttonClasses}
onClick={openModal}
>
<FiPlus />
</Button>
</Group>
<TextInput
w={isMobile ? undefined : '35%'}
pos={isMobile ? undefined : 'absolute'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
autoFocus={!isMobile}
placeholder='Search...'
value={searchValue}
onInput={(event: any) => {
setSearchValue(event.target.value);
}}
/>
</Group>
<SettingsHeader
setDragging={undefined}
openModal={openModal}
title={'Search Providers'}
tooltip={{
title: 'Wake on Lan clients can be configured here.',
highlightList: ['Clients', 'homepage'],
description: 'Clients can be powered on from the settings button the homepage.',
}}
searchValue={searchValue}
setSearchValue={setSearchValue}
/>
<Stack w={'100%'} align='center'>
<Grid columns={18} w={'75%'}>
{wolClients?.map((client: WolClient) => (