Warning and danger per event (#677)

* feat: warning and danger per event

---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>
This commit is contained in:
Fabian Posenau
2023-12-30 20:56:57 +01:00
committed by GitHub
parent 7b9f64a630
commit 3998b8927d
31 changed files with 290 additions and 203 deletions
@@ -2,7 +2,7 @@ import MultiPartProgressBar from '../../../common/components/multi-part-progress
import { useTimer } from '../../../common/hooks/useSocket';
import useViewSettings from '../../../common/hooks-query/useViewSettings';
import styles from "./CuesheetProgress.module.scss"
import styles from './CuesheetProgress.module.scss';
export default function CuesheetProgress() {
const { data } = useViewSettings();
@@ -14,9 +14,9 @@ export default function CuesheetProgress() {
now={timer.current}
complete={totalTime}
normalColor={data!.normalColor}
warning={data!.warningThreshold}
warning={timer.timeWarning}
warningColor={data!.warningColor}
danger={data!.dangerThreshold}
danger={timer.timeDanger}
dangerColor={data!.dangerColor}
className={styles.progressOverride}
/>
@@ -56,6 +56,8 @@ export default function EventEditor() {
isPublic={event.isPublic}
endAction={event.endAction}
timerType={event.timerType}
timeWarning={event.timeWarning}
timeDanger={event.timeDanger}
/>
<EventEditorDataLeft
key={`${event.id}-left`}
@@ -4,6 +4,7 @@ import { EndAction, OntimeEvent, TimerType } from 'ontime-types';
import { calculateDuration, millisToString } from 'ontime-utils';
import TimeInput from '../../../common/components/input/time-input/TimeInput';
import TimeInputWithButton from '../../../common/components/input/time-input/TimeInputWithButton';
import { useEventAction } from '../../../common/hooks/useEventAction';
import { millisToDelayString } from '../../../common/utils/dateConfig';
import { cx } from '../../../common/utils/styleUtils';
@@ -20,13 +21,24 @@ interface EventEditorTimesProps {
isPublic: boolean;
endAction: EndAction;
timerType: TimerType;
timeWarning: number;
timeDanger: number;
}
type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride' | 'timerType' | 'endAction' | 'isPublic';
type TimeActions =
| 'timeStart'
| 'timeEnd'
| 'durationOverride'
| 'timerType'
| 'endAction'
| 'isPublic'
| 'timeWarning'
| 'timeDanger';
// Todo: add previous end to TimeInput fields
const EventEditorTimes = (props: EventEditorTimesProps) => {
const { eventId, timeStart, timeEnd, duration, delay, isPublic, endAction, timerType } = props;
const { eventId, timeStart, timeEnd, duration, delay, isPublic, endAction, timerType, timeWarning, timeDanger } =
props;
const { updateEvent } = useEventAction();
const [warning, setWarnings] = useState({ start: '', end: '', duration: '' });
@@ -61,7 +73,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
break;
}
default: {
if (field === 'timerType' || field === 'endAction') {
if (field === 'timerType' || field === 'endAction' || field === 'timeWarning' || field === 'timeDanger') {
// @ts-expect-error -- not sure how to typecheck here
newEventData[field as keyof OntimeEvent] = value as string;
} else {
@@ -83,7 +95,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
<label className={inputTimeLabels} htmlFor='timeStart'>
{startLabel}
</label>
<TimeInput
<TimeInputWithButton
id='timeStart'
name='timeStart'
submitHandler={handleSubmit}
@@ -96,7 +108,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
<label className={inputTimeLabels} htmlFor='timeEnd'>
{endLabel}
</label>
<TimeInput
<TimeInputWithButton
id='timeEnd'
name='timeEnd'
submitHandler={handleSubmit}
@@ -109,7 +121,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
<label className={style.inputLabel} htmlFor='durationOverride'>
Duration
</label>
<TimeInput
<TimeInputWithButton
id='durationOverride'
name='durationOverride'
submitHandler={handleSubmit}
@@ -118,6 +130,11 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
placeholder='Duration'
warning={warning.duration}
/>
<span className={style.spacer} />
<label className={`${style.inputLabel} ${style.publicToggle}`}>
<Switch isChecked={isPublic} onChange={() => handleSubmit('isPublic', isPublic)} variant='ontime' />
Event is public
</label>
</div>
<div className={style.timeSettings}>
<label className={style.inputLabel}>Timer Type</label>
@@ -146,11 +163,30 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
<option value={EndAction.LoadNext}>Load Next</option>
<option value={EndAction.PlayNext}>Play Next</option>
</Select>
<span className={style.spacer} />
<label className={`${style.inputLabel} ${style.publicToggle}`}>
<Switch isChecked={isPublic} onChange={() => handleSubmit('isPublic', isPublic)} variant='ontime' />
Event is public
<label className={style.inputLabel} htmlFor='timeWarning'>
Warning Time
</label>
<TimeInput
id='timeWarning'
name='timeWarning'
submitHandler={handleSubmit}
validationHandler={timerValidationHandler}
time={timeWarning}
placeholder='Duration'
warning={warning.duration}
/>
<label className={style.inputLabel} htmlFor='timeDanger'>
Danger Time
</label>
<TimeInput
id='timeDanger'
name='timeDanger'
submitHandler={handleSubmit}
validationHandler={timerValidationHandler}
time={timeDanger}
placeholder='Duration'
warning={warning.duration}
/>
</div>
</div>
);
@@ -1,35 +0,0 @@
import { useController, UseControllerProps } from 'react-hook-form';
import { Input } from '@chakra-ui/react';
import { ViewSettings } from 'ontime-types';
import { millisToMinutes } from '../../../common/utils/dateConfig';
import { inputProps } from '../modalHelper';
export default function InputMillisWithString(props: UseControllerProps<ViewSettings>) {
const { name, control } = props;
const {
field: { onChange, value },
} = useController({
control,
name,
rules: {
pattern: {
value: /^[0-9]+$/,
message: 'Only numbers are valid',
},
},
});
return (
<Input
{...inputProps}
type='number'
variant='ontime-filled-on-light'
width='75px'
size='sm'
maxLength={3}
defaultValue={millisToMinutes(value as number)}
onChange={onChange}
/>
);
}
@@ -8,7 +8,6 @@ import { postViewSettings } from '../../../common/api/ontimeApi';
import { PopoverPickerRHF } from '../../../common/components/input/popover-picker/PopoverPicker';
import useInfo from '../../../common/hooks-query/useInfo';
import useViewSettings from '../../../common/hooks-query/useViewSettings';
import { mtm } from '../../../common/utils/timeConstants';
import ModalLoader from '../modal-loader/ModalLoader';
import { inputProps } from '../modalHelper';
import ModalInput from '../ModalInput';
@@ -16,8 +15,6 @@ import ModalLink from '../ModalLink';
import ModalSplitInput from '../ModalSplitInput';
import OntimeModalFooter from '../OntimeModalFooter';
import InputMillisWithString from './InputMillisWithString';
import style from './SettingsModal.module.scss';
const cssOverrideDocsUrl = 'https://ontime.gitbook.io/v2/features/custom-styling';
@@ -31,7 +28,7 @@ export default function ViewSettingsForm() {
handleSubmit,
register,
reset,
formState: { isSubmitting, isDirty, isValid, dirtyFields },
formState: { isSubmitting, isDirty, isValid },
} = useForm<ViewSettings>({
defaultValues: data,
values: data,
@@ -47,19 +44,8 @@ export default function ViewSettingsForm() {
}, [data, reset]);
const onSubmit = async (formData: ViewSettings) => {
const parsedWarningThreshold = dirtyFields?.warningThreshold
? // @ts-expect-error -- trust me
Number.parseInt(formData.warningThreshold) * mtm
: formData.warningThreshold;
const parsedDangerThreshold = dirtyFields?.dangerThreshold
? // @ts-expect-error -- trust me
Number.parseInt(formData.dangerThreshold) * mtm
: formData.dangerThreshold;
const newData = {
...formData,
warningThreshold: parsedWarningThreshold,
dangerThreshold: parsedDangerThreshold,
};
try {
@@ -107,27 +93,13 @@ export default function ViewSettingsForm() {
<Switch {...register('overrideStyles')} variant='ontime-on-light' />
</ModalSplitInput>
<span className={style.title}>Timer view settings</span>
<ModalSplitInput field='normalColor' title='Timer colour' description='Normal colour of a running timer'>
<ModalSplitInput field='normalColor' title='Timer color' description='Normal color of a running timer'>
<PopoverPickerRHF name='normalColor' control={control} />
</ModalSplitInput>
<ModalSplitInput
field='warningColor'
title='Warning Color'
description='Time (in minutes) when the timer moves to warning mode'
>
<InputMillisWithString name='warningThreshold' control={control} />
</ModalSplitInput>
<ModalSplitInput field='warningColor' title='Warning Color' description='Colour of timer in warning mode'>
<ModalSplitInput field='warningColor' title='Warning color' description='Color of timer in warning mode'>
<PopoverPickerRHF name='warningColor' control={control} />
</ModalSplitInput>
<ModalSplitInput
field='dangerThreshold'
title='Danger colour'
description='Time (in minutes) when the timer moves to danger mode'
>
<InputMillisWithString name='dangerThreshold' control={control} />
</ModalSplitInput>
<ModalSplitInput field='dangerColor' title='Timer colour' description='Colour of timer in danger mode'>
<ModalSplitInput field='dangerColor' title='Timer color' description='Color of timer in danger mode'>
<PopoverPickerRHF name='dangerColor' control={control} />
</ModalSplitInput>
<div style={{ height: '16px' }} />
@@ -20,9 +20,9 @@ export default function StatusBarProgress(props: StatusBarProgressProps) {
now={timer.current}
complete={totalTime}
normalColor={viewSettings.normalColor}
warning={viewSettings.warningThreshold}
warning={timer?.timeWarning}
warningColor={viewSettings.warningColor}
danger={viewSettings.dangerThreshold}
danger={timer?.timeDanger}
dangerColor={viewSettings.dangerColor}
className={styles.progressOverride}
/>
@@ -2,7 +2,7 @@ import { memo, useCallback, useState } from 'react';
import { OntimeEvent } from 'ontime-types';
import { calculateDuration, millisToString } from 'ontime-utils';
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
import TimeInputWithButton from '../../../../common/components/input/time-input/TimeInputWithButton';
import { useEventAction } from '../../../../common/hooks/useEventAction';
import { millisToDelayString } from '../../../../common/utils/dateConfig';
import { TimeEntryField, validateEntry } from '../../../../common/utils/timesManager';
@@ -18,7 +18,7 @@ interface EventBlockTimerProps {
previousEnd: number;
}
type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride';
type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride' | 'timeWarning' | 'timeDanger';
const EventBlockTimers = (props: EventBlockTimerProps) => {
const { eventId, timeStart, timeEnd, duration, delay, previousEnd } = props;
@@ -70,7 +70,7 @@ const EventBlockTimers = (props: EventBlockTimerProps) => {
return (
<div className={style.eventTimers}>
<TimeInput
<TimeInputWithButton
name='timeStart'
submitHandler={handleSubmit}
validationHandler={handleValidation}
@@ -80,7 +80,7 @@ const EventBlockTimers = (props: EventBlockTimerProps) => {
previousEnd={previousEnd}
warning={warning.start}
/>
<TimeInput
<TimeInputWithButton
name='timeEnd'
submitHandler={handleSubmit}
validationHandler={handleValidation}
@@ -90,7 +90,7 @@ const EventBlockTimers = (props: EventBlockTimerProps) => {
previousEnd={previousEnd}
warning={warning.end}
/>
<TimeInput
<TimeInputWithButton
name='durationOverride'
submitHandler={handleSubmit}
validationHandler={handleValidation}
@@ -135,8 +135,8 @@ export default function MinimalTimer(props: MinimalTimerProps) {
const showFinished = finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
const showProgress = time.playback !== Playback.Stop;
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold;
const showWarning = (time.current ?? 1) < (time.timeWarning ?? 0);
const showDanger = (time.current ?? 1) < (time.timeDanger ?? 0);
const showBlinking = pres.timerBlink;
const showBlackout = pres.timerBlackout;
@@ -98,8 +98,8 @@ export default function Timer(props: TimerProps) {
const showEndMessage = (time.current ?? 1) < 0 && viewSettings.endMessage;
const showProgress = time.playback !== Playback.Stop;
const showFinished = finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold;
const showWarning = (time.current ?? 1) < (eventNow?.timeWarning ?? 0);
const showDanger = (time.current ?? 1) < (eventNow?.timeDanger ?? 0);
const showBlinking = pres.timerBlink;
const showBlackout = pres.timerBlackout;
const showClock = time.timerType !== TimerType.Clock;
@@ -176,9 +176,9 @@ export default function Timer(props: TimerProps) {
now={time.current}
complete={totalTime}
normalColor={viewSettings.normalColor}
warning={viewSettings.warningThreshold}
warning={eventNow?.timeWarning}
warningColor={viewSettings.warningColor}
danger={viewSettings.dangerThreshold}
danger={eventNow?.timeDanger}
dangerColor={viewSettings.dangerColor}
hidden={!showProgress}
/>