From 3ce5b42e05c63ee6dee7b702bd82bfe2b1734941 Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Thu, 3 Jul 2025 17:24:34 +0200 Subject: [PATCH] refactor: prepare rundown loading process --- apps/client/src/common/api/constants.ts | 1 + .../common/hooks-query/useProjectRundowns.ts | 22 ++++ .../panel/project-panel/ManageRundowns.tsx | 111 ++++++++++++++++++ .../project-panel/ProjectPanel.module.scss | 10 +- .../panel/project-panel/ProjectPanel.tsx | 9 +- .../app-settings/useAppSettingsMenu.tsx | 3 +- apps/client/src/ontimeConfig.ts | 4 +- .../src/api-data/rundown/rundown.router.ts | 12 +- .../BackendResponse.type.ts | 9 +- 9 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 apps/client/src/common/hooks-query/useProjectRundowns.ts create mode 100644 apps/client/src/features/app-settings/panel/project-panel/ManageRundowns.tsx diff --git a/apps/client/src/common/api/constants.ts b/apps/client/src/common/api/constants.ts index a5968445c..a08118089 100644 --- a/apps/client/src/common/api/constants.ts +++ b/apps/client/src/common/api/constants.ts @@ -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']; diff --git a/apps/client/src/common/hooks-query/useProjectRundowns.ts b/apps/client/src/common/hooks-query/useProjectRundowns.ts new file mode 100644 index 000000000..db796b6d1 --- /dev/null +++ b/apps/client/src/common/hooks-query/useProjectRundowns.ts @@ -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({ + 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 }; +} diff --git a/apps/client/src/features/app-settings/panel/project-panel/ManageRundowns.tsx b/apps/client/src/features/app-settings/panel/project-panel/ManageRundowns.tsx new file mode 100644 index 000000000..d27d7a820 --- /dev/null +++ b/apps/client/src/features/app-settings/panel/project-panel/ManageRundowns.tsx @@ -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 ( + <> + + + + Manage project rundowns + + + + + + + + + # Entries + Title + + + + + {data.rundowns.map((rundown) => { + const isLoaded = data.loaded === rundown.id; + return ( + + {rundown.numEntries} + {`${rundown.title}${isLoaded && ' (loaded)'}`} + + + + + + ); + })} + + + + + + You will lose all data in your rundown.
Are you sure? + + } + footerElements={ + <> + + + + } + /> + + The current playback will be stopped.
Are you sure? + + } + footerElements={ + <> + + + + } + /> + + ); +} diff --git a/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.module.scss b/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.module.scss index 1e9be473a..663b7f9f6 100644 --- a/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.module.scss +++ b/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.module.scss @@ -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; diff --git a/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.tsx b/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.tsx index abfdd55e9..502d10d0b 100644 --- a/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.tsx +++ b/apps/client/src/features/app-settings/panel/project-panel/ProjectPanel.tsx @@ -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('data', location); - const manageRef = useScrollIntoView('manage', location); + const manageRundownsRef = useScrollIntoView('rundowns', location); + const manageProjectsRef = useScrollIntoView('list', location); const handleQuickClose = () => { setLocation('project'); @@ -26,7 +28,10 @@ export default function ProjectPanel({ location, setLocation }: ProjectPanelProp
-
+
+ +
+
diff --git a/apps/client/src/features/app-settings/useAppSettingsMenu.tsx b/apps/client/src/features/app-settings/useAppSettingsMenu.tsx index 56f3d9d99..c9fcbd957 100644 --- a/apps/client/src/features/app-settings/useAppSettingsMenu.tsx +++ b/apps/client/src/features/app-settings/useAppSettingsMenu.tsx @@ -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' }, ], }, { diff --git a/apps/client/src/ontimeConfig.ts b/apps/client/src/ontimeConfig.ts index 760d8f306..3883a352c 100644 --- a/apps/client/src/ontimeConfig.ts +++ b/apps/client/src/ontimeConfig.ts @@ -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 diff --git a/apps/server/src/api-data/rundown/rundown.router.ts b/apps/server/src/api-data/rundown/rundown.router.ts index 624034f7b..692c41991 100644 --- a/apps/server/src/api-data/rundown/rundown.router.ts +++ b/apps/server/src/api-data/rundown/rundown.router.ts @@ -37,7 +37,17 @@ router.get('/', async (_req: Request, res: Response) => { 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, + }, + ], + }); }); /** diff --git a/packages/types/src/api/rundown-controller/BackendResponse.type.ts b/packages/types/src/api/rundown-controller/BackendResponse.type.ts index 377da48d0..3247db302 100644 --- a/packages/types/src/api/rundown-controller/BackendResponse.type.ts +++ b/packages/types/src/api/rundown-controller/BackendResponse.type.ts @@ -12,9 +12,14 @@ export type TransientEventPayload = Partial & { before?: string; }; -export type ProjectRundownsList = { +export type ProjectRundown = { id: string; title: string; numEntries: number; revision: number; -}[]; +}; + +export type ProjectRundownsList = { + loaded: string; + rundowns: ProjectRundown[]; +};