From 9ec47eda6eb1493df000200f1b9de1451dc3509e Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Mon, 4 Aug 2025 06:20:06 +0200 Subject: [PATCH] feat: expose block target duration in header --- apps/client/src/common/utils/time.ts | 3 +-- .../rundown-block/RundownBlock.module.scss | 12 ++++++++- .../rundown/rundown-block/RundownBlock.tsx | 25 ++++++++++++++++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/apps/client/src/common/utils/time.ts b/apps/client/src/common/utils/time.ts index d85ddc176..c321ecbd9 100644 --- a/apps/client/src/common/utils/time.ts +++ b/apps/client/src/common/utils/time.ts @@ -105,8 +105,7 @@ export const formatTime = ( /** * Handles case for formatting a duration time - * @param duration - * @returns + * eg: "1h 0m 0s" or "0h 30m" */ export function formatDuration(duration: number, hideSeconds = true): string { // durations should never be negative, we handle it here to flag if there is an issue in future diff --git a/apps/client/src/features/rundown/rundown-block/RundownBlock.module.scss b/apps/client/src/features/rundown/rundown-block/RundownBlock.module.scss index 6c791427e..eb0303c97 100644 --- a/apps/client/src/features/rundown/rundown-block/RundownBlock.module.scss +++ b/apps/client/src/features/rundown/rundown-block/RundownBlock.module.scss @@ -32,7 +32,7 @@ &:focus { outline: 1px solid $blue-500; - outline-offset: -1px; + outline-offset: -1px; } } @@ -68,6 +68,16 @@ } } +.strike { + text-decoration: line-through; +} +.over { + color: $playback-over; +} +.under { + color: $playback-under; +} + .drag { @include drag-style; position: absolute; diff --git a/apps/client/src/features/rundown/rundown-block/RundownBlock.tsx b/apps/client/src/features/rundown/rundown-block/RundownBlock.tsx index 23d2e4681..f6be80ee3 100644 --- a/apps/client/src/features/rundown/rundown-block/RundownBlock.tsx +++ b/apps/client/src/features/rundown/rundown-block/RundownBlock.tsx @@ -14,6 +14,7 @@ import { EntryId, OntimeBlock } from 'ontime-types'; import IconButton from '../../../common/components/buttons/IconButton'; import { useContextMenu } from '../../../common/hooks/useContextMenu'; import { useEntryActions } from '../../../common/hooks/useEntryAction'; +import { getOffsetState } from '../../../common/utils/offset'; import { cx, getAccessibleColour } from '../../../common/utils/styleUtils'; import { formatDuration, formatTime } from '../../../common/utils/time'; import EditableBlockTitle from '../common/EditableBlockTitle'; @@ -91,6 +92,19 @@ export default function RundownBlock({ data, hasCursor, collapsed, onCollapse }: const binderColours = data.colour && getAccessibleColour(data.colour); const isValidDrop = over?.id && canDrop(over.data.current?.type, over.data.current?.parent); + const [planOffset, planOffsetLabel] = (() => { + if (data.targetDuration === null) { + return [null, null]; + } + + const offset = data.duration - data.targetDuration; + if (offset === 0) { + return [null, 'under']; + } + + return [offset < 0 ? `-${formatDuration(offset * -1)}` : `+${formatDuration(offset)}`, getOffsetState(offset * -1)]; + })(); + const dragStyle = { zIndex: isDragging ? 2 : 'inherit', transform: CSS.Translate.toString(transform), @@ -139,7 +153,16 @@ export default function RundownBlock({ data, hasCursor, collapsed, onCollapse }:
Duration
-
{formatDuration(data.duration)}
+ {planOffset === null ? ( +
+ {formatDuration(data.duration)} +
+ ) : ( +
+ {formatDuration(data.duration)} + {planOffset} +
+ )}
Entries