refactor: prepare rundown loading process

This commit is contained in:
Carlos Valente
2025-07-03 17:24:34 +02:00
committed by Carlos Valente
parent c6c1e4a5d7
commit 3ce5b42e05
9 changed files with 171 additions and 10 deletions
+1
View File
@@ -8,6 +8,7 @@ export const AUTOMATION = ['automation'];
export const CUSTOM_FIELDS = ['customFields'];
export const PROJECT_DATA = ['project'];
export const PROJECT_LIST = ['projectList'];
export const PROJECT_RUNDOWNS = ['projectRundowns'];
export const RUNDOWN = ['rundown'];
export const RUNTIME = ['runtimeStore'];
export const URL_PRESETS = ['urlpresets'];
@@ -0,0 +1,22 @@
import { useQuery } from '@tanstack/react-query';
import { ProjectRundownsList } from 'ontime-types';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { PROJECT_RUNDOWNS } from '../api/constants';
import { fetchProjectRundownList } from '../api/rundown';
/**
* Project rundowns
*/
export function useProjectRundowns() {
const { data, status, isError, refetch, isFetching } = useQuery<ProjectRundownsList>({
queryKey: PROJECT_RUNDOWNS,
queryFn: fetchProjectRundownList,
placeholderData: (previousData, _previousQuery) => previousData,
retry: 5,
retryDelay: (attempt) => attempt * 2500,
refetchInterval: queryRefetchIntervalSlow,
networkMode: 'always',
});
return { data: data ?? { loaded: '', rundowns: [] }, status, isError, refetch, isFetching };
}
@@ -0,0 +1,111 @@
import { IoAdd } from 'react-icons/io5';
import { useDisclosure } from '@mantine/hooks';
import Button from '../../../../common/components/buttons/Button';
import Dialog from '../../../../common/components/dialog/Dialog';
import { useProjectRundowns } from '../../../../common/hooks-query/useProjectRundowns';
import { cx } from '../../../../common/utils/styleUtils';
import * as Panel from '../../panel-utils/PanelUtils';
import style from './ProjectPanel.module.scss';
export default function ManageRundowns() {
const { data } = useProjectRundowns();
const [deleteOpen, deleteHandlers] = useDisclosure();
const [loadOpen, loadHandlers] = useDisclosure();
return (
<>
<Panel.Section>
<Panel.Card>
<Panel.SubHeader>
Manage project rundowns
<Panel.InlineElements>
<Button onClick={() => undefined} disabled>
New <IoAdd />
</Button>
</Panel.InlineElements>
</Panel.SubHeader>
<Panel.Divider />
<Panel.Table>
<thead>
<tr>
<th># Entries</th>
<th style={{ width: '100%' }}>Title</th>
<th />
</tr>
</thead>
<tbody>
{data.rundowns.map((rundown) => {
const isLoaded = data.loaded === rundown.id;
return (
<tr key={rundown.id} className={cx([isLoaded && style.current])}>
<td>{rundown.numEntries}</td>
<td>{`${rundown.title}${isLoaded && ' (loaded)'}`}</td>
<Panel.InlineElements as='td'>
<Button size='small' onClick={() => loadHandlers.open()} disabled={isLoaded}>
Load
</Button>
<Button
size='small'
variant='subtle-destructive'
onClick={() => deleteHandlers.open()}
disabled={isLoaded}
>
Delete
</Button>
</Panel.InlineElements>
</tr>
);
})}
</tbody>
</Panel.Table>
</Panel.Card>
</Panel.Section>
<Dialog
isOpen={deleteOpen}
onClose={deleteHandlers.close}
title='Load rundown'
showBackdrop
showCloseButton
bodyElements={
<>
You will lose all data in your rundown. <br /> Are you sure?
</>
}
footerElements={
<>
<Button size='large' onClick={deleteHandlers.close}>
Cancel
</Button>
<Button variant='destructive' size='large' onClick={() => undefined}>
Delete rundown
</Button>
</>
}
/>
<Dialog
isOpen={loadOpen}
onClose={loadHandlers.close}
title='Delete rundown'
showBackdrop
showCloseButton
bodyElements={
<>
The current playback will be stopped. <br /> Are you sure?
</>
}
footerElements={
<>
<Button size='large' onClick={loadHandlers.close}>
Cancel
</Button>
<Button variant='primary' size='large' onClick={() => undefined}>
Load rundown
</Button>
</>
}
/>
</>
);
}
@@ -26,6 +26,10 @@
max-width: 400px;
}
.fullWidth {
width: 100%;
}
.innerColumn {
margin: 0 2rem;
margin-bottom: 2rem;
@@ -59,9 +63,11 @@
}
.customDataItem {
display: contents;
width: 100%;
display: flex;
flex-direction: column;
gap: 0.5rem;
.titleRow{
display: flex;
gap: 1rem;
@@ -5,6 +5,7 @@ import QuickStart from '../../quick-start/QuickStart';
import type { SettingsOptionId } from '../../useAppSettingsMenu';
import ManageProjects from './ManageProjects';
import ManageRundowns from './ManageRundowns';
import ProjectData from './ProjectData';
interface ProjectPanelProps extends PanelBaseProps {
@@ -13,7 +14,8 @@ interface ProjectPanelProps extends PanelBaseProps {
export default function ProjectPanel({ location, setLocation }: ProjectPanelProps) {
const projectRef = useScrollIntoView<HTMLDivElement>('data', location);
const manageRef = useScrollIntoView<HTMLDivElement>('manage', location);
const manageRundownsRef = useScrollIntoView<HTMLDivElement>('rundowns', location);
const manageProjectsRef = useScrollIntoView<HTMLDivElement>('list', location);
const handleQuickClose = () => {
setLocation('project');
@@ -26,7 +28,10 @@ export default function ProjectPanel({ location, setLocation }: ProjectPanelProp
<div ref={projectRef}>
<ProjectData />
</div>
<div ref={manageRef}>
<div ref={manageRundownsRef}>
<ManageRundowns />
</div>
<div ref={manageProjectsRef}>
<ManageProjects />
</div>
</>
@@ -17,7 +17,8 @@ const staticOptions = [
secondary: [
{ id: 'project__create', label: 'Create...' },
{ id: 'project__data', label: 'Project data' },
{ id: 'project__manage', label: 'Manage projects' },
{ id: 'project__rundowns', label: 'Manage rundowns' },
{ id: 'project__list', label: 'Manage projects' },
],
},
{
+2 -2
View File
@@ -2,5 +2,5 @@ export const tooltipDelaySlow = 1000;
export const tooltipDelayMid = 500;
export const tooltipDelayFast = 300;
export const queryRefetchInterval = 10000; // 10 seconds
export const queryRefetchIntervalSlow = 30000; // 30 seconds
export const queryRefetchInterval = 60000; // 60 seconds
export const queryRefetchIntervalSlow = 120000; // 120 seconds
@@ -37,7 +37,17 @@ router.get('/', async (_req: Request, res: Response<ProjectRundownsList>) => {
const rundown = getCurrentRundown();
// TODO: we currently make a project with only the current rundown
res.json([{ id: rundown.id, title: rundown.title, numEntries: rundown.order.length, revision: rundown.revision }]);
res.json({
loaded: rundown.id,
rundowns: [
{
id: rundown.id,
title: rundown.title,
numEntries: rundown.order.length,
revision: rundown.revision,
},
],
});
});
/**
@@ -12,9 +12,14 @@ export type TransientEventPayload = Partial<OntimeEntry> & {
before?: string;
};
export type ProjectRundownsList = {
export type ProjectRundown = {
id: string;
title: string;
numEntries: number;
revision: number;
}[];
};
export type ProjectRundownsList = {
loaded: string;
rundowns: ProjectRundown[];
};