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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
import { Modal, Stack } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { modalClasses } from 'styles';
import type { Application, Provider } from 'types';
import type { Application, GuacamoleClient, Provider } from 'types';
import HelpSection from './HelpSection';
export function HelpModal({
@@ -10,12 +10,14 @@ export function HelpModal({
closeProviderModal,
applicationData,
providerData,
clientData,
}: {
handleResetSearch: () => void;
providerModalOpened: boolean;
closeProviderModal: () => void;
applicationData: Application[];
providerData: Provider[];
clientData: GuacamoleClient[];
}) {
const { isMobile } = useCommonHooks();
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>
</Modal.Body>
</Modal.Content>

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
import { Center, Group, Modal, Stack, Text } from '@mantine/core';
import { useCommonHooks, useWeatherAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { modalClasses } from 'styles';
import type { CurrentWeather as CW, ExtendedForecast, HourlyForecast } from 'types';
@@ -28,13 +27,9 @@ export function WeatherModal({
x: currentLocation.x,
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 (
<Modal.Root
fullScreen={isMobile}

View File

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

View File

@@ -76,7 +76,7 @@ export function Homepage() {
});
const personal: PersonalData = personalData?.[0];
const message: Message = messageData?.[0];
const message: Message = messageData?.[0] || {};
const [displayedGreeting, setDisplayedGreeting] = useState<string | undefined>('');
const [current, setCurrent] = useState<CW>({
icon: '',
@@ -122,7 +122,7 @@ export function Homepage() {
useEffect(() => {
if (personal) {
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)) {
case 0:
return `Good night, ${personal?.firstName}!`;

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { Box, Button, Group, Menu, Overlay, Stack } from '@mantine/core';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { FiMenu, FiSettings } from 'react-icons/fi';
import { useNavigate } from 'react-router-dom';
import { buttonClasses, menuClasses } from 'styles';
@@ -10,21 +10,29 @@ import ThemingCard from './ThemingCard';
import WakeOnLan from './WakeOnLanCard';
function SettingsButton() {
const { data: appCategoryData, isFetched: appCategoryDataIsFetched } = useQueryAPI({
const { data: appCategoryData } = useQueryAPI({
endpoint: 'api/appCategories',
});
const { data: wolClientData } = useQueryAPI({
endpoint: 'api/wolClients',
});
const { data: wolEnabledData, isFetched: wolEnabledDataIsFetched } = useQueryAPI({
const { data: wolEnabledData } = useQueryAPI({
endpoint: 'api/wolEnabled',
});
const wolClients: WolClient[] = wolClientData || [];
const [enabled, setEnabled] = useState<number>(0);
const enabled: number = wolEnabledData?.[0].enableWol || 0;
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 navigate = useNavigate();
const {
theme,
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 (
<Menu
classNames={menuClasses}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -74,3 +74,27 @@
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;
}
}