mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-24 16:39:13 +00:00
refactor: performance tweaks to event editor
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { EventEditorSubmitActions } from '../../../../features/event-editor/EventEditor';
|
import { TitleActions } from '../../../../features/event-editor/composite/EventEditorTitles';
|
||||||
|
|
||||||
import Swatch from './Swatch';
|
import Swatch from './Swatch';
|
||||||
|
|
||||||
@@ -8,8 +8,8 @@ import style from './SwatchSelect.module.scss';
|
|||||||
|
|
||||||
interface ColourInputProps {
|
interface ColourInputProps {
|
||||||
value: string;
|
value: string;
|
||||||
name: EventEditorSubmitActions;
|
name: TitleActions;
|
||||||
handleChange: (newValue: EventEditorSubmitActions, name: string) => void;
|
handleChange: (newValue: TitleActions, name: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const colours = [
|
const colours = [
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ $playback-width: 450px;
|
|||||||
background-color: $gray-1200;
|
background-color: $gray-1200;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-left: 1px solid $white-10;
|
border-left: 1px solid $white-10;
|
||||||
|
border-radius: 0 8px 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,23 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Select, Switch } from '@chakra-ui/react';
|
import { OntimeEvent } from 'ontime-types';
|
||||||
import { EndAction, OntimeEvent, TimerType } from 'ontime-types';
|
|
||||||
import { millisToString } from 'ontime-utils';
|
|
||||||
|
|
||||||
import CopyTag from '../../common/components/copy-tag/CopyTag';
|
import CopyTag from '../../common/components/copy-tag/CopyTag';
|
||||||
import SwatchSelect from '../../common/components/input/colour-input/SwatchSelect';
|
|
||||||
import TimeInput from '../../common/components/input/time-input/TimeInput';
|
|
||||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
|
||||||
import useRundown from '../../common/hooks-query/useRundown';
|
import useRundown from '../../common/hooks-query/useRundown';
|
||||||
import { useEventEditorStore } from '../../common/stores/eventEditor';
|
import { useEventEditorStore } from '../../common/stores/eventEditor';
|
||||||
import { useEmitLog } from '../../common/stores/logger';
|
|
||||||
import { millisToMinutes } from '../../common/utils/dateConfig';
|
|
||||||
import getDelayTo from '../../common/utils/getDelayTo';
|
import getDelayTo from '../../common/utils/getDelayTo';
|
||||||
import { calculateDuration, TimeEntryField, validateEntry } from '../../common/utils/timesManager';
|
|
||||||
|
|
||||||
import CountedTextArea from './composite/CountedTextArea';
|
import EventEditorTimes from './composite/EventEditorTimes';
|
||||||
import CountedTextInput from './composite/CountedTextInput';
|
import EventEditorTitles from './composite/EventEditorTitles';
|
||||||
|
|
||||||
import style from './EventEditor.module.scss';
|
import style from './EventEditor.module.scss';
|
||||||
|
|
||||||
export type EventEditorSubmitActions = keyof OntimeEvent | 'durationOverride';
|
export type EventEditorSubmitActions = keyof OntimeEvent;
|
||||||
|
|
||||||
// Todo: add previous end to TimeInput fields
|
|
||||||
export default function EventEditor() {
|
export default function EventEditor() {
|
||||||
const { openId } = useEventEditorStore();
|
const { openId } = useEventEditorStore();
|
||||||
const { data } = useRundown();
|
const { data } = useRundown();
|
||||||
const { emitError } = useEmitLog();
|
|
||||||
const { updateEvent } = useEventAction();
|
|
||||||
const [event, setEvent] = useState<OntimeEvent | null>(null);
|
const [event, setEvent] = useState<OntimeEvent | null>(null);
|
||||||
const [delay, setDelay] = useState(0);
|
const [delay, setDelay] = useState(0);
|
||||||
const [warning, setWarnings] = useState({ start: '', end: '', duration: '' });
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!data || !openId) {
|
if (!data || !openId) {
|
||||||
@@ -46,83 +34,10 @@ export default function EventEditor() {
|
|||||||
}
|
}
|
||||||
}, [data, event, openId]);
|
}, [data, event, openId]);
|
||||||
|
|
||||||
const handleSubmit = useCallback(
|
|
||||||
(field: EventEditorSubmitActions, value: string | number) => {
|
|
||||||
if (event === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const newEventData: Partial<OntimeEvent> = { id: event.id };
|
|
||||||
switch (field) {
|
|
||||||
case 'durationOverride': {
|
|
||||||
// duration defines timeEnd
|
|
||||||
newEventData.duration = value as number;
|
|
||||||
newEventData.timeEnd = event.timeStart + (value as number);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'timeStart': {
|
|
||||||
newEventData.duration = calculateDuration(value as number, event.timeEnd);
|
|
||||||
newEventData.timeStart = value as number;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'timeEnd': {
|
|
||||||
newEventData.duration = calculateDuration(event.timeStart, value as number);
|
|
||||||
newEventData.timeEnd = value as number;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (field in event) {
|
|
||||||
// create object with new field
|
|
||||||
newEventData[field] = value;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
emitError(`Unknown field: ${field}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateEvent(newEventData);
|
|
||||||
},
|
|
||||||
[emitError, event, updateEvent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const timerValidationHandler = useCallback(
|
|
||||||
(entry: TimeEntryField, val: number) => {
|
|
||||||
if (!event?.timeStart) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const valid = validateEntry(entry, val, event.timeStart, event.timeEnd);
|
|
||||||
setWarnings((prev) => ({ ...prev, ...valid.warnings }));
|
|
||||||
return valid.value;
|
|
||||||
},
|
|
||||||
[event?.timeStart, event?.timeEnd],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChange = useCallback(
|
|
||||||
(field: string, value: string) => {
|
|
||||||
updateEvent({ id: event.id, [field]: value });
|
|
||||||
},
|
|
||||||
[event, updateEvent],
|
|
||||||
);
|
|
||||||
|
|
||||||
const togglePublic = useCallback(
|
|
||||||
(currentValue: boolean) => {
|
|
||||||
if (!event) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateEvent({ id: event.id, isPublic: !currentValue });
|
|
||||||
},
|
|
||||||
[event, updateEvent],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!event) {
|
if (!event) {
|
||||||
return <span>Loading...</span>;
|
return <span>Loading...</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const delayed = delay !== 0;
|
|
||||||
const addedTime = delayed ? `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))} minutes` : null;
|
|
||||||
const newStart = delayed ? `New start ${millisToString(event.timeStart + delay)}` : null;
|
|
||||||
const newEnd = delayed ? `New end ${millisToString(event.timeEnd + delay)}` : null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={style.eventEditor}>
|
<div className={style.eventEditor}>
|
||||||
<div className={style.eventInfo}>
|
<div className={style.eventInfo}>
|
||||||
@@ -132,103 +47,24 @@ export default function EventEditor() {
|
|||||||
<div className={style.eventActions}>
|
<div className={style.eventActions}>
|
||||||
<CopyTag label='OSC trigger'>{`/ontime/gotoid/${event.id}`}</CopyTag>
|
<CopyTag label='OSC trigger'>{`/ontime/gotoid/${event.id}`}</CopyTag>
|
||||||
</div>
|
</div>
|
||||||
<div className={style.timeOptions}>
|
<EventEditorTimes
|
||||||
<div className={style.timers}>
|
eventId={event.id}
|
||||||
<label className={style.inputLabel}>
|
timeStart={event.timeStart}
|
||||||
Start time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
timeEnd={event.timeEnd}
|
||||||
{delayed && <div className={style.delayLabel}>{newStart}</div>}
|
duration={event.duration}
|
||||||
</label>
|
delay={delay}
|
||||||
<TimeInput
|
isPublic={event.isPublic}
|
||||||
name='timeStart'
|
endAction={event.endAction}
|
||||||
submitHandler={handleSubmit}
|
timerType={event.timerType}
|
||||||
validationHandler={timerValidationHandler}
|
/>
|
||||||
time={event.timeStart}
|
<EventEditorTitles
|
||||||
delay={delay}
|
eventId={event.id}
|
||||||
placeholder='Start'
|
title={event.title}
|
||||||
warning={warning.start}
|
presenter={event.presenter}
|
||||||
/>
|
subtitle={event.subtitle}
|
||||||
<label className={style.inputLabel}>
|
note={event.note}
|
||||||
End time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
colour={event.colour}
|
||||||
{delayed && <div className={style.delayLabel}>{newEnd}</div>}
|
/>
|
||||||
</label>
|
|
||||||
<TimeInput
|
|
||||||
name='timeEnd'
|
|
||||||
submitHandler={handleSubmit}
|
|
||||||
validationHandler={timerValidationHandler}
|
|
||||||
time={event.timeEnd}
|
|
||||||
delay={delay}
|
|
||||||
placeholder='End'
|
|
||||||
warning={warning.end}
|
|
||||||
/>
|
|
||||||
<label className={style.inputLabel}>Duration</label>
|
|
||||||
<TimeInput
|
|
||||||
name='durationOverride'
|
|
||||||
submitHandler={handleSubmit}
|
|
||||||
validationHandler={timerValidationHandler}
|
|
||||||
time={event.duration}
|
|
||||||
placeholder='Duration'
|
|
||||||
warning={warning.duration}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={style.timeSettings}>
|
|
||||||
<label className={style.inputLabel}>Timer Type</label>
|
|
||||||
<Select
|
|
||||||
size='sm'
|
|
||||||
name='timerType'
|
|
||||||
value={event.timerType}
|
|
||||||
onChange={(event) => handleChange('timerType', event.target.value)}
|
|
||||||
variant='ontime'
|
|
||||||
>
|
|
||||||
<option value={TimerType.CountDown}>Count down</option>
|
|
||||||
<option value={TimerType.CountUp}>Count up</option>
|
|
||||||
<option value={TimerType.Clock}>Clock</option>
|
|
||||||
</Select>
|
|
||||||
<label className={style.inputLabel}>End Action</label>
|
|
||||||
<Select
|
|
||||||
size='sm'
|
|
||||||
name='endAction'
|
|
||||||
value={event.endAction}
|
|
||||||
onChange={(event) => handleChange('endAction', event.target.value)}
|
|
||||||
variant='ontime'
|
|
||||||
>
|
|
||||||
<option value={EndAction.Continue}>Continue</option>
|
|
||||||
<option value={EndAction.Stop}>Stop</option>
|
|
||||||
<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={event.isPublic} onChange={() => togglePublic(event.isPublic)} variant='ontime' />
|
|
||||||
Event is public
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={style.titles}>
|
|
||||||
<div className={style.left}>
|
|
||||||
<CountedTextInput field='title' label='Title' initialValue={event.title} submitHandler={handleSubmit} />
|
|
||||||
<CountedTextInput
|
|
||||||
field='presenter'
|
|
||||||
label='Presenter'
|
|
||||||
initialValue={event.presenter}
|
|
||||||
submitHandler={handleSubmit}
|
|
||||||
/>
|
|
||||||
<CountedTextInput
|
|
||||||
field='subtitle'
|
|
||||||
label='Subtitle'
|
|
||||||
initialValue={event.subtitle}
|
|
||||||
submitHandler={handleSubmit}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={style.right}>
|
|
||||||
<div className={style.column}>
|
|
||||||
<label className={style.inputLabel}>Colour</label>
|
|
||||||
<div className={style.inline}>
|
|
||||||
<SwatchSelect name='colour' value={event.colour} handleChange={handleSubmit} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<CountedTextArea field='note' label='Note' initialValue={event.note} submitHandler={handleSubmit} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,4 +35,3 @@ const EventEditorExport = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo(EventEditorExport);
|
export default memo(EventEditorExport);
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ import { useCallback } from 'react';
|
|||||||
import { Textarea } from '@chakra-ui/react';
|
import { Textarea } from '@chakra-ui/react';
|
||||||
|
|
||||||
import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput';
|
import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput';
|
||||||
import { EventEditorSubmitActions } from '../EventEditor';
|
|
||||||
|
import { TitleActions } from './EventEditorTitles';
|
||||||
|
|
||||||
import style from '../EventEditor.module.scss';
|
import style from '../EventEditor.module.scss';
|
||||||
|
|
||||||
interface CountedTextAreaProps {
|
interface CountedTextAreaProps {
|
||||||
field: EventEditorSubmitActions;
|
field: TitleActions;
|
||||||
label: string;
|
label: string;
|
||||||
initialValue: string;
|
initialValue: string;
|
||||||
submitHandler: (field: EventEditorSubmitActions, value: string) => void;
|
submitHandler: (field: TitleActions, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CountedTextArea(props: CountedTextAreaProps) {
|
export default function CountedTextArea(props: CountedTextAreaProps) {
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ import { useCallback } from 'react';
|
|||||||
import { Input } from '@chakra-ui/react';
|
import { Input } from '@chakra-ui/react';
|
||||||
|
|
||||||
import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput';
|
import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput';
|
||||||
import { EventEditorSubmitActions } from '../EventEditor';
|
|
||||||
|
import { TitleActions } from './EventEditorTitles';
|
||||||
|
|
||||||
import style from '../EventEditor.module.scss';
|
import style from '../EventEditor.module.scss';
|
||||||
|
|
||||||
interface CountedTextInputProps {
|
interface CountedTextInputProps {
|
||||||
field: EventEditorSubmitActions;
|
field: TitleActions;
|
||||||
label: string;
|
label: string;
|
||||||
initialValue: string;
|
initialValue: string;
|
||||||
submitHandler: (field: EventEditorSubmitActions, value: string) => void;
|
submitHandler: (field: TitleActions, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CountedTextInput(props: CountedTextInputProps) {
|
export default function CountedTextInput(props: CountedTextInputProps) {
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import { memo, useState } from 'react';
|
||||||
|
import { Select, Switch } from '@chakra-ui/react';
|
||||||
|
import { EndAction, OntimeEvent, TimerType } from 'ontime-types';
|
||||||
|
import { millisToString } from 'ontime-utils';
|
||||||
|
|
||||||
|
import TimeInput from '../../../common/components/input/time-input/TimeInput';
|
||||||
|
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||||
|
import { millisToMinutes } from '../../../common/utils/dateConfig';
|
||||||
|
import { calculateDuration, TimeEntryField, validateEntry } from '../../../common/utils/timesManager';
|
||||||
|
|
||||||
|
import style from '../EventEditor.module.scss';
|
||||||
|
|
||||||
|
interface EventEditorTimesProps {
|
||||||
|
eventId: string;
|
||||||
|
timeStart: number;
|
||||||
|
timeEnd: number;
|
||||||
|
duration: number;
|
||||||
|
delay: number;
|
||||||
|
isPublic: boolean;
|
||||||
|
endAction: EndAction;
|
||||||
|
timerType: TimerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride' | 'timerType' | 'endAction' | 'isPublic';
|
||||||
|
|
||||||
|
// Todo: add previous end to TimeInput fields
|
||||||
|
const EventEditorTimes = (props: EventEditorTimesProps) => {
|
||||||
|
const { eventId, timeStart, timeEnd, duration, delay, isPublic, endAction, timerType } = props;
|
||||||
|
const { updateEvent } = useEventAction();
|
||||||
|
|
||||||
|
const [warning, setWarnings] = useState({ start: '', end: '', duration: '' });
|
||||||
|
|
||||||
|
const timerValidationHandler = (entry: TimeEntryField, val: number) => {
|
||||||
|
const valid = validateEntry(entry, val, timeStart, timeEnd);
|
||||||
|
setWarnings((prev) => ({ ...prev, ...valid.warnings }));
|
||||||
|
return valid.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (field: TimeActions, value: number | string | boolean) => {
|
||||||
|
const newEventData: Partial<OntimeEvent> = { id: eventId };
|
||||||
|
switch (field) {
|
||||||
|
case 'durationOverride': {
|
||||||
|
// duration defines timeEnd
|
||||||
|
newEventData.duration = value as number;
|
||||||
|
newEventData.timeEnd = timeStart + (value as number);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'timeStart': {
|
||||||
|
newEventData.duration = calculateDuration(value as number, timeEnd);
|
||||||
|
newEventData.timeStart = value as number;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'timeEnd': {
|
||||||
|
newEventData.duration = calculateDuration(timeStart, value as number);
|
||||||
|
newEventData.timeEnd = value as number;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'isPublic': {
|
||||||
|
updateEvent({ id: eventId, isPublic: !(value as boolean) });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
if (field === 'timerType' || field === 'endAction') {
|
||||||
|
// @ts-expect-error -- not sure how to typecheck here
|
||||||
|
newEventData[field as keyof OntimeEvent] = value as string;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateEvent(newEventData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const delayed = delay !== 0;
|
||||||
|
const addedTime = delayed ? `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))} minutes` : null;
|
||||||
|
const newStart = delayed ? `New start ${millisToString(timeStart + delay)}` : null;
|
||||||
|
const newEnd = delayed ? `New end ${millisToString(timeEnd + delay)}` : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style.timeOptions}>
|
||||||
|
<div className={style.timers}>
|
||||||
|
<label className={style.inputLabel}>
|
||||||
|
Start time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||||
|
{delayed && <div className={style.delayLabel}>{newStart}</div>}
|
||||||
|
</label>
|
||||||
|
<TimeInput
|
||||||
|
name='timeStart'
|
||||||
|
submitHandler={handleSubmit}
|
||||||
|
validationHandler={timerValidationHandler}
|
||||||
|
time={timeStart}
|
||||||
|
delay={delay}
|
||||||
|
placeholder='Start'
|
||||||
|
warning={warning.start}
|
||||||
|
/>
|
||||||
|
<label className={style.inputLabel}>
|
||||||
|
End time {delayed && <span className={style.delayLabel}>{addedTime}</span>}
|
||||||
|
{delayed && <div className={style.delayLabel}>{newEnd}</div>}
|
||||||
|
</label>
|
||||||
|
<TimeInput
|
||||||
|
name='timeEnd'
|
||||||
|
submitHandler={handleSubmit}
|
||||||
|
validationHandler={timerValidationHandler}
|
||||||
|
time={timeEnd}
|
||||||
|
delay={delay}
|
||||||
|
placeholder='End'
|
||||||
|
warning={warning.end}
|
||||||
|
/>
|
||||||
|
<label className={style.inputLabel}>Duration</label>
|
||||||
|
<TimeInput
|
||||||
|
name='durationOverride'
|
||||||
|
submitHandler={handleSubmit}
|
||||||
|
validationHandler={timerValidationHandler}
|
||||||
|
time={duration}
|
||||||
|
placeholder='Duration'
|
||||||
|
warning={warning.duration}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={style.timeSettings}>
|
||||||
|
<label className={style.inputLabel}>Timer Type</label>
|
||||||
|
<Select
|
||||||
|
size='sm'
|
||||||
|
name='timerType'
|
||||||
|
value={timerType}
|
||||||
|
onChange={(event) => handleSubmit('timerType', event.target.value)}
|
||||||
|
variant='ontime'
|
||||||
|
>
|
||||||
|
<option value={TimerType.CountDown}>Count down</option>
|
||||||
|
<option value={TimerType.CountUp}>Count up</option>
|
||||||
|
<option value={TimerType.Clock}>Clock</option>
|
||||||
|
</Select>
|
||||||
|
<label className={style.inputLabel}>End Action</label>
|
||||||
|
<Select
|
||||||
|
size='sm'
|
||||||
|
name='endAction'
|
||||||
|
value={endAction}
|
||||||
|
onChange={(event) => handleSubmit('endAction', event.target.value)}
|
||||||
|
variant='ontime'
|
||||||
|
>
|
||||||
|
<option value={EndAction.Continue}>Continue</option>
|
||||||
|
<option value={EndAction.Stop}>Stop</option>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(EventEditorTimes);
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
import SwatchSelect from '../../../common/components/input/colour-input/SwatchSelect';
|
||||||
|
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||||
|
|
||||||
|
import CountedTextArea from './CountedTextArea';
|
||||||
|
import CountedTextInput from './CountedTextInput';
|
||||||
|
|
||||||
|
import style from '../EventEditor.module.scss';
|
||||||
|
|
||||||
|
interface EventEditorTitlesProps {
|
||||||
|
eventId: string;
|
||||||
|
title: string;
|
||||||
|
presenter: string;
|
||||||
|
subtitle: string;
|
||||||
|
note: string;
|
||||||
|
colour: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TitleActions = 'title' | 'presenter' | 'subtitle' | 'note' | 'colour';
|
||||||
|
|
||||||
|
const EventEditorTitles = (props: EventEditorTitlesProps) => {
|
||||||
|
const { eventId, title, presenter, subtitle, note, colour } = props;
|
||||||
|
const { updateEvent } = useEventAction();
|
||||||
|
|
||||||
|
const handleSubmit = (field: TitleActions, value: string) => {
|
||||||
|
updateEvent({ id: eventId, [field]: value });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style.titles}>
|
||||||
|
<div className={style.left}>
|
||||||
|
<CountedTextInput field='title' label='Title' initialValue={title} submitHandler={handleSubmit} />
|
||||||
|
<CountedTextInput field='presenter' label='Presenter' initialValue={presenter} submitHandler={handleSubmit} />
|
||||||
|
<CountedTextInput field='subtitle' label='Subtitle' initialValue={subtitle} submitHandler={handleSubmit} />
|
||||||
|
</div>
|
||||||
|
<div className={style.right}>
|
||||||
|
<div className={style.column}>
|
||||||
|
<label className={style.inputLabel}>Colour</label>
|
||||||
|
<div className={style.inline}>
|
||||||
|
<SwatchSelect name='colour' value={colour} handleChange={handleSubmit} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CountedTextArea field='note' label='Note' initialValue={note} submitHandler={handleSubmit} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(EventEditorTitles);
|
||||||
Reference in New Issue
Block a user