Settings style (#808)

This commit is contained in:
Carlos Valente
2024-03-09 10:56:14 +01:00
committed by GitHub
parent ad71793cf3
commit 78b386742e
10 changed files with 170 additions and 152 deletions
@@ -32,10 +32,10 @@ export default function AppSettings() {
<PanelContent onClose={closeSettings}> <PanelContent onClose={closeSettings}>
{selectedPanel === 'project' && <ProjectPanel />} {selectedPanel === 'project' && <ProjectPanel />}
{selectedPanel === 'general' && <GeneralPanel />} {selectedPanel === 'general' && <GeneralPanel />}
{selectedPanel === 'project_settings' && <ProjectSettingsPanel />}
{selectedPanel === 'sources' && <SourcesPanel />} {selectedPanel === 'sources' && <SourcesPanel />}
{selectedPanel === 'interface' && <InterfacePanel />} {selectedPanel === 'interface' && <InterfacePanel />}
{selectedPanel === 'integrations' && <IntegrationsPanel />} {selectedPanel === 'integrations' && <IntegrationsPanel />}
{selectedPanel === 'project_settings' && <ProjectSettingsPanel />}
{selectedPanel === 'about' && <AboutPanel />} {selectedPanel === 'about' && <AboutPanel />}
{selectedPanel === 'log' && <LogPanel />} {selectedPanel === 'log' && <LogPanel />}
</PanelContent> </PanelContent>
@@ -98,7 +98,7 @@ $inner-padding: 1rem;
} }
.listGroup { .listGroup {
padding: 1rem 2rem 0 2rem; padding: 0 2rem;
> li:not(:last-child) { > li:not(:last-child) {
border-bottom: 1px solid $white-10; border-bottom: 1px solid $white-10;
@@ -130,23 +130,22 @@ $inner-padding: 1rem;
.divider { .divider {
border-top: 1px solid $white-10; border-top: 1px solid $white-10;
margin: 1rem -2rem; margin: 1rem -2rem;
z-index: 999;
} }
$loader-size: 4rem;
.overlay { .overlay {
position: absolute; position: absolute;
z-index: 10; z-index: 10;
width: calc(100% - 2rem); width: 100%;
left: 1rem; height: 100%;
height: calc(80% - 1rem); backdrop-filter: blur(2px);
display: grid; display: grid;
place-content: center; place-content: center;
backdrop-filter: blur(5px); background-color: $black-10;
background-color: rgba(0, 0, 0, 0.01);
} }
.loader { .loader {
$loader-size: 4rem;
width: $loader-size; width: $loader-size;
height: $loader-size; height: $loader-size;
background: $blue-500; background: $blue-500;
@@ -82,10 +82,13 @@ export function Divider() {
return <hr className={style.divider} />; return <hr className={style.divider} />;
} }
export function Loader() { export function Loader({ isLoading }: { isLoading: boolean }) {
if (!isLoading) {
return null;
}
return ( return (
<div className={style.overlay}> <div className={style.overlay}>
<span className={style.loader} /> <div className={style.loader} />
</div> </div>
); );
} }
@@ -59,6 +59,8 @@ export default function GeneralPanelForm() {
reset(data); reset(data);
}; };
const isLoading = status === 'pending';
return ( return (
<Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} id='app-settings'> <Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} id='app-settings'>
<Panel.Card> <Panel.Card>
@@ -82,76 +84,79 @@ export default function GeneralPanelForm() {
</Panel.SubHeader> </Panel.SubHeader>
{submitError && <Panel.Error>{submitError}</Panel.Error>} {submitError && <Panel.Error>{submitError}</Panel.Error>}
<Panel.Divider /> <Panel.Divider />
<Panel.ListGroup> <Panel.Section>
<Panel.ListItem> <Panel.Loader isLoading={isLoading} />
<Panel.Field <Panel.ListGroup>
title='Ontime server port' <Panel.ListItem>
description='Port ontime server listens in. Defaults to 4001 (needs app restart)' <Panel.Field
error={errors.serverPort?.message} title='Ontime server port'
/> description='Port ontime server listens in. Defaults to 4001 (needs app restart)'
<Input error={errors.serverPort?.message}
id='serverPort' />
size='sm' <Input
type='number' id='serverPort'
variant='ontime-filled' size='sm'
maxLength={5} type='number'
width='75px' variant='ontime-filled'
{...register('serverPort', { maxLength={5}
required: { value: true, message: 'Required field' }, width='75px'
max: { value: 65535, message: 'Port must be within range 1024 - 65535' }, {...register('serverPort', {
min: { value: 1024, message: 'Port must be within range 1024 - 65535' }, required: { value: true, message: 'Required field' },
pattern: { max: { value: 65535, message: 'Port must be within range 1024 - 65535' },
value: isOnlyNumbers, min: { value: 1024, message: 'Port must be within range 1024 - 65535' },
message: 'Value should be numeric', pattern: {
}, value: isOnlyNumbers,
})} message: 'Value should be numeric',
/> },
</Panel.ListItem> })}
<Panel.ListItem> />
<Panel.Field </Panel.ListItem>
title='Editor pin code' <Panel.ListItem>
description='Protect the editor view with a pin code' <Panel.Field
error={errors.editorKey?.message} title='Editor pin code'
/> description='Protect the editor view with a pin code'
<GeneralPinInput register={register} formName='editorKey' isDisabled={disableInputs} /> error={errors.editorKey?.message}
</Panel.ListItem> />
<Panel.ListItem> <GeneralPinInput register={register} formName='editorKey' isDisabled={disableInputs} />
<Panel.Field </Panel.ListItem>
title='Operator pin code' <Panel.ListItem>
description='Protect the operator and cuesheet views with a pin code' <Panel.Field
error={errors.operatorKey?.message} title='Operator pin code'
/> description='Protect the operator and cuesheet views with a pin code'
<GeneralPinInput register={register} formName='operatorKey' isDisabled={disableInputs} /> error={errors.operatorKey?.message}
</Panel.ListItem> />
<Panel.ListItem> <GeneralPinInput register={register} formName='operatorKey' isDisabled={disableInputs} />
<Panel.Field </Panel.ListItem>
title='Time format' <Panel.ListItem>
description='Default time format to show in views 12 /24 hours' <Panel.Field
error={errors.timeFormat?.message} title='Time format'
/> description='Default time format to show in views 12 /24 hours'
<Select variant='ontime' size='sm' width='auto' isDisabled={disableInputs} {...register('timeFormat')}> error={errors.timeFormat?.message}
<option value='12'>12 hours 11:00:10 PM</option> />
<option value='24'>24 hours 23:00:10</option> <Select variant='ontime' size='sm' width='auto' isDisabled={disableInputs} {...register('timeFormat')}>
</Select> <option value='12'>12 hours 11:00:10 PM</option>
</Panel.ListItem> <option value='24'>24 hours 23:00:10</option>
<Panel.ListItem> </Select>
<Panel.Field </Panel.ListItem>
title='Views language' <Panel.ListItem>
description='Language to be displayed in views' <Panel.Field
error={errors.language?.message} title='Views language'
/> description='Language to be displayed in views'
<Select variant='ontime' size='sm' width='auto' isDisabled={disableInputs} {...register('language')}> error={errors.language?.message}
<option value='en'>English</option> />
<option value='fr'>French</option> <Select variant='ontime' size='sm' width='auto' isDisabled={disableInputs} {...register('language')}>
<option value='de'>German</option> <option value='en'>English</option>
<option value='it'>Italian</option> <option value='fr'>French</option>
<option value='no'>Norwegian</option> <option value='de'>German</option>
<option value='pt'>Portuguese</option> <option value='it'>Italian</option>
<option value='es'>Spanish</option> <option value='no'>Norwegian</option>
<option value='sv'>Swedish</option> <option value='pt'>Portuguese</option>
</Select> <option value='es'>Spanish</option>
</Panel.ListItem> <option value='sv'>Swedish</option>
</Panel.ListGroup> </Select>
</Panel.ListItem>
</Panel.ListGroup>
</Panel.Section>
</Panel.Card> </Panel.Card>
</Panel.Section> </Panel.Section>
); );
@@ -84,11 +84,11 @@ export default function UrlPresetsForm() {
}); });
}; };
const isPending = status === 'pending'; const isLoading = status === 'pending';
const canSubmit = !isSubmitting && isDirty && isValid; const canSubmit = !isSubmitting && isDirty && isValid;
return ( return (
<Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} data-testId='url-preset-form'> <Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} data-testid='url-preset-form'>
<Panel.Card> <Panel.Card>
<Panel.SubHeader> <Panel.SubHeader>
URL Presets URL Presets
@@ -102,21 +102,21 @@ export default function UrlPresetsForm() {
</div> </div>
</Panel.SubHeader> </Panel.SubHeader>
<Panel.Divider /> <Panel.Divider />
{isPending && <Panel.Loader />} <Alert status='info' variant='ontime-on-dark-info'>
<AlertIcon />
<AlertDescription>
URL Presets
<br />
<br />
Custom presets allow providing a short name for any ontime URL. <br />
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
<br />
<br />
<ExternalLink href={urlPresetsDocs}>See the docs</ExternalLink>
</AlertDescription>
</Alert>
<Panel.Section> <Panel.Section>
<Alert status='info' variant='ontime-on-dark-info'> <Panel.Loader isLoading={isLoading} />
<AlertIcon />
<AlertDescription>
URL Presets
<br />
<br />
Custom presets allow providing a short name for any ontime URL. <br />
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
<br />
<br />
<ExternalLink href={urlPresetsDocs}>See the docs</ExternalLink>
</AlertDescription>
</Alert>
<Panel.Title> <Panel.Title>
Manage presets Manage presets
<Button variant='ontime-subtle' rightIcon={<IoAdd />} size='sm' onClick={addNew}> <Button variant='ontime-subtle' rightIcon={<IoAdd />} size='sm' onClick={addNew}>
@@ -16,8 +16,8 @@ import style from './GeneralPanel.module.scss';
const cssOverrideDocsUrl = 'https://docs.getontime.no/features/custom-styling/'; const cssOverrideDocsUrl = 'https://docs.getontime.no/features/custom-styling/';
export default function ViewSettingsForm() { export default function ViewSettingsForm() {
const { data, status, refetch, isFetching } = useViewSettings(); const { data, status, refetch } = useViewSettings();
const { data: info, isFetching: isFetchingInfo } = useInfo(); const { data: info, status: infoStatus } = useInfo();
const { const {
control, control,
@@ -64,7 +64,7 @@ export default function ViewSettingsForm() {
return null; return null;
} }
const disableInputs = status === 'pending'; const isLoading = status === 'pending' || infoStatus === 'pending';
return ( return (
<Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} id='view-settings'> <Panel.Section as='form' onSubmit={handleSubmit(onSubmit)} id='view-settings'>
@@ -81,55 +81,56 @@ export default function ViewSettingsForm() {
</div> </div>
</Panel.SubHeader> </Panel.SubHeader>
<Panel.Divider /> <Panel.Divider />
{isFetching || isFetchingInfo ? <Panel.Loader /> : null} <Alert status='info' variant='ontime-on-dark-info'>
<Panel.ListGroup> <AlertIcon />
<Alert status='info' variant='ontime-on-dark-info'> <AlertDescription>
<AlertIcon /> You can override the styles of the viewers with a custom CSS file. <br />
<AlertDescription> {info?.cssOverride && `In your installation the file is at ${info?.cssOverride}`}
You can override the styles of the viewers with a custom CSS file. <br /> <br />
{info?.cssOverride && `In your installation the file is at ${info?.cssOverride}`} <br />
<br /> <ExternalLink href={cssOverrideDocsUrl}>See the docs</ExternalLink>
<br /> </AlertDescription>
<ExternalLink href={cssOverrideDocsUrl}>See the docs</ExternalLink> </Alert>
</AlertDescription> <Panel.Section>
</Alert> <Panel.Loader isLoading={isLoading} />
<Panel.ListItem> <Panel.ListGroup>
<Panel.Field <Panel.ListItem>
title='Override CSS styles' <Panel.Field
description='Enables overriding view styles with custom stylesheet' title='Override CSS styles'
/> description='Enables overriding view styles with custom stylesheet'
<Switch {...register('overrideStyles')} variant='ontime' size='lg' /> />
</Panel.ListItem> <Switch {...register('overrideStyles')} variant='ontime' size='lg' />
</Panel.ListGroup> </Panel.ListItem>
<Panel.ListGroup> </Panel.ListGroup>
<Panel.ListItem> <Panel.ListGroup>
<Panel.Field title='Timer colour' description='Default colour of a running timer' /> <Panel.ListItem>
<PopoverPickerRHF name='normalColor' control={control} /> <Panel.Field title='Timer colour' description='Default colour of a running timer' />
</Panel.ListItem> <PopoverPickerRHF name='normalColor' control={control} />
<Panel.ListItem> </Panel.ListItem>
<Panel.Field title='Warning colour' description='Colour of a running timer in warning mode' /> <Panel.ListItem>
<PopoverPickerRHF name='warningColor' control={control} /> <Panel.Field title='Warning colour' description='Colour of a running timer in warning mode' />
</Panel.ListItem> <PopoverPickerRHF name='warningColor' control={control} />
<Panel.ListItem> </Panel.ListItem>
<Panel.Field title='Danger colour' description='Colour of a running timer in danger mode' /> <Panel.ListItem>
<PopoverPickerRHF name='dangerColor' control={control} /> <Panel.Field title='Danger colour' description='Colour of a running timer in danger mode' />
</Panel.ListItem> <PopoverPickerRHF name='dangerColor' control={control} />
</Panel.ListGroup> </Panel.ListItem>
<Panel.ListGroup> </Panel.ListGroup>
<Panel.ListItem> <Panel.ListGroup>
<Panel.Field title='End message' description='If no end message is provided, timer will continue' /> <Panel.ListItem>
<Input <Panel.Field title='End message' description='If no end message is provided, timer will continue' />
size='sm' <Input
autoComplete='off' size='sm'
variant='ontime-filled' autoComplete='off'
maxLength={150} variant='ontime-filled'
width='275px' maxLength={150}
placeholder='Message shown when timer reaches end' width='275px'
isDisabled={disableInputs} placeholder='Message shown when timer reaches end'
{...register('endMessage')} {...register('endMessage')}
/> />
</Panel.ListItem> </Panel.ListItem>
</Panel.ListGroup> </Panel.ListGroup>
</Panel.Section>
</Panel.Card> </Panel.Card>
</Panel.Section> </Panel.Section>
); );
@@ -16,7 +16,7 @@ import { cycles } from './integrationUtils';
import style from './IntegrationsPanel.module.css'; import style from './IntegrationsPanel.module.css';
export default function HttpIntegrations() { export default function HttpIntegrations() {
const { data } = useHttpSettings(); const { data, status } = useHttpSettings();
const { mutateAsync } = usePostHttpSettings(); const { mutateAsync } = usePostHttpSettings();
const { const {
@@ -69,6 +69,7 @@ export default function HttpIntegrations() {
}; };
const canSubmit = !isSubmitting && isDirty && isValid; const canSubmit = !isSubmitting && isDirty && isValid;
const isLoading = status === 'pending';
return ( return (
<Panel.Section> <Panel.Section>
@@ -93,6 +94,7 @@ export default function HttpIntegrations() {
</Panel.SubHeader> </Panel.SubHeader>
<Panel.Divider /> <Panel.Divider />
<Panel.Section as='form' id='http-form' onSubmit={handleSubmit(onSubmit)} onKeyDown={preventEscape}> <Panel.Section as='form' id='http-form' onSubmit={handleSubmit(onSubmit)} onKeyDown={preventEscape}>
<Panel.Loader isLoading={isLoading} />
{errors?.root && <Panel.Error>{errors.root.message}</Panel.Error>} {errors?.root && <Panel.Error>{errors.root.message}</Panel.Error>}
<Panel.ListGroup> <Panel.ListGroup>
<Panel.ListItem> <Panel.ListItem>
@@ -16,7 +16,7 @@ import { cycles } from './integrationUtils';
import style from './IntegrationsPanel.module.css'; import style from './IntegrationsPanel.module.css';
export default function OscIntegrations() { export default function OscIntegrations() {
const { data } = useOscSettings(); const { data, status } = useOscSettings();
const { mutateAsync } = useOscSettingsMutation(); const { mutateAsync } = useOscSettingsMutation();
const { const {
@@ -75,6 +75,7 @@ export default function OscIntegrations() {
}; };
const canSubmit = !isSubmitting && isDirty && isValid; const canSubmit = !isSubmitting && isDirty && isValid;
const isLoading = status === 'pending';
return ( return (
<Panel.Card> <Panel.Card>
@@ -100,6 +101,7 @@ export default function OscIntegrations() {
<Panel.Divider /> <Panel.Divider />
<Panel.Section as='form' id='osc-form' onSubmit={handleSubmit(onSubmit)} onKeyDown={preventEscape}> <Panel.Section as='form' id='osc-form' onSubmit={handleSubmit(onSubmit)} onKeyDown={preventEscape}>
<Panel.Loader isLoading={isLoading} />
<Panel.Title>OSC Settings</Panel.Title> <Panel.Title>OSC Settings</Panel.Title>
{errors?.root && <Panel.Error>{errors.root.message}</Panel.Error>} {errors?.root && <Panel.Error>{errors.root.message}</Panel.Error>}
<Panel.ListGroup> <Panel.ListGroup>
@@ -51,6 +51,7 @@ export default function ManageProjects() {
const handleCloseForm = () => { const handleCloseForm = () => {
setIsCreatingProject(false); setIsCreatingProject(false);
}; };
return ( return (
<Panel.Section> <Panel.Section>
<Input <Input
@@ -11,7 +11,7 @@ import * as Panel from '../PanelUtils';
import style from './ProjectPanel.module.scss'; import style from './ProjectPanel.module.scss';
export default function ProjectData() { export default function ProjectData() {
const { data, refetch } = useProjectData(); const { data, status, refetch } = useProjectData();
const { const {
handleSubmit, handleSubmit,
@@ -45,9 +45,13 @@ export default function ProjectData() {
} }
}; };
// populate with new data if we get an update
const onReset = () => { const onReset = () => {
reset(data); reset(data);
}; };
const isLoading = status === 'pending';
return ( return (
<Panel.Section as='form' onSubmit={handleSubmit(onSubmit)}> <Panel.Section as='form' onSubmit={handleSubmit(onSubmit)}>
<Panel.Card> <Panel.Card>
@@ -70,6 +74,7 @@ export default function ProjectData() {
</Panel.SubHeader> </Panel.SubHeader>
<Panel.Divider /> <Panel.Divider />
<Panel.Section> <Panel.Section>
<Panel.Loader isLoading={isLoading} />
<label> <label>
Project title Project title
<Input <Input