Updated WoL Settings

This commit is contained in:
Sean Edmunds
2025-03-21 10:33:50 -05:00
parent 27a9db82f0
commit 27a7dd2fb9
12 changed files with 637 additions and 135 deletions

View File

@@ -0,0 +1,74 @@
import { Icon } from '@iconify/react';
import { Box, Stack, Text } from '@mantine/core';
import { useCommonHooks } from 'hooks';
import { useState } from 'react';
import { boxClasses, textClasses } from 'styles';
function AppSettingsItem({ url, icon, name }: any) {
const [textDecoration, setTextDecoration] = useState<string>('none');
const [hover, setHover] = useState<boolean>(false);
const { isMobile } = useCommonHooks();
return (
<Box
onMouseOver={() => {
setTextDecoration('underline');
setHover(true);
}}
onMouseOut={() => {
setTextDecoration('none');
setHover(false);
}}
className={boxClasses.appItemBox}
>
{hover && (
<svg
style={{ position: 'absolute', borderRadius: '6px' }}
height='100%'
width='100%'
xmlns='http://www.w3.org/2000/svg'
>
<rect rx='16' ry='16' height='100%' width='100%' strokeLinejoin='round' />
</svg>
)}
<Stack
align={isMobile ? 'center' : undefined}
justify={isMobile ? 'center' : undefined}
style={{
height: '128px',
width: isMobile ? '100%' : undefined,
marginRight: isMobile ? '0' : '1em',
marginLeft: isMobile ? '0' : '2em',
paddingTop: isMobile ? '0' : '32px',
}}
>
<Icon icon={`mdi:${icon}`} height={64} width={64} />
</Stack>
<Stack
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
flex: 1,
overflow: 'hidden',
gap: 0,
}}
>
<Text fz={22} className={textClasses.appName} classNames={textClasses}>
{name}
</Text>
<Text
fz={16}
className={textClasses.appUrl}
classNames={textClasses}
style={{
textDecoration: textDecoration,
}}
>
{url}
</Text>
</Stack>
</Box>
);
}
export default AppSettingsItem;

View File

@@ -15,7 +15,6 @@ import {
} from '@mantine/core';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import AppItem from 'pages/homepage/components/Applications/AppItem';
import { useEffect, useState } from 'react';
import { FiHelpCircle, FiPlus } from 'react-icons/fi';
import {
@@ -25,6 +24,7 @@ import {
textClasses,
textInputClasses,
} from 'styles';
import AppSettingsItem from './AppSettingsItem';
import DebouncedApplication from './DebouncedApplication';
const ApplicationSettings = () => {
@@ -34,9 +34,6 @@ const ApplicationSettings = () => {
});
const [applicationData, setApplicationData] = useState<any>([]);
const { theme } = useCommonHooks();
const [application, setApplication] = useState<any>({});
const [update, setUpdate] = useState({});
const [debounced] = useDebouncedValue(update, 1000);
const [newApplication, setNewApplication] = useState<any>({
name: '',
url: '',
@@ -62,18 +59,6 @@ const ApplicationSettings = () => {
}
}, [apps, isAppsFetching, debouncedSearch]);
useEffect(() => {
if (Object.keys(debounced).length) {
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
}
}, [debounced]);
useEffect(() => {
if (update !== application && Object.keys(update).length > 0) {
setApplication(update);
}
}, [update]);
return (
<Stack w={'100%'}>
<Modal.Root
@@ -103,71 +88,79 @@ const ApplicationSettings = () => {
</Modal.Header>
<Modal.Body pb={'2rem'}>
<Stack gap={0} w={'100%'} align='center'>
<TextInput
label='Name'
autoFocus
w={'75%'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
onChange={(event) => {
setNewApplication({
...newApplication,
name: event.target.value,
});
}}
/>
<TextInput
label='URL'
autoFocus
w={'75%'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
onChange={(event) => {
setNewApplication({
...newApplication,
url: event.target.value,
});
}}
/>
<TextInput
label='Icon'
autoFocus
w={'75%'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
onChange={(event) => {
setNewApplication({
...newApplication,
icon: event.target.value,
});
}}
/>
<TextInput
label='Base URL'
autoFocus
w={'75%'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
onChange={(event) => {
setNewApplication({
...newApplication,
baseUrl: event.target.value,
});
}}
/>
<TextInput
label='Filter Name'
autoFocus
w={'75%'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
onChange={(event) => {
setNewApplication({
...newApplication,
filterName: event.target.value,
});
}}
/>
<Group w={'100%'} justify='center'>
<TextInput
label={'Name'}
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={newApplication.name}
onChange={(event) => {
setNewApplication({
...newApplication,
name: event.target.value,
});
}}
/>
<TextInput
label={'Base URL'}
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={newApplication.baseUrl}
onChange={(event) => {
setNewApplication({
...newApplication,
baseUrl: event.target.value,
});
}}
/>
</Group>
<Group w={'100%'} justify='center'>
<TextInput
label={'URL'}
className={textInputClasses.settingsItem}
w={'59%'}
pt={16}
classNames={textInputClasses}
defaultValue={newApplication.url}
onChange={(event) => {
setNewApplication({
...newApplication,
url: event.target.value,
});
}}
/>
</Group>
<Group w={'100%'} justify='center'>
<TextInput
label={'Icon'}
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={newApplication.icon}
onChange={(event) => {
setNewApplication({
...newApplication,
icon: event.target.value,
});
}}
/>
<TextInput
label={'Filter'}
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={newApplication.filterName}
onChange={(event) => {
setNewApplication({
...newApplication,
filterName: event.target.value,
});
}}
/>
</Group>
<Divider color='black' my='sm' />
<Group pos={'absolute'} bottom={8}>
<Button
@@ -262,20 +255,18 @@ const ApplicationSettings = () => {
<Stack w={'100%'} align='center'>
<Grid columns={18} w={'75%'}>
{applicationData?.map((application: any) => (
<GridCol span={3} key={application.id}>
<GridCol span={6} key={application.id}>
<Popover withArrow withOverlay withinPortal={false}>
<Popover.Target>
<Box
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<AppItem
<AppSettingsItem
url={application.url}
icon={application.icon}
name={application.name}
linkDisabled
/>
</Box>
</Popover.Target>
@@ -283,7 +274,6 @@ const ApplicationSettings = () => {
<DebouncedApplication
application={application}
handleDelete={handleDelete}
setApplication={setUpdate}
/>
</Popover.Dropdown>
</Popover>

View File

@@ -9,14 +9,33 @@ import {
TextInput,
Tooltip,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { useDebouncedValue, useDisclosure } from '@mantine/hooks';
import { IconTrash } from '@tabler/icons-react';
import { useCommonHooks } from 'hooks';
import { useCommonHooks, useMutationAPI } from 'hooks';
import { useEffect, useState } from 'react';
import { cardClasses, modalClasses, textInputClasses } from 'styles';
function DebouncedApplication({ application, handleDelete, setApplication }: any) {
function DebouncedApplication({ application, handleDelete }: any) {
const { theme } = useCommonHooks();
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
const { mutateAsync } = useMutationAPI();
const [app, setApp] = useState<any>(application);
const [update, setUpdate] = useState({});
const [debounced] = useDebouncedValue(update, 1000);
useEffect(() => {
if (Object.keys(debounced).length) {
mutateAsync({ method: 'PATCH', endpoint: 'api/apps', body: debounced });
}
}, [debounced]);
useEffect(() => {
if (update !== app && Object.keys(update).length) {
setApp(update);
}
}, [update]);
return (
<Card className={cardClasses.settingsItem} classNames={cardClasses}>
<Modal.Root
@@ -57,14 +76,14 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
backgroundColor: 'var(--color-text-acc)',
color: 'var(--color-background)',
}}
>{`"${application.name}"`}</Text>
>{`"${app.name}"`}</Text>
<Text c='red'>This action cannot be undone!</Text>
<Group pos={'absolute'} bottom={8}>
<Button
color={'red'}
styles={{ label: { color: 'black' } }}
onClick={() => {
handleDelete(application);
handleDelete(app);
closeModal();
}}
>
@@ -106,10 +125,10 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={application.name}
defaultValue={app.name}
onChange={(event) => {
setApplication({
...application,
setUpdate({
...app,
name: event.target.value,
});
}}
@@ -119,10 +138,10 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={application.baseUrl}
defaultValue={app.baseUrl}
onChange={(event) => {
setApplication({
...application,
setUpdate({
...app,
baseUrl: event.target.value,
});
}}
@@ -135,10 +154,10 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
w={'100%'}
pt={16}
classNames={textInputClasses}
defaultValue={application.url}
defaultValue={app.url}
onChange={(event) => {
setApplication({
...application,
setUpdate({
...app,
url: event.target.value,
});
}}
@@ -150,10 +169,10 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={application.icon}
defaultValue={app.icon}
onChange={(event) => {
setApplication({
...application,
setUpdate({
...app,
icon: event.target.value,
});
}}
@@ -163,10 +182,10 @@ function DebouncedApplication({ application, handleDelete, setApplication }: any
className={textInputClasses.settingsItem}
pt={16}
classNames={textInputClasses}
defaultValue={application.filterName}
defaultValue={app.filterName}
onChange={(event) => {
setApplication({
...application,
setUpdate({
...app,
filterName: event.target.value,
});
}}