feat: auto cue re-numbering (#2016)

* feat: update auto cue numbering

* feat: renumber from ui

* refactor: patchEntries is not used

* chore: format

* fix: correct cue at top of group

* fix: handle precision

* refactor dialog

* bump limit for performance time test

* extract type

* add class name to lable

* fix rebase

* refator: extract renumering logic

* chore: comments for getIntegerAndFraction function

* chore: add the for renumber mutation

* fix: fraction match precision

* refactor: small cleanup

* refactor: use more narrow validator

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
This commit is contained in:
Alex Christoffer Rasmussen
2026-05-03 18:39:00 +02:00
committed by GitHub
parent ba1c3235b3
commit 1a08b39b8b
20 changed files with 554 additions and 144 deletions
+16 -1
View File
@@ -1,5 +1,13 @@
import axios, { AxiosResponse } from 'axios';
import { EntryId, OntimeEntry, OntimeEvent, ProjectRundownsList, Rundown, TransientEventPayload } from 'ontime-types';
import {
EntryId,
OntimeEntry,
OntimeEvent,
ProjectRundownsList,
RenumberCues,
Rundown,
TransientEventPayload,
} from 'ontime-types';
import { apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
@@ -111,6 +119,13 @@ export async function putBatchEditEvents(rundownId: RundownId, data: BatchEditEn
return axios.put(`${rundownPath}/${rundownId}/batch`, data);
}
/**
* HTTP request to renumber cues for multiple events
*/
export function patchRenumberCues(rundownId: RundownId, data: RenumberCues): Promise<AxiosResponse<Rundown>> {
return axios.patch(`${rundownPath}/${rundownId}/renumber`, data);
}
export type ReorderEntry = {
entryId: EntryId;
destinationId: EntryId;
@@ -41,6 +41,7 @@ import {
patchReorderEntry,
postAddEntry,
postCloneEntry,
patchRenumberCues,
putBatchEditEvents,
putEditEntry,
requestApplyDelay,
@@ -523,6 +524,39 @@ export const useEntryActions = () => {
[batchUpdateEventsMutation, getCurrentRundownData],
);
const { mutateAsync: renumberCuesMutation } = useMutation({
mutationFn: ([rundownId, body]: Parameters<typeof patchRenumberCues>) => patchRenumberCues(rundownId, body),
onMutate: async () => {
const queryKey = resolveCurrentRundownQueryKey();
await queryClient.cancelQueries({ queryKey });
const previousRundown = queryClient.getQueryData<Rundown>(queryKey);
return { previousRundown, queryKey };
},
onSuccess: (response, _variables, context) => {
if (!response.data || !context?.queryKey) return;
const updatedRundown = response.data;
queryClient.setQueryData<Rundown>(context.queryKey, updatedRundown);
},
onError: (_error, _vars, context) => {
if (context?.previousRundown) queryClient.setQueryData<Rundown>(context.queryKey, context.previousRundown);
},
});
const renumberCues = useCallback(
async (eventIds: EntryId[], prefix: string, start: string, increment: string) => {
const rundown = getCurrentRundownData();
const rundownId = rundown?.id;
if (!rundownId) throw new Error('Rundown not initialized');
try {
await renumberCuesMutation([rundownId, { ids: eventIds, prefix, start, increment }]);
} catch (error) {
logAxiosError('Error renumbering cues', error);
throw error;
}
},
[getCurrentRundownData, renumberCuesMutation],
);
/**
* Calls mutation to delete an entry
* @private
@@ -947,6 +981,7 @@ export const useEntryActions = () => {
groupEntries,
move,
reorderEntry,
renumberCues,
swapEvents,
updateEntry,
updateTimer,
@@ -963,6 +998,7 @@ export const useEntryActions = () => {
groupEntries,
move,
reorderEntry,
renumberCues,
swapEvents,
updateEntry,
updateTimer,