mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 12:53:32 +00:00
b981bae63f
Selecting several events in the editor now turns the entry editor into a multi editor: fields shared by every selected event show their value, fields which differ show as mixed, and editing a field applies it to the whole selection through the existing batch endpoint. Rather than branching between a single and a multi editor, the editor now always renders a merged view of N events (N >= 1) where a field is undefined when the events disagree. For a single event nothing is ever undefined, so single event editing is unchanged by construction and the composites carry no multi edit branching. - add mergeEvents() to build the merged view over a selection - EventEditor takes an events array and resolves the write path itself, replacing the eventId threaded into the composites with a submit callback - extract EventEditorSchedule, which is only rendered for a single event since schedule values cascade through the rundown - support indeterminate values in Switch, Select, SwatchSelect and TimeInput - cue, event id, schedule and automations are not available in multi edit Two related fixes which multi edit depends on: - useReactiveTextInput submitted an empty string when the initial value was undefined, so leaving a mixed field without editing it would have wiped the value on every selected event - the batch optimistic update spread the request body onto the entry instead of the patch, and replaced custom fields rather than merging them Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PDETGBrhwgAqgTHjmrmJGJ
83 lines
2.3 KiB
TypeScript
83 lines
2.3 KiB
TypeScript
import { OntimeEntry, OntimeEvent, isOntimeEvent, isOntimeGroup, isOntimeMilestone } from 'ontime-types';
|
|
import { useMemo } from 'react';
|
|
|
|
import useRundown from '../../../common/hooks-query/useRundown';
|
|
import { useEventSelection } from '../useEventSelection';
|
|
import EventEditorFooter from './composite/EventEditorFooter';
|
|
import EventEditor from './EventEditor';
|
|
import EventEditorEmpty from './EventEditorEmpty';
|
|
import GroupEditor from './GroupEditor';
|
|
import MilestoneEditor from './MilestoneEditor';
|
|
|
|
import style from './EntryEditor.module.scss';
|
|
|
|
export default function RundownEntryEditor() {
|
|
const selectedEvents = useEventSelection((state) => state.selectedEvents);
|
|
const { data } = useRundown();
|
|
|
|
/**
|
|
* Events in the current selection
|
|
* Only events can be multi selected, groups and milestones are always selected on their own
|
|
*/
|
|
const events = useMemo<OntimeEvent[]>(() => {
|
|
if (data.order.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
const selection: OntimeEvent[] = [];
|
|
selectedEvents.forEach((entryId) => {
|
|
const entry = data.entries[entryId];
|
|
if (isOntimeEvent(entry)) {
|
|
selection.push(entry);
|
|
}
|
|
});
|
|
return selection;
|
|
}, [data.order.length, data.entries, selectedEvents]);
|
|
|
|
const entry = useMemo<OntimeEntry | null>(() => {
|
|
if (data.order.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const selectedEventId = Array.from(selectedEvents).at(0);
|
|
if (!selectedEventId) {
|
|
return null;
|
|
}
|
|
|
|
const event = data.entries[selectedEventId];
|
|
return event ?? null;
|
|
}, [data.order.length, data.entries, selectedEvents]);
|
|
|
|
if (events.length > 0) {
|
|
const singleEvent = events.length === 1 ? events[0] : null;
|
|
return (
|
|
<div className={style.rundownEditor} data-testid='editor-container'>
|
|
<EventEditor events={events} />
|
|
{singleEvent && <EventEditorFooter id={singleEvent.id} cue={singleEvent.cue} />}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!entry) {
|
|
return <EventEditorEmpty />;
|
|
}
|
|
|
|
if (isOntimeMilestone(entry)) {
|
|
return (
|
|
<div className={style.rundownEditor} data-testid='editor-container'>
|
|
<MilestoneEditor milestone={entry} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isOntimeGroup(entry)) {
|
|
return (
|
|
<div className={style.rundownEditor} data-testid='editor-container'>
|
|
<GroupEditor group={entry} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|