mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 04:43:35 +00:00
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:
@@ -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}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user