Customizable fonts

This commit is contained in:
Sean Edmunds
2026-01-28 14:27:27 -06:00
parent f92ee29dfa
commit 983fcbe2ae
7 changed files with 435 additions and 358 deletions

View File

@@ -12,6 +12,7 @@ import { useDebouncedValue } from '@mantine/hooks';
import { SettingsHeader } from 'components';
import { useCommonHooks, useMutationAPI, useQueryAPI } from 'hooks';
import { useEffect, useState } from 'react';
import FontPicker from 'react-fontpicker-ts';
import { cardClasses, switchClasses, textClasses, textInputClasses } from 'styles';
import type { PersonalData } from 'types';
@@ -20,25 +21,44 @@ const PersonalizationSettings = () => {
const { data: personalData, isFetching: personalDataIsFetching } = useQueryAPI({
endpoint: endpoint,
});
const [personal, setPersonal] = useState<PersonalData>(personalData?.[0] || {});
const personal: PersonalData = personalData?.[0] || {};
const [wolEnabled, setWolEnabled] = useState<boolean>(personal?.enableWol === 1);
const [guacamoleEnabled, setGuacamoleEnabled] = useState<boolean>(
personal?.enableGuacamole === 1,
);
const [defaultFont, setDefaultFont] = useState<string>(personal?.defaultFont);
const [update, setUpdate] = useState<any>({});
const [debounced] = useDebouncedValue(update, 1000);
const { mutateAsync } = useMutationAPI();
const { isMobile, theme } = useCommonHooks();
useEffect(() => {
if (Object.keys(debounced).length) {
setPersonal(update);
mutateAsync({ method: 'PATCH', endpoint: endpoint, body: debounced });
setUpdate({});
}
}, [debounced]);
useEffect(() => {
if (update !== personal && Object.keys(update).length) {
setPersonal(update);
if (Object.keys(personal).length > 0) {
setWolEnabled(personal.enableWol === 1);
setGuacamoleEnabled(personal.enableGuacamole === 1);
setDefaultFont(personal.defaultFont);
}
}, [update]);
}, [personal]);
useEffect(() => {
if (
Object.keys(personal).length > 0 &&
defaultFont &&
defaultFont !== personal.defaultFont
) {
setUpdate({
...personal,
defaultFont: defaultFont,
});
}
}, [defaultFont]);
return (
<Stack>
<SettingsHeader
@@ -76,163 +96,229 @@ const PersonalizationSettings = () => {
}}
/>
<Grid w={'75%'}>
<GridCol span={isMobile ? 12 : 4}>
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
<TextInput
label={'First Name'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.firstName}
onChange={(event) => {
const updatedPersonal = {
...personal,
firstName: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={isMobile ? 12 : 4}>
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
<TextInput
label={'Last Name'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.lastName}
onChange={(event) => {
const updatedPersonal = {
...personal,
lastName: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={isMobile ? 12 : 4}>
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
<TextInput
label={'Email'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.email}
onChange={(event) => {
const updatedPersonal = {
...personal,
email: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={12}>
<Card className={cardClasses.settingsItem} classNames={cardClasses} pt={0}>
<TextInput
label={'Physical Address'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.physicalAddress}
onChange={(event) => {
const updatedPersonal = {
...personal,
physicalAddress: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
{Object.keys(personal).length > 0 && (
<>
<GridCol span={isMobile ? 12 : 4}>
<Card
className={cardClasses.settingsItem}
classNames={cardClasses}
pt={0}
>
<TextInput
label={'First Name'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.firstName}
onChange={(event) => {
const updatedPersonal = {
...personal,
firstName: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={isMobile ? 12 : 4}>
<Card
className={cardClasses.settingsItem}
classNames={cardClasses}
pt={0}
>
<TextInput
label={'Last Name'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.lastName}
onChange={(event) => {
const updatedPersonal = {
...personal,
lastName: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={isMobile ? 12 : 4}>
<Card
className={cardClasses.settingsItem}
classNames={cardClasses}
pt={0}
>
<TextInput
label={'Email'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.email}
onChange={(event) => {
const updatedPersonal = {
...personal,
email: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={12}>
<Card
className={cardClasses.settingsItem}
classNames={cardClasses}
pt={0}
>
<TextInput
label={'Nickname'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.nickname || ''}
onChange={(event) => {
const updatedPersonal = {
...personal,
nickname: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={12}>
<Card
className={cardClasses.settingsItem}
classNames={cardClasses}
pt={0}
>
<TextInput
label={'Physical Address'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.physicalAddress}
onChange={(event) => {
const updatedPersonal = {
...personal,
physicalAddress: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
</Card>
</GridCol>
<GridCol span={12}>
<Card
w={'100%'}
className={cardClasses.settingsItem}
classNames={cardClasses}
style={{ overflow: 'visible' }}
>
<Stack w={'100%'} px={16}>
<Text
top={-12}
className={textClasses.currentTheme}
classNames={textClasses}
>
Extras
</Text>
<Text>Font Select</Text>
{defaultFont && (
<FontPicker
autoLoad
defaultValue={defaultFont || 'Jost'}
value={(font: string) => setDefaultFont(font)}
/>
)}
<Switch
label={'Enable Wake on LAN'}
className={switchClasses.default}
classNames={switchClasses}
checked={wolEnabled}
onChange={(event) => {
setWolEnabled(!wolEnabled);
const updatedPersonal = {
...personal,
enableWol: event.target.checked ? 1 : 0,
};
setUpdate(updatedPersonal);
}}
/>
<Switch
label={'Enable Guacamole Clients'}
className={switchClasses.default}
classNames={switchClasses}
checked={guacamoleEnabled}
onChange={(event) => {
setGuacamoleEnabled(!guacamoleEnabled);
const updatedPersonal = {
...personal,
enableGuacamole: event.target.checked ? 1 : 0,
};
setUpdate(updatedPersonal);
}}
/>
{personal?.enableGuacamole === 1 && (
<TextInput
label={'Guacamole Host'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.guacamoleHost}
onChange={(event) => {
const updatedPersonal = {
...personal,
guacamoleHost: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: {
boxShadow: '2px 2px var(--mantine-color-border-0)',
},
}
: undefined
}
/>
)}
</Stack>
</Card>
</GridCol>
</>
)}
</Grid>
<Card
w={'75%'}
className={cardClasses.settingsItem}
classNames={cardClasses}
style={{ overflow: 'visible' }}
>
<Stack w={'100%'} px={16}>
<Text top={-12} className={textClasses.currentTheme} classNames={textClasses}>
Extras
</Text>
<Switch
label={'Enable Wake on LAN'}
className={switchClasses.default}
classNames={switchClasses}
checked={personal?.enableWol === 1}
onChange={(event) => {
const updatedPersonal = {
...personal,
enableWol: event.target.checked ? 1 : 0,
};
setUpdate(updatedPersonal);
}}
/>
<Switch
label={'Enable Guacamole Clients'}
className={switchClasses.default}
classNames={switchClasses}
checked={personal?.enableGuacamole === 1}
onChange={(event) => {
const updatedPersonal = {
...personal,
enableGuacamole: event.target.checked ? 1 : 0,
};
setUpdate(updatedPersonal);
}}
/>
{personal?.enableGuacamole === 1 && (
<TextInput
label={'Guacamole Host'}
className={textInputClasses.settingsItem}
classNames={textInputClasses}
defaultValue={personal?.guacamoleHost}
onChange={(event) => {
const updatedPersonal = {
...personal,
guacamoleHost: event.currentTarget.value,
};
setUpdate(updatedPersonal);
}}
styles={
theme.textShadowsEnabled
? {
label: { boxShadow: '2px 2px var(--mantine-color-border-0)' },
}
: undefined
}
/>
)}
</Stack>
</Card>
</Stack>
</Stack>
);