More performance updates

This commit is contained in:
Sean Edmunds
2026-01-09 12:45:40 -06:00
parent 3307bcefc9
commit 924b42a567
31 changed files with 227 additions and 174 deletions

62
package-lock.json generated
View File

@@ -1,20 +1,20 @@
{ {
"name": "triggerfrontend", "name": "triggerfrontend",
"version": "1.0.0", "version": "1.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "triggerfrontend", "name": "triggerfrontend",
"version": "1.0.0", "version": "1.1.0",
"dependencies": { "dependencies": {
"@gfazioli/mantine-text-animate": "^2.3.10", "@gfazioli/mantine-text-animate": "^2.3.10",
"@hello-pangea/dnd": "^18.0.1", "@hello-pangea/dnd": "^18.0.1",
"@mantine/core": "^8.3.11", "@mantine/core": "^8.3.12",
"@mantine/dates": "^8.3.11", "@mantine/dates": "^8.3.12",
"@mantine/form": "^8.3.11", "@mantine/form": "^8.3.12",
"@mantine/hooks": "^8.3.11", "@mantine/hooks": "^8.3.12",
"@mantine/notifications": "^8.3.11", "@mantine/notifications": "^8.3.12",
"@tanstack/react-query": "^5.90.16", "@tanstack/react-query": "^5.90.16",
"@tanstack/react-query-devtools": "^5.91.2", "@tanstack/react-query-devtools": "^5.91.2",
"@tanstack/react-query-persist-client": "^5.90.18", "@tanstack/react-query-persist-client": "^5.90.18",
@@ -911,9 +911,9 @@
} }
}, },
"node_modules/@mantine/core": { "node_modules/@mantine/core": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/core/-/core-8.3.12.tgz",
"integrity": "sha512-FWXp94tiTFdh8BKc7UTNdIuumD96JFxLF/VvUWGRzf5i563Ye0Ma/aZtOBwpdEtdIQkojOhoMMV76k5toOQgEQ==", "integrity": "sha512-bDEoUl4SneltfI1GeEaBk6BVDbLuB/w15YwseAmUvc8ldAbNcsVhxKxY/BdhwqUo6O3L2vhdlb3WwxR1y8741g==",
"dependencies": { "dependencies": {
"@floating-ui/react": "^0.27.16", "@floating-ui/react": "^0.27.16",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@@ -923,30 +923,30 @@
"type-fest": "^4.41.0" "type-fest": "^4.41.0"
}, },
"peerDependencies": { "peerDependencies": {
"@mantine/hooks": "8.3.11", "@mantine/hooks": "8.3.12",
"react": "^18.x || ^19.x", "react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x" "react-dom": "^18.x || ^19.x"
} }
}, },
"node_modules/@mantine/dates": { "node_modules/@mantine/dates": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/dates/-/dates-8.3.12.tgz",
"integrity": "sha512-e5QBHMpTjKDkYHFEYkD3bho4Kk/M04jmXLu9Txb/XkWVDb5A0CNGg6+A8TinXING4B7kn2dMOLmL3s7r6vnJJA==", "integrity": "sha512-rL2kYJRTrU29l1KjQt5bdUweiZuJs3DjiIcc6VGHUuj7OwfYZbEpWzMbcu8fSrya/+kZAe8JM0irX22CjmsDGw==",
"dependencies": { "dependencies": {
"clsx": "^2.1.1" "clsx": "^2.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"@mantine/core": "8.3.11", "@mantine/core": "8.3.12",
"@mantine/hooks": "8.3.11", "@mantine/hooks": "8.3.12",
"dayjs": ">=1.0.0", "dayjs": ">=1.0.0",
"react": "^18.x || ^19.x", "react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x" "react-dom": "^18.x || ^19.x"
} }
}, },
"node_modules/@mantine/form": { "node_modules/@mantine/form": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/form/-/form-8.3.12.tgz",
"integrity": "sha512-546npW8gHCXUShVL/OOnKBJ1So5bcKTdmiNxzXn8q2eGhMIB+zdj7/v4QUz2j55OjDRPq0dAVJhzDjLfzeibTg==", "integrity": "sha512-bjXL1Ah3lAZYHrjrF37FeHV8DFGeXV+apn0PRQw2MTLpdUSqKsiYIDDgugzRfNXWX1cn1ELtxtDXsMfdj/LI7Q==",
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"klona": "^2.0.6" "klona": "^2.0.6"
@@ -956,32 +956,32 @@
} }
}, },
"node_modules/@mantine/hooks": { "node_modules/@mantine/hooks": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-8.3.12.tgz",
"integrity": "sha512-WJFKDnJJM2fAOCkrO8qRsi3wDdicSFqElruPj5TEYWDL93vl0pITOyN0oRF93bUtx190waqela3edfGH3r5/1g==", "integrity": "sha512-lMMDzDewd3lUNtJCAHDj3g8On9X5aBl4q6EBwgOixKQSby9RG9ASEpK8oYHundHTm9tzo3MDeXWV/z32oSQWuw==",
"peerDependencies": { "peerDependencies": {
"react": "^18.x || ^19.x" "react": "^18.x || ^19.x"
} }
}, },
"node_modules/@mantine/notifications": { "node_modules/@mantine/notifications": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-8.3.12.tgz",
"integrity": "sha512-bcFzw9m4uzxHMflNeWqa9imCy8T5mHCAV8WRqzAwK/Yls8GL9uJoQH/e1v6Pl4g4yvBmaF9yUHu7Zq3iyKhHhQ==", "integrity": "sha512-lqPPa11XdcndG8ywMao8yVkMA4jg/pBNbS85bR7OwHQa1yUftmfVlqJl9PZZCiWLX2AgKY3+xM5dHo4LidL+DA==",
"dependencies": { "dependencies": {
"@mantine/store": "8.3.11", "@mantine/store": "8.3.12",
"react-transition-group": "4.4.5" "react-transition-group": "4.4.5"
}, },
"peerDependencies": { "peerDependencies": {
"@mantine/core": "8.3.11", "@mantine/core": "8.3.12",
"@mantine/hooks": "8.3.11", "@mantine/hooks": "8.3.12",
"react": "^18.x || ^19.x", "react": "^18.x || ^19.x",
"react-dom": "^18.x || ^19.x" "react-dom": "^18.x || ^19.x"
} }
}, },
"node_modules/@mantine/store": { "node_modules/@mantine/store": {
"version": "8.3.11", "version": "8.3.12",
"resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.11.tgz", "resolved": "https://registry.npmjs.org/@mantine/store/-/store-8.3.12.tgz",
"integrity": "sha512-ku48CzN9HobVUjd7o41voaaHKz2CMrtkk454vwXWAspfbpqR4hklL5zq/MzctETFCG6HBDiPRfZAvNm8WaM3Tw==", "integrity": "sha512-EC4eIKpm5s7neMbBrWsP6jGKLqrzHf63Ao3penYr7fn25dFXdbXZYw+IG8GYzxOC4yG61b2zTS+bpy5+vwzXpw==",
"peerDependencies": { "peerDependencies": {
"react": "^18.x || ^19.x" "react": "^18.x || ^19.x"
} }

View File

@@ -12,11 +12,11 @@
"dependencies": { "dependencies": {
"@gfazioli/mantine-text-animate": "^2.3.10", "@gfazioli/mantine-text-animate": "^2.3.10",
"@hello-pangea/dnd": "^18.0.1", "@hello-pangea/dnd": "^18.0.1",
"@mantine/core": "^8.3.11", "@mantine/core": "^8.3.12",
"@mantine/dates": "^8.3.11", "@mantine/dates": "^8.3.12",
"@mantine/form": "^8.3.11", "@mantine/form": "^8.3.12",
"@mantine/hooks": "^8.3.11", "@mantine/hooks": "^8.3.12",
"@mantine/notifications": "^8.3.11", "@mantine/notifications": "^8.3.12",
"@tanstack/react-query": "^5.90.16", "@tanstack/react-query": "^5.90.16",
"@tanstack/react-query-devtools": "^5.91.2", "@tanstack/react-query-devtools": "^5.91.2",
"@tanstack/react-query-persist-client": "^5.90.18", "@tanstack/react-query-persist-client": "^5.90.18",

View File

@@ -1,8 +1,9 @@
import { ActionIcon, Box, Group, Text } from '@mantine/core'; import { ActionIcon, Box, Group, Text } from '@mantine/core';
import { createColumnHelper } from '@tanstack/react-table'; import { createColumnHelper } from '@tanstack/react-table';
import { FaFilter, FaTrash } from 'react-icons/fa'; import { FaFilter, FaTrash } from 'react-icons/fa';
import type { SearchHistoryItem } from 'types';
const columnHelper = createColumnHelper<any>(); const columnHelper = createColumnHelper<SearchHistoryItem>();
export const SearchHistoryColumns = ( export const SearchHistoryColumns = (
setSelectedItem: React.Dispatch<React.SetStateAction<any>>, setSelectedItem: React.Dispatch<React.SetStateAction<any>>,
deleteItemOpen: () => void, deleteItemOpen: () => void,
@@ -109,11 +110,11 @@ export const SearchHistoryColumns = (
id: 'dateTimeStamp', id: 'dateTimeStamp',
cell: ({ cell }: any) => { cell: ({ cell }: any) => {
const m = new Date(cell.getValue()); const m = new Date(cell.getValue());
const year = `${m.getUTCFullYear()}`.slice(-2); const year = `${m.getFullYear()}`.slice(-2);
const month = `0${m.getUTCMonth() + 1}`.slice(-2); const month = `0${m.getMonth() + 1}`.slice(-2);
const day = `0${m.getUTCDate()}`.slice(-2); const day = `0${m.getDate()}`.slice(-2);
const hours = `0${m.getUTCHours()}`.slice(-2); const hours = `0${m.getHours()}`.slice(-2);
const minutes = `0${m.getUTCMinutes()}`.slice(-2); const minutes = `0${m.getMinutes()}`.slice(-2);
const dateString = `${month}/${day}/${year} ${hours}:${minutes}`; const dateString = `${month}/${day}/${year} ${hours}:${minutes}`;
return ( return (

View File

@@ -64,6 +64,7 @@ export function DataTable({
setColumnFilters([]); setColumnFilters([]);
} }
}, [filtersEnabled]); }, [filtersEnabled]);
return ( return (
<ScrollArea <ScrollArea
className={scrollAreaClasses.default} className={scrollAreaClasses.default}

View File

@@ -53,7 +53,6 @@ export function Filter({
}); });
}; };
return filterVariant === 'range' ? ( return filterVariant === 'range' ? (
<div>
<DatePickerInput <DatePickerInput
type='range' type='range'
clearable clearable
@@ -61,7 +60,6 @@ export function Filter({
value={columnFilterValue as [Date, Date]} value={columnFilterValue as [Date, Date]}
onChange={(value) => column.setFilterValue(value)} onChange={(value) => column.setFilterValue(value)}
/> />
</div>
) : filterVariant === 'select' ? ( ) : filterVariant === 'select' ? (
<select <select
onChange={(e) => column.setFilterValue(e.target.value)} onChange={(e) => column.setFilterValue(e.target.value)}

View File

@@ -13,10 +13,6 @@ export function DebouncedInput({
} & Omit<TextInputProps, 'onChange'>) { } & Omit<TextInputProps, 'onChange'>) {
const [value, setValue] = useState(initialValue); const [value, setValue] = useState(initialValue);
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
useEffect(() => { useEffect(() => {
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
onChange(value); onChange(value);

View File

@@ -1,7 +1,7 @@
import { Modal, Stack } from '@mantine/core'; import { Modal, Stack } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { modalClasses } from 'styles'; import { modalClasses } from 'styles';
import type { Application, Provider } from 'types'; import type { Application, GuacamoleClient, Provider } from 'types';
import HelpSection from './HelpSection'; import HelpSection from './HelpSection';
export function HelpModal({ export function HelpModal({
@@ -10,12 +10,14 @@ export function HelpModal({
closeProviderModal, closeProviderModal,
applicationData, applicationData,
providerData, providerData,
clientData,
}: { }: {
handleResetSearch: () => void; handleResetSearch: () => void;
providerModalOpened: boolean; providerModalOpened: boolean;
closeProviderModal: () => void; closeProviderModal: () => void;
applicationData: Application[]; applicationData: Application[];
providerData: Provider[]; providerData: Provider[];
clientData: GuacamoleClient[];
}) { }) {
const { isMobile } = useCommonHooks(); const { isMobile } = useCommonHooks();
return ( return (
@@ -61,6 +63,16 @@ export function HelpModal({
}; };
})} })}
/> />
<Stack my={'.5rem'} />
<HelpSection
name={'Available Clients'}
items={clientData.map((client: GuacamoleClient) => {
return {
name: `${client.clientName} (${client.clientIdentifier})`,
command: `>${client.clientName}`,
};
})}
/>
</Stack> </Stack>
</Modal.Body> </Modal.Body>
</Modal.Content> </Modal.Content>

View File

@@ -1,7 +1,5 @@
import { Box, Card, Group, Stack, Text } from '@mantine/core'; import { Box, Card, Group, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks, useSunStateAPI } from 'hooks';
import { useSunStateAPI } from 'hooks/useAPI';
import { useEffect, useState } from 'react';
import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa'; import { FaLongArrowAltDown, FaLongArrowAltUp, FaWind } from 'react-icons/fa';
import { WiBarometer, WiHumidity, WiSunrise, WiSunset } from 'react-icons/wi'; import { WiBarometer, WiHumidity, WiSunrise, WiSunset } from 'react-icons/wi';
import { textClasses } from 'styles'; import { textClasses } from 'styles';
@@ -16,21 +14,15 @@ export default function CurrentWeather({ current }: { current: CW }) {
lon: currentLocation.lon, lon: currentLocation.lon,
}); });
const [sunStates, setSunStates] = useState<SunState[]>([]); const sunStates: SunState[] =
sunStateData?.results.map((item: SunState) => {
useEffect(() => {
if (sunStateData) {
setSunStates(
sunStateData.results.map((item: SunState) => {
return { return {
date: item.date, date: item.date,
sunrise: item.sunrise, sunrise: item.sunrise,
sunset: item.sunset, sunset: item.sunset,
}; };
}), }) || [];
);
}
}, [sunStateData]);
return ( return (
<> <>
{current && ( {current && (

View File

@@ -7,7 +7,7 @@ function WeatherFeaturePill({
}: { }: {
leftSide: React.ReactNode; leftSide: React.ReactNode;
rightSide: React.ReactNode; rightSide: React.ReactNode;
highlight?: string; highlight?: 'left' | 'right';
}) { }) {
return ( return (
<Group <Group

View File

@@ -1,6 +1,5 @@
import { Center, Group, Modal, Stack, Text } from '@mantine/core'; import { Center, Group, Modal, Stack, Text } from '@mantine/core';
import { useCommonHooks, useWeatherAPI } from 'hooks'; import { useCommonHooks, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { modalClasses } from 'styles'; import { modalClasses } from 'styles';
import type { CurrentWeather as CW, ExtendedForecast, HourlyForecast } from 'types'; import type { CurrentWeather as CW, ExtendedForecast, HourlyForecast } from 'types';
@@ -28,13 +27,9 @@ export function WeatherModal({
x: currentLocation.x, x: currentLocation.x,
y: currentLocation.y, y: currentLocation.y,
}); });
const [hourlyForecast, setHourlyForecast] = useState<HourlyForecast[]>([]); const hourlyForecast: HourlyForecast[] =
hourly?.properties?.periods?.slice(1, 25) || [];
useEffect(() => {
if (hourly) {
setHourlyForecast(hourly.properties.periods.slice(1, 25));
}
}, [hourly]);
return ( return (
<Modal.Root <Modal.Root
fullScreen={isMobile} fullScreen={isMobile}

View File

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

View File

@@ -76,7 +76,7 @@ export function Homepage() {
}); });
const personal: PersonalData = personalData?.[0]; const personal: PersonalData = personalData?.[0];
const message: Message = messageData?.[0]; const message: Message = messageData?.[0] || {};
const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>(''); const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>('');
const [current, setCurrent] = useState<CW>({ const [current, setCurrent] = useState<CW>({
icon: '', icon: '',
@@ -122,7 +122,7 @@ export function Homepage() {
useEffect(() => { useEffect(() => {
if (personal) { if (personal) {
setDisplayedGreeting(() => { setDisplayedGreeting(() => {
if (Math.floor(Math.random() * 100) > 10 || message === undefined) { if (Math.floor(Math.random() * 100) > 10 || message.message === undefined) {
switch (Math.floor(new Date().getHours() / 6)) { switch (Math.floor(new Date().getHours() / 6)) {
case 0: case 0:
return `Good night, ${personal?.firstName}!`; return `Good night, ${personal?.firstName}!`;

View File

@@ -632,6 +632,7 @@ export default function Search({
handleResetSearch={handleResetSearch} handleResetSearch={handleResetSearch}
applicationData={applications} applicationData={applications}
providerData={providers} providerData={providers}
clientData={guacamoleClients}
providerModalOpened={providerModalOpened} providerModalOpened={providerModalOpened}
closeProviderModal={closeProviderModal} closeProviderModal={closeProviderModal}
/> />

View File

@@ -15,11 +15,11 @@ function SearchSelectItem({
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const [showFillButton, setShowFillButton] = useState(false); const [showFillButton, setShowFillButton] = useState(false);
const m = new Date(item.option.dateTimeStamp); const m = new Date(item.option.dateTimeStamp);
const year = `${m.getUTCFullYear()}`.slice(-2); const year = `${m.getFullYear()}`.slice(-2);
const month = `0${m.getUTCMonth() + 1}`.slice(-2); const month = `0${m.getMonth() + 1}`.slice(-2);
const day = `0${m.getUTCDate()}`.slice(-2); const day = `0${m.getDate()}`.slice(-2);
const hours = `0${m.getUTCHours()}`.slice(-2); const hours = `0${m.getHours()}`.slice(-2);
const minutes = `0${m.getUTCMinutes()}`.slice(-2); const minutes = `0${m.getMinutes()}`.slice(-2);
const dateString = isMobile const dateString = isMobile
? `${month}/${day}/${year}` ? `${month}/${day}/${year}`
: `${month}/${day}/${year} ${hours}:${minutes}`; : `${month}/${day}/${year} ${hours}:${minutes}`;

View File

@@ -1,6 +1,6 @@
import { Box, Button, Group, Menu, Overlay, Stack } from '@mantine/core'; import { Box, Button, Group, Menu, Overlay, Stack } from '@mantine/core';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useState } from 'react';
import { FiMenu, FiSettings } from 'react-icons/fi'; import { FiMenu, FiSettings } from 'react-icons/fi';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { buttonClasses, menuClasses } from 'styles'; import { buttonClasses, menuClasses } from 'styles';
@@ -10,21 +10,29 @@ import ThemingCard from './ThemingCard';
import WakeOnLan from './WakeOnLanCard'; import WakeOnLan from './WakeOnLanCard';
function SettingsButton() { function SettingsButton() {
const { data: appCategoryData, isFetched: appCategoryDataIsFetched } = useQueryAPI({ const { data: appCategoryData } = useQueryAPI({
endpoint: 'api/appCategories', endpoint: 'api/appCategories',
}); });
const { data: wolClientData } = useQueryAPI({ const { data: wolClientData } = useQueryAPI({
endpoint: 'api/wolClients', endpoint: 'api/wolClients',
}); });
const { data: wolEnabledData, isFetched: wolEnabledDataIsFetched } = useQueryAPI({ const { data: wolEnabledData } = useQueryAPI({
endpoint: 'api/wolEnabled', endpoint: 'api/wolEnabled',
}); });
const wolClients: WolClient[] = wolClientData || []; const wolClients: WolClient[] = wolClientData || [];
const [enabled, setEnabled] = useState<number>(0); const enabled: number = wolEnabledData?.[0].enableWol || 0;
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const [categories, setCategories] = useState([{ value: 'All', label: 'All' }]); const categories = appCategoryData
? [
{ value: 'All', label: 'All' },
...appCategoryData.map((category: AppCategory) => {
return { value: category.filterName, label: category.filterName };
}),
]
: [{ value: 'All', label: 'All' }];
const [selectedClient, setSelectedClient] = useState<WolClient>(wolClientData?.[0]); const [selectedClient, setSelectedClient] = useState<WolClient>(wolClientData?.[0]);
const navigate = useNavigate(); const navigate = useNavigate();
const { const {
theme, theme,
isMobile, isMobile,
@@ -54,21 +62,6 @@ function SettingsButton() {
}); });
}; };
useEffect(() => {
if (appCategoryData) {
const list: [any] = appCategoryData.map((category: AppCategory) => {
return { value: category.filterName, label: category.filterName };
});
list.unshift({ value: 'All', label: 'All' });
setCategories(list);
}
}, [appCategoryData, appCategoryDataIsFetched]);
useEffect(() => {
if (wolEnabledData && wolEnabledDataIsFetched) {
setEnabled(wolEnabledData[0].enableWol);
}
}, [wolEnabledData, wolEnabledDataIsFetched]);
return ( return (
<Menu <Menu
classNames={menuClasses} classNames={menuClasses}

View File

@@ -22,7 +22,7 @@ import { useEffect, useState } from 'react';
import * as fa6 from 'react-icons/fa6'; import * as fa6 from 'react-icons/fa6';
import * as si from 'react-icons/si'; import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc'; import * as vsc from 'react-icons/vsc';
import { popoverClasses, selectClasses, textInputClasses } from 'styles'; import { buttonClasses, popoverClasses, selectClasses, textInputClasses } from 'styles';
import type { Application } from 'types'; import type { Application } from 'types';
import DebouncedApplication from './DebouncedApplication'; import DebouncedApplication from './DebouncedApplication';
@@ -281,12 +281,16 @@ const ApplicationSettings = () => {
/> />
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -14,7 +14,7 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components'; import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textInputClasses } from 'styles'; import { buttonClasses, textInputClasses } from 'styles';
import type { Bookmark, BookmarkGroup } from 'types'; import type { Bookmark, BookmarkGroup } from 'types';
import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem'; import DebouncedGroupSettingsItem from './DebouncedGroupSettingsItem';
@@ -175,12 +175,16 @@ const BookmarkSettings = () => {
/> />
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -420,12 +420,16 @@ function DebouncedGroupSettingsItem({
align='center' align='center'
justify='center' justify='center'
> >
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={() => { onClick={() => {
setPopoverOpened(false); setPopoverOpened(false);
form.reset(); form.reset();

View File

@@ -15,10 +15,10 @@ import { isNotEmpty, useForm } from '@mantine/form';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks'; import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components'; import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { FaTerminal } from 'react-icons/fa'; import { FaTerminal } from 'react-icons/fa';
import { FaComputer, FaDesktop } from 'react-icons/fa6'; import { FaComputer, FaDesktop } from 'react-icons/fa6';
import { popoverClasses, selectClasses, textInputClasses } from 'styles'; import { buttonClasses, popoverClasses, selectClasses, textInputClasses } from 'styles';
import type { GuacamoleClient } from 'types'; import type { GuacamoleClient } from 'types';
import DebouncedGuacamoleClient from './DebouncedGuacamoleClient'; import DebouncedGuacamoleClient from './DebouncedGuacamoleClient';
import GuacamoleClientSettingsItem from './GuacamoleClientSettingsItem'; import GuacamoleClientSettingsItem from './GuacamoleClientSettingsItem';
@@ -65,6 +65,12 @@ const GuacamoleClients = () => {
form.reset(); form.reset();
}; };
useEffect(() => {
if (debouncedSearch === searchValue) {
setIsLoading(false);
}
}, [debouncedSearch]);
return ( return (
<Stack> <Stack>
<AddItemModal <AddItemModal
@@ -169,12 +175,16 @@ const GuacamoleClients = () => {
/> />
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -12,7 +12,7 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components'; import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { textInputClasses } from 'styles'; import { buttonClasses, textInputClasses } from 'styles';
import type { Message } from 'types'; import type { Message } from 'types';
import DebouncedMessage from './DebouncedMessage'; import DebouncedMessage from './DebouncedMessage';
@@ -53,6 +53,12 @@ const MessageSettings = () => {
} }
}, [debounced]); }, [debounced]);
useEffect(() => {
if (debouncedSearch === searchValue) {
setIsLoading(false);
}
}, [debouncedSearch]);
return ( return (
<Stack w={'100%'}> <Stack w={'100%'}>
<SettingsHeader <SettingsHeader
@@ -112,12 +118,16 @@ const MessageSettings = () => {
} }
/> />
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -17,25 +17,15 @@ import type { PersonalData } from 'types';
const PersonalizationSettings = () => { const PersonalizationSettings = () => {
const endpoint = 'api/personalData'; const endpoint = 'api/personalData';
const { const { data: personalData, isFetching: personalDataIsFetching } = useQueryAPI({
data: personalData,
isFetched: personalDataIsFetched,
isFetching: personalDataIsFetching,
} = useQueryAPI({
endpoint: endpoint, endpoint: endpoint,
}); });
const [personal, setPersonal] = useState<PersonalData>(); const [personal, setPersonal] = useState<PersonalData>(personalData?.[0] || {});
const [update, setUpdate] = useState<any>({}); const [update, setUpdate] = useState<any>({});
const [debounced] = useDebouncedValue(update, 1000); const [debounced] = useDebouncedValue(update, 1000);
const { mutateAsync } = useMutationAPI(); const { mutateAsync } = useMutationAPI();
const { isMobile, theme } = useCommonHooks(); const { isMobile, theme } = useCommonHooks();
useEffect(() => {
if (personalData && personalDataIsFetched) {
setPersonal(personalData[0]);
}
}, [personalData, personalDataIsFetched]);
useEffect(() => { useEffect(() => {
if (Object.keys(debounced).length) { if (Object.keys(debounced).length) {
setPersonal(update); setPersonal(update);

View File

@@ -77,6 +77,7 @@ function DebouncedSearchProvider({
position: 'absolute', position: 'absolute',
right: '1rem', right: '1rem',
top: '.5rem', top: '.5rem',
zIndex: 1,
}} }}
> >
<ManagementPill <ManagementPill

View File

@@ -24,6 +24,7 @@ import { FiPower } from 'react-icons/fi';
import * as si from 'react-icons/si'; import * as si from 'react-icons/si';
import * as vsc from 'react-icons/vsc'; import * as vsc from 'react-icons/vsc';
import { import {
buttonClasses,
popoverClasses, popoverClasses,
selectClasses, selectClasses,
switchClasses, switchClasses,
@@ -236,12 +237,16 @@ const SearchProviderSettings = () => {
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -1,5 +1,6 @@
import { Button, Card, FileInput, Group, Image, Stack, TextInput } from '@mantine/core'; import { Button, Card, FileInput, Group, Image, Stack, TextInput } from '@mantine/core';
import { useFileUploadAPI } from 'hooks/useAPI'; import { useFileUploadAPI } from 'hooks/useAPI';
import { buttonClasses } from 'styles';
export function AddBackground({ export function AddBackground({
fileName, fileName,
@@ -131,7 +132,8 @@ export function AddBackground({
Upload Upload
</Button> </Button>
<Button <Button
color='var(--mantine-color-red-6)' className={buttonClasses.cancelButton}
classNames={buttonClasses}
onClick={() => { onClick={() => {
handleAddBackgroundModalClose(); handleAddBackgroundModalClose();
}} }}

View File

@@ -1,5 +1,6 @@
import { Button, Card, Group, Stack, Text } from '@mantine/core'; import { Button, Card, Group, Stack, Text } from '@mantine/core';
import { useMutationAPI } from 'hooks'; import { useMutationAPI } from 'hooks';
import { buttonClasses } from 'styles';
export function DeleteBackground({ export function DeleteBackground({
fileName, fileName,
@@ -63,7 +64,12 @@ export function DeleteBackground({
> >
DELETE DELETE
</Button> </Button>
<Button mt={'auto'} onClick={handleDeleteBackgroundModalClose}> <Button
className={buttonClasses.cancelButton}
classNames={buttonClasses}
mt={'auto'}
onClick={handleDeleteBackgroundModalClose}
>
Cancel Cancel
</Button> </Button>
</Group> </Group>

View File

@@ -1,7 +1,7 @@
import { Button, Group, Image, Stack, Text, TextInput } from '@mantine/core'; import { Button, Group, Image, Stack, Text, TextInput } from '@mantine/core';
import { useCommonHooks, useMutationAPI } from 'hooks'; import { useCommonHooks, useMutationAPI } from 'hooks';
import { useState } from 'react'; import { useState } from 'react';
import { textInputClasses } from 'styles'; import { buttonClasses, textInputClasses } from 'styles';
export function EditBackground({ export function EditBackground({
fileName, fileName,
@@ -54,7 +54,8 @@ export function EditBackground({
<Group> <Group>
<Button <Button
mt={'auto'} mt={'auto'}
color='green' className={buttonClasses.saveButton}
classNames={buttonClasses}
onClick={() => { onClick={() => {
if ( if (
!rename.endsWith('.png') && !rename.endsWith('.png') &&
@@ -88,8 +89,9 @@ export function EditBackground({
Save Save
</Button> </Button>
<Button <Button
className={buttonClasses.cancelButton}
classNames={buttonClasses}
mt={'auto'} mt={'auto'}
color={'var(--mantine-color-red-6)'}
onClick={handleEditBackgroundModalClose} onClick={handleEditBackgroundModalClose}
> >
Cancel Cancel

View File

@@ -2,7 +2,7 @@ import { Button, Group, Modal, Text, TextInput } from '@mantine/core';
import { useCommonHooks } from 'hooks'; import { useCommonHooks } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FaTag } from 'react-icons/fa'; import { FaTag } from 'react-icons/fa';
import { modalClasses, textInputClasses } from 'styles'; import { buttonClasses, modalClasses, textInputClasses } from 'styles';
import type { Theme, ThemePlaylist } from 'types'; import type { Theme, ThemePlaylist } from 'types';
import { ThemePreviewMiddleware } from '../../Themes'; import { ThemePreviewMiddleware } from '../../Themes';
import { TimeOfDay } from './TimeOfDay'; import { TimeOfDay } from './TimeOfDay';
@@ -115,12 +115,17 @@ export function ThemePlaylistModal({
})} })}
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button color={'green'} onClick={handleSavePlaylist}> <Button
color={'green'}
className={buttonClasses.saveButton}
classNames={buttonClasses}
onClick={handleSavePlaylist}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={() => { onClick={() => {
handleClose(); handleClose();
setThemeList('[]'); setThemeList('[]');

View File

@@ -19,6 +19,7 @@ import { useRef, useState } from 'react';
import { FaShuffle } from 'react-icons/fa6'; import { FaShuffle } from 'react-icons/fa6';
import { FiImage, FiSave, FiShuffle, FiX } from 'react-icons/fi'; import { FiImage, FiSave, FiShuffle, FiX } from 'react-icons/fi';
import { import {
buttonClasses,
colorSwatchClasses, colorSwatchClasses,
scrollAreaClasses, scrollAreaClasses,
textClasses, textClasses,
@@ -657,14 +658,15 @@ export function ThemeEditor({
<Group> <Group>
<Button <Button
disabled={JSON.stringify(defaultTheme) === JSON.stringify(selectedTheme)} disabled={JSON.stringify(defaultTheme) === JSON.stringify(selectedTheme)}
color={'green'} className={buttonClasses.saveButton}
classNames={buttonClasses}
onClick={handleSaveTheme} onClick={handleSaveTheme}
> >
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={() => { onClick={() => {
handleClose(); handleClose();
closeBackgroundPicker(); closeBackgroundPicker();

View File

@@ -33,11 +33,7 @@ export function Themes() {
const allThemes: Theme[] = allThemeData?.filter((a: Theme) => const allThemes: Theme[] = allThemeData?.filter((a: Theme) =>
a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()), a.name.toLowerCase().includes((debouncedSearch as string).toLowerCase()),
); );
const { const { data: backgroundData, isFetching: backgroundDataIsFetching } = useQueryAPI({
data: backgroundData,
isFetched: backgroundDataIsFetched,
isFetching: backgroundDataIsFetching,
} = useQueryAPI({
endpoint: backgroundsEndpoint, endpoint: backgroundsEndpoint,
}); });
const { data: themePlaylistData } = useQueryAPI({ const { data: themePlaylistData } = useQueryAPI({
@@ -50,7 +46,9 @@ export function Themes() {
backgroundModalOpened, backgroundModalOpened,
{ open: openBackgroundModal, close: closeBackgroundModal }, { open: openBackgroundModal, close: closeBackgroundModal },
] = useDisclosure(false); ] = useDisclosure(false);
const [backgroundList, setBackgroundList] = useState<string[]>(['None']); const backgroundList = backgroundData
? ['None', ...backgroundData.sort((a: string, b: string) => a.localeCompare(b))]
: ['None'];
const inUseBackgrounds: string[] = [ const inUseBackgrounds: string[] = [
...new Set( ...new Set(
allThemes allThemes
@@ -76,6 +74,7 @@ export function Themes() {
textShadowsEnabled: 0, textShadowsEnabled: 0,
appShadowsEnabled: 0, appShadowsEnabled: 0,
}); });
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isNew, setIsNew] = useState(false); const [isNew, setIsNew] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
@@ -93,15 +92,6 @@ export function Themes() {
openThemeModal(); openThemeModal();
}; };
useEffect(() => {
if (backgroundData && backgroundDataIsFetched) {
setBackgroundList([
'None',
...backgroundData.sort((a: string, b: string) => a.localeCompare(b)),
]);
}
}, [backgroundData, backgroundDataIsFetched]);
useEffect(() => { useEffect(() => {
if (debouncedSearch === searchValue) { if (debouncedSearch === searchValue) {
setIsLoading(false); setIsLoading(false);

View File

@@ -13,7 +13,7 @@ import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { AddItemModal, NoItemsFound, SettingsHeader } from 'components'; import { AddItemModal, NoItemsFound, SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { popoverClasses, textInputClasses } from 'styles'; import { buttonClasses, popoverClasses, textInputClasses } from 'styles';
import type { WolClient } from 'types'; import type { WolClient } from 'types';
import DebouncedWolClient from './DebouncedWolClient'; import DebouncedWolClient from './DebouncedWolClient';
import WolClientSettingsItem from './WolClientSettingsItem'; import WolClientSettingsItem from './WolClientSettingsItem';
@@ -116,12 +116,16 @@ const WolClientSettings = () => {
/> />
</Group> </Group>
<Group mt={'1rem'}> <Group mt={'1rem'}>
<Button type='submit' color={'green'}> <Button
type='submit'
className={buttonClasses.saveButton}
classNames={buttonClasses}
>
Save Save
</Button> </Button>
<Button <Button
color={'red'} className={buttonClasses.cancelButton}
styles={{ label: { color: 'black' } }} classNames={buttonClasses}
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel

View File

@@ -74,3 +74,27 @@
color: var(--mantine-color-background-0) !important; color: var(--mantine-color-background-0) !important;
} }
} }
.saveButton {
&.root {
background-color: var(--mantine-color-green-filled);
.label {
color: black;
}
}
&:hover {
background-color: var(--mantine-color-green-filled-hover) !important;
}
}
.cancelButton {
&.root {
background-color: var(--mantine-color-red-filled);
.label {
color: black;
}
}
&:hover {
background-color: var(--mantine-color-red-filled-hover) !important;
}
}