mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-08 07:49:12 +00:00
feat(rundown): edit multiple events at once in the entry editor
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
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { OntimeEntry, isOntimeEvent, isOntimeGroup, isOntimeMilestone } from 'ontime-types';
|
||||
import { OntimeEntry, OntimeEvent, isOntimeEvent, isOntimeGroup, isOntimeMilestone } from 'ontime-types';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import useRundown from '../../../common/hooks-query/useRundown';
|
||||
@@ -15,6 +15,25 @@ 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;
|
||||
@@ -29,19 +48,20 @@ export default function RundownEntryEditor() {
|
||||
return event ?? null;
|
||||
}, [data.order.length, data.entries, selectedEvents]);
|
||||
|
||||
if (!entry) {
|
||||
return <EventEditorEmpty />;
|
||||
}
|
||||
|
||||
if (isOntimeEvent(entry)) {
|
||||
if (events.length > 0) {
|
||||
const singleEvent = events.length === 1 ? events[0] : null;
|
||||
return (
|
||||
<div className={style.rundownEditor} data-testid='editor-container'>
|
||||
<EventEditor event={entry} />
|
||||
<EventEditorFooter id={entry.id} cue={entry.cue} />
|
||||
<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'>
|
||||
|
||||
Reference in New Issue
Block a user