diff --git a/package-lock.json b/package-lock.json index dfece51..d596824 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "triggerfrontend", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "triggerfrontend", - "version": "1.1.0", + "version": "1.2.0", "dependencies": { - "@gfazioli/mantine-text-animate": "^2.3.13", + "@gfazioli/mantine-text-animate": "^2.4.0", "@hello-pangea/dnd": "^18.0.1", "@mantine/core": "^8.3.14", "@mantine/dates": "^8.3.14", @@ -29,16 +29,16 @@ "recharts": "^3.7.0" }, "devDependencies": { - "@types/node": "^25.2.1", + "@types/node": "^25.2.3", "@types/react": "^19.2.13", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.3", + "@vitejs/plugin-react": "^5.1.4", "@vitest/coverage-v8": "^4.0.18", "@vitest/ui": "^4.0.18", "typescript": "^5.9.3", "vite": "^7.3.1", "vite-plugin-dts": "~4.5.4", - "vite-tsconfig-paths": "^6.0.5", + "vite-tsconfig-paths": "^6.1.0", "vitest": "^4.0.18" } }, @@ -820,9 +820,9 @@ "license": "MIT" }, "node_modules/@gfazioli/mantine-text-animate": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.3.13.tgz", - "integrity": "sha512-EUx9aYfRiy021sYNjbVt3D+fl1S3S74g7h68yOsm2XEhcbMrjiyahoJkoxsySGIQRRnUbnIkG+ncvi68v9eApg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@gfazioli/mantine-text-animate/-/mantine-text-animate-2.4.0.tgz", + "integrity": "sha512-m6zZf6MB8Xm40fEGjSAmcs4gYx0ovWzgIQKhTlsaPDF0w1oD4n1Lj1SX4+vdefSGrGef7Xaazt7mrW2HE2ACdA==", "peerDependencies": { "@mantine/core": ">=7.0.0", "@mantine/hooks": ">=7.0.0", @@ -1152,9 +1152,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz", - "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==", + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", "dev": true }, "node_modules/@rollup/pluginutils": { @@ -1837,9 +1837,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz", - "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "dependencies": { "undici-types": "~7.16.0" @@ -1871,15 +1871,15 @@ "license": "MIT" }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.3.tgz", - "integrity": "sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", "dev": true, "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.2", + "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, @@ -4189,9 +4189,9 @@ } }, "node_modules/vite-tsconfig-paths": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.0.5.tgz", - "integrity": "sha512-f/WvY6ekHykUF1rWJUAbCU7iS/5QYDIugwpqJA+ttwKbxSbzNlqlE8vZSrsnxNQciUW+z6lvhlXMaEyZn9MSig==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.0.tgz", + "integrity": "sha512-kpd3sY9glHIDaq4V/Tlc1Y8WaKtutoc3B525GHxEVKWX42FKfQsXvjFOemu1I8VIN8pNbrMLWVTbW79JaRUxKg==", "dev": true, "dependencies": { "debug": "^4.1.1", diff --git a/package.json b/package.json index 4768810..4d83448 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "preview": "vite preview" }, "dependencies": { - "@gfazioli/mantine-text-animate": "^2.3.13", + "@gfazioli/mantine-text-animate": "^2.4.0", "@hello-pangea/dnd": "^18.0.1", "@mantine/core": "^8.3.14", "@mantine/dates": "^8.3.14", @@ -31,16 +31,16 @@ "recharts": "^3.7.0" }, "devDependencies": { - "@types/node": "^25.2.1", + "@types/node": "^25.2.3", "@types/react": "^19.2.13", "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.3", + "@vitejs/plugin-react": "^5.1.4", "@vitest/coverage-v8": "^4.0.18", "@vitest/ui": "^4.0.18", "typescript": "^5.9.3", "vite": "^7.3.1", "vite-plugin-dts": "~4.5.4", - "vite-tsconfig-paths": "^6.0.5", + "vite-tsconfig-paths": "^6.1.0", "vitest": "^4.0.18" } } diff --git a/src/components/HelpModal/HelpModal.tsx b/src/components/HelpModal/HelpModal.tsx index 0daebbf..02c6150 100644 --- a/src/components/HelpModal/HelpModal.tsx +++ b/src/components/HelpModal/HelpModal.tsx @@ -43,9 +43,24 @@ export function HelpModal({ + + Expand each of the sections below to find available Search Bar commands. + { return { name: provider.name, @@ -56,6 +71,7 @@ export function HelpModal({ { return { name: application.url, @@ -66,6 +82,7 @@ export function HelpModal({ '} items={clientData.map((client: GuacamoleClient) => { return { name: `${client.clientName} (${client.clientIdentifier})`, diff --git a/src/components/HelpModal/HelpSection.tsx b/src/components/HelpModal/HelpSection.tsx index 363c1c2..1e31f08 100644 --- a/src/components/HelpModal/HelpSection.tsx +++ b/src/components/HelpModal/HelpSection.tsx @@ -1,13 +1,24 @@ -import { Group, Text } from '@mantine/core'; +import { ActionIcon, Group, Text } from '@mantine/core'; import { useCommonHooks } from 'hooks'; +import { useState } from 'react'; +import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; import { textClasses } from 'styles'; import type { SearchShortcut } from 'types'; import HintItem from './HintItem'; -function HelpSection({ name, items }: { name: string; items: SearchShortcut[] }) { +function HelpSection({ + name, + prefix, + items, +}: { + name: string; + prefix: string; + items: SearchShortcut[]; +}) { const { theme } = useCommonHooks(); + const [expand, setExpand] = useState(false); return ( - + - {name} + {name}{' '} + setExpand(!expand)}> + {expand ? : } + - {items - ?.toSorted((a: SearchShortcut, b: SearchShortcut) => - a.command > b.command ? 1 : -1, - ) - .map((item: SearchShortcut) => ( - - ))} + {expand ? ( + items + ?.toSorted((a: SearchShortcut, b: SearchShortcut) => + a.command > b.command ? 1 : -1, + ) + .map((item: SearchShortcut) => ( + + )) + ) : ( + + + Command prefix: {prefix} + + + )} ); diff --git a/src/hooks/useCommonHooks.ts b/src/hooks/useCommonHooks.ts index 7d32e9a..b3d1b45 100644 --- a/src/hooks/useCommonHooks.ts +++ b/src/hooks/useCommonHooks.ts @@ -69,6 +69,10 @@ export function useCommonHooks() { key: 'providerIcon', defaultValue: 'FaGoogle', }); + const [timeFormat, setTimeFormat] = useLocalStorage({ + key: 'timeFormat', + defaultValue: '12h', + }); const [searchValue, setSearchValue] = useState(''); const [providerLabel, setProviderLabel] = useState(defaultProviderLabel); const [providerURL, setProviderURL] = useState(defaultProviderURL); @@ -268,6 +272,8 @@ export function useCommonHooks() { setDefaultProviderPrefix, defaultProviderIcon, setDefaultProviderIcon, + timeFormat, + setTimeFormat, currentUser, setCurrentUser, currentLocation, diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx index 1989638..1361006 100644 --- a/src/pages/homepage/Homepage.tsx +++ b/src/pages/homepage/Homepage.tsx @@ -92,6 +92,8 @@ export function Homepage() { windDirection: 0, pressure: 0, }); + const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] = + useDisclosure(false); const extendedForecast = forecast?.properties?.periods.map( (item: ExtendedForecast) => ({ detailedForecast: item.detailedForecast, @@ -209,6 +211,9 @@ export function Homepage() { openWeatherModal={openWeatherModal} position='top' personal={personal} + providerModalOpened={providerModalOpened} + openProviderModal={openProviderModal} + closeProviderModal={closeProviderModal} />
)} - + ); } diff --git a/src/pages/homepage/components/Header/Header.tsx b/src/pages/homepage/components/Header/Header.tsx index 51b205a..9e2b6f3 100644 --- a/src/pages/homepage/components/Header/Header.tsx +++ b/src/pages/homepage/components/Header/Header.tsx @@ -23,6 +23,8 @@ function Header({ theme, textShadowStyle, largeTextShadowStyle, + timeFormat, + setTimeFormat, } = useCommonHooks(); useEffect(() => { @@ -119,14 +121,19 @@ function Header({ style={{ textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined, }} + onClick={() => + timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h') + } > - {`${ - time.getHours() === 0 - ? 12 - : time.getHours() > 12 - ? time.getHours() - 12 - : time.getHours() - }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`} + {timeFormat === '12h' + ? `${ + time.getHours() === 0 + ? 12 + : time.getHours() > 12 + ? time.getHours() - 12 + : time.getHours() + }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}` + : `${fixDigits(time.getHours())}:${fixDigits(time.getMinutes())}`} diff --git a/src/pages/homepage/components/Header/LargeHeader.tsx b/src/pages/homepage/components/Header/LargeHeader.tsx index 1738282..1c2a760 100644 --- a/src/pages/homepage/components/Header/LargeHeader.tsx +++ b/src/pages/homepage/components/Header/LargeHeader.tsx @@ -12,6 +12,8 @@ function LargeHeader({ greet, date }: { greet: string | undefined; date: string theme, textShadowStyle, largeTextShadowStyle, + timeFormat, + setTimeFormat, } = useCommonHooks(); useEffect(() => { @@ -58,15 +60,21 @@ function LargeHeader({ greet, date }: { greet: string | undefined; date: string classNames={textClasses} style={{ textShadow: theme.textShadowsEnabled ? textShadowStyle : undefined, + zIndex: 1, }} + onClick={() => + timeFormat === '12h' ? setTimeFormat('24h') : setTimeFormat('12h') + } > - {`${ - time.getHours() === 0 - ? 12 - : time.getHours() > 12 - ? time.getHours() - 12 - : time.getHours() - }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`} + {timeFormat === '12h' + ? `${ + time.getHours() === 0 + ? 12 + : time.getHours() > 12 + ? time.getHours() - 12 + : time.getHours() + }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}` + : `${fixDigits(time.getHours())}:${fixDigits(time.getMinutes())}`} ); diff --git a/src/pages/homepage/components/Search/Search.tsx b/src/pages/homepage/components/Search/Search.tsx index 80fa286..a168345 100644 --- a/src/pages/homepage/components/Search/Search.tsx +++ b/src/pages/homepage/components/Search/Search.tsx @@ -38,6 +38,9 @@ export default function Search({ openWeatherModal, position, personal, + providerModalOpened, + openProviderModal, + closeProviderModal, }: { current: CurrentWeather; extendedForecast: ExtendedForecast[]; @@ -46,6 +49,9 @@ export default function Search({ openWeatherModal: () => void; position: string; personal: PersonalData; + providerModalOpened: boolean; + openProviderModal: () => void; + closeProviderModal: () => void; }) { const { isMobile, @@ -138,8 +144,6 @@ export default function Search({ const [historyMultiplier, setHistoryMultiplier] = useState(1); const [dropdownOpened, { open: openDropdown, close: closeDropdown }] = useDisclosure(); - const [providerModalOpened, { open: openProviderModal, close: closeProviderModal }] = - useDisclosure(false); const handleResetSearch = () => { setSearchValue(''); diff --git a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx index c5687c0..c8224b4 100644 --- a/src/pages/homepage/components/SettingsButton/SettingsButton.tsx +++ b/src/pages/homepage/components/SettingsButton/SettingsButton.tsx @@ -1,7 +1,7 @@ import { Box, Button, Group, Menu, Overlay, Stack } from '@mantine/core'; import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks'; import { useState } from 'react'; -import { FiMenu, FiSettings } from 'react-icons/fi'; +import { FiHelpCircle, FiMenu, FiSettings } from 'react-icons/fi'; import { useNavigate } from 'react-router-dom'; import { buttonClasses, menuClasses } from 'styles'; import type { AppCategory, WolClient } from 'types'; @@ -9,7 +9,7 @@ import SettingsCard from './SettingsCard'; import ThemingCard from './ThemingCard'; import WakeOnLan from './WakeOnLanCard'; -function SettingsButton() { +function SettingsButton({ openProviderModal }: { openProviderModal: () => void }) { const { data: appCategoryData } = useQueryAPI({ endpoint: 'api/appCategories', }); @@ -32,7 +32,7 @@ function SettingsButton() { : [{ value: 'All', label: 'All' }]; const [selectedClient, setSelectedClient] = useState(wolClientData?.[0]); const navigate = useNavigate(); - + const [isOpen, setIsOpen] = useState(false); const { theme, isMobile, @@ -72,10 +72,16 @@ function SettingsButton() { withOverlay overlayProps={{ blur: 5, + onClick: () => setIsOpen(false), }} + opened={isOpen} > - @@ -149,6 +155,17 @@ function SettingsButton() { > Settings +