feat: add group metadata to overview

This commit is contained in:
Carlos Valente
2025-07-07 13:59:41 +02:00
committed by Carlos Valente
parent 17a7d035bf
commit a7ae8598db
4 changed files with 88 additions and 18 deletions
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { OntimeEntry, Rundown } from 'ontime-types';
import { EntryId, OntimeEntry, Rundown } from 'ontime-types';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { RUNDOWN } from '../api/constants';
@@ -78,3 +78,18 @@ export function usePartialRundown(cb: (event: OntimeEntry) => boolean) {
return { data: filteredData, status };
}
/**
* Hook to get a specific entry by ID from the rundown
*/
export function useEntry(entryId: EntryId | null): OntimeEntry | null {
const { data: rundown } = useRundown();
// track the specific entry we care about
const entry = useMemo(() => {
if (entryId === null) return null;
return rundown.entries[entryId];
}, [entryId, rundown.entries]);
return entry;
}
@@ -3,7 +3,13 @@ import { memo, PropsWithChildren, useMemo } from 'react';
import { useIsMobileScreen } from '../../common/hooks/useIsMobileScreen';
import { useRuntimeOverview } from '../../common/hooks/useSocket';
import { CurrentBlockOverview, OverviewWrapper, RuntimeOverview, TimerOverview } from './composite/OverviewWrapper';
import {
ClockOverview,
CurrentBlockOverview,
OverviewWrapper,
RuntimeOverview,
TimerOverview,
} from './composite/OverviewWrapper';
import { TimeRow } from './composite/TimeLayout';
import { calculateEndAndDaySpan, formatedTime } from './overviewUtils';
@@ -51,8 +57,9 @@ function CuesheetDesktop({ children }: PropsWithChildren) {
/>
</div>
<TimerOverview />
<CurrentBlockOverview />
<RuntimeOverview />
<CurrentBlockOverview />
<ClockOverview />
<div>
<TimeRow
label='Planned end'
@@ -3,6 +3,7 @@ import { memo, PropsWithChildren, useMemo } from 'react';
import { useRuntimeOverview } from '../../common/hooks/useSocket';
import {
ClockOverview,
CurrentBlockOverview,
OverviewWrapper,
ProgressOverview,
@@ -42,8 +43,9 @@ function EditorOverview({ children }: PropsWithChildren) {
/>
</div>
<ProgressOverview />
<CurrentBlockOverview />
<RuntimeOverview />
<CurrentBlockOverview />
<ClockOverview />
<div>
<TimeRow
label='Planned end'
@@ -1,13 +1,21 @@
import { PropsWithChildren, ReactNode } from 'react';
import { ErrorBoundary } from '@sentry/react';
import { isOntimeBlock } from 'ontime-types';
import { isPlaybackActive, millisToString } from 'ontime-utils';
import { useIsOnline, useRuntimePlaybackOverview, useTimer } from '../../../common/hooks/useSocket';
import {
useClock,
useCurrentBlockId,
useIsOnline,
useRuntimePlaybackOverview,
useTimer,
} from '../../../common/hooks/useSocket';
import useProjectData from '../../../common/hooks-query/useProjectData';
import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils';
import { useEntry } from '../../../common/hooks-query/useRundown';
import { cx, enDash, timerPlaceholder, timerPlaceholderMin } from '../../../common/utils/styleUtils';
import { formatedTime, getOffsetText } from '../overviewUtils';
import { TimeColumn } from './TimeLayout';
import { TimeColumn, TimeRow } from './TimeLayout';
import style from '../Overview.module.scss';
@@ -44,10 +52,48 @@ export function TitlesOverview() {
export function CurrentBlockOverview() {
const { blockStartedAt: blockStartAt, clock } = useRuntimePlaybackOverview();
const { currentBlockId } = useCurrentBlockId();
const entry = useEntry(currentBlockId);
const timeInBlock = formatedTime(blockStartAt ? clock - blockStartAt : null);
const timeInBlock = formatedTime(blockStartAt ? clock - blockStartAt : null, 2);
return <TimeColumn label='Time in block' value={timeInBlock} className={style.clock} muted={blockStartAt === null} />;
/**
* The time to the end of the block
* as scheduled
* TODO(v4): this needs to be calculated according to offset mode
*/
const blockEnd = (() => {
if (!entry) return timerPlaceholderMin;
if (!isOntimeBlock(entry)) return timerPlaceholderMin;
if (entry.timeEnd === null) return timerPlaceholderMin;
return formatedTime(entry.timeEnd - clock, 2);
})();
/**
* The time to the end of the block
* as projected accounting for delays and offset
* TODO(v4): this needs to be calculated according to offset mode
*/
const projectedBlockEnd = (() => {
if (blockStartAt === null || !entry) return timerPlaceholderMin;
if (!isOntimeBlock(entry)) return timerPlaceholderMin;
return formatedTime(blockStartAt + entry.duration - clock, 2);
})();
return (
<>
<TimeColumn label='Elapsed in block' value={timeInBlock} className={style.clock} muted={blockStartAt === null} />
<div>
<TimeRow label='Block end' value={blockEnd} className={style.end} muted={blockStartAt === null} />
<TimeRow
label='Projected block end'
value={projectedBlockEnd}
className={style.end}
muted={blockStartAt === null}
/>
</div>
</>
);
}
export function TimerOverview() {
@@ -62,23 +108,23 @@ export function ProgressOverview() {
const { numEvents, selectedEventIndex } = useRuntimePlaybackOverview();
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
const ofTotal = numEvents || enDash;
const progressText = numEvents ? `${current} of ${ofTotal}` : '-';
const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash;
return <TimeColumn label='Progress' value={progressText} />;
}
export function RuntimeOverview() {
const { clock, offset, playback } = useRuntimePlaybackOverview();
const { offset, playback } = useRuntimePlaybackOverview();
const isPlaying = isPlaybackActive(playback);
const offsetText = getOffsetText(isPlaying ? offset : null);
const offsetClasses = cx([style.offset, isPlaying && (offset < 0 ? style.behind : style.ahead)]);
return (
<>
<TimeColumn label='Offset' value={offsetText} className={offsetClasses} testId='offset' />
<TimeColumn label='Time now' value={formatedTime(clock)} />
</>
);
return <TimeColumn label='Offset' value={offsetText} className={offsetClasses} testId='offset' />;
}
export function ClockOverview() {
const { clock } = useClock();
return <TimeColumn label='Time now' value={formatedTime(clock)} />;
}