diff --git a/apps/client/src/common/utils/offset.ts b/apps/client/src/common/utils/offset.ts index 37b08a32b..3555bd0a7 100644 --- a/apps/client/src/common/utils/offset.ts +++ b/apps/client/src/common/utils/offset.ts @@ -18,8 +18,8 @@ export function getOffsetText(offset: MaybeNumber): string { return offsetText; } -export function getOffsetState(offset: MaybeNumber): 'ahead' | 'behind' | 'muted' | null { +export function getOffsetState(offset: MaybeNumber): 'over' | 'under' | 'muted' | null { if (offset === null) return null; if (offset === 0) return 'muted'; - return offset < 0 ? 'behind' : 'ahead'; + return offset < 0 ? 'over' : 'under'; } diff --git a/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.module.scss b/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.module.scss index de8289546..a85b4b5fe 100644 --- a/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.module.scss +++ b/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.module.scss @@ -1,7 +1,7 @@ th.over { - color: $ontime-delay-text; + color: $playback-over; } th.under { - color: $playback-ahead; + color: $playback-under; } diff --git a/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.tsx b/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.tsx index f7b25eae7..b1ba675e6 100644 --- a/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.tsx +++ b/apps/client/src/features/app-settings/panel/feature-panel/ReportSettings.tsx @@ -29,8 +29,8 @@ export default function ReportSettings() { }; const combinedReport = useMemo(() => { - return getCombinedReport(reportData, data.entries, data.order); - }, [reportData, data.entries, data.order]); + return getCombinedReport(reportData, data.entries, data.flatOrder); + }, [reportData, data.entries, data.flatOrder]); return ( @@ -82,7 +82,7 @@ export default function ReportSettings() { return 'over'; })(); return ( - + {entry.index} {entry.cue} {entry.title} diff --git a/apps/client/src/features/app-settings/panel/feature-panel/reportSettings.utils.ts b/apps/client/src/features/app-settings/panel/feature-panel/reportSettings.utils.ts index d47ee4f9a..11b25c1b0 100644 --- a/apps/client/src/features/app-settings/panel/feature-panel/reportSettings.utils.ts +++ b/apps/client/src/features/app-settings/panel/feature-panel/reportSettings.utils.ts @@ -4,6 +4,7 @@ import { makeCSVFromArrayOfArrays } from '../../../../common/utils/csv'; import { formatTime } from '../../../../common/utils/time'; export type CombinedReport = { + id: EntryId; index: number; title: string; cue: string; @@ -16,24 +17,48 @@ export type CombinedReport = { /** * Creates a combined report with the rundown data */ -export function getCombinedReport(report: OntimeReport, rundown: RundownEntries, order: EntryId[]): CombinedReport[] { +export function getCombinedReport( + report: OntimeReport, + rundown: RundownEntries, + flatOrder: EntryId[], +): CombinedReport[] { if (Object.keys(report).length === 0) return []; - if (order.length === 0) return []; + if (flatOrder.length === 0) return []; const combinedReport: CombinedReport[] = []; - for (const [key, value] of Object.entries(report)) { - if (!rundown[key] || !isOntimeEvent(rundown[key])) continue; + let index = 1; + for (let i = 0; i < flatOrder.length; i++) { + const id = flatOrder[i]; + const entry = rundown[id]; + if (!entry || !isOntimeEvent(entry)) continue; - combinedReport.push({ - index: order.findIndex((id) => id === key), - title: rundown[key].title, - cue: rundown[key].cue, - scheduledStart: rundown[key].timeStart, - actualEnd: value.endedAt, - scheduledEnd: rundown[key].timeEnd, - actualStart: value.startedAt, - }); + if (!(id in report)) { + combinedReport.push({ + id: id, + index: index, + title: entry.title, + cue: entry.cue, + scheduledStart: entry.timeStart, + actualEnd: null, + scheduledEnd: entry.timeEnd, + actualStart: null, + }); + } + + if (id in report) { + combinedReport.push({ + id: id, + index: index, + title: entry.title, + cue: entry.cue, + scheduledStart: entry.timeStart, + actualEnd: report[id].endedAt, + scheduledEnd: entry.timeEnd, + actualStart: report[id].startedAt, + }); + } + index++; } return combinedReport; diff --git a/apps/client/src/features/overview/composite/TimeElements.module.scss b/apps/client/src/features/overview/composite/TimeElements.module.scss index c944e17ec..2b9612b9f 100644 --- a/apps/client/src/features/overview/composite/TimeElements.module.scss +++ b/apps/client/src/features/overview/composite/TimeElements.module.scss @@ -55,18 +55,6 @@ color: $muted-gray; } -.offset { - color: $muted-gray; -} - -.ahead { - color: $playback-ahead; -} - -.behind { - color: $ontime-delay-text; -} - .labelTitle { white-space: nowrap; overflow: hidden; diff --git a/apps/client/src/features/overview/composite/TimeElements.tsx b/apps/client/src/features/overview/composite/TimeElements.tsx index c11716610..4a2c3d659 100644 --- a/apps/client/src/features/overview/composite/TimeElements.tsx +++ b/apps/client/src/features/overview/composite/TimeElements.tsx @@ -18,17 +18,18 @@ import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils'; import { formatTime, useTimeUntilStart } from '../../../common/utils/time'; import { calculateEndAndDaySpan, formattedTime } from '../overview.utils'; -import { TimeColumn } from './TimeLayout'; +import { OverUnder, TimeColumn } from './TimeLayout'; import style from './TimeElements.module.scss'; export function StartTimes() { const { plannedEnd, plannedStart, actualStart, expectedEnd } = useRuntimeOverview(); + + const plannedStartText = plannedStart === null ? timerPlaceholder : formatTime(plannedStart); + const [maybePlannedEnd, maybePlannedDaySpan] = useMemo(() => calculateEndAndDaySpan(plannedEnd), [plannedEnd]); - const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]); - - const muted = maybeExpectedEnd === null; + const plannedEndText = maybePlannedEnd === null ? timerPlaceholder : formatTime(maybePlannedEnd); return (
@@ -36,31 +37,31 @@ export function StartTimes() { Start
} /> - {formatTime(plannedStart)} + {plannedStartText}
} /> - {formattedTime(actualStart)} + {formattedTime(actualStart)}
End
} /> - {maybePlannedDaySpan >= 0 ? ( + {maybePlannedDaySpan > 0 ? ( } > - {formatTime(maybePlannedEnd)} + {plannedEndText} ) : ( - {formatTime(maybePlannedEnd)} + {plannedEndText} )}
} /> - {maybeExpectedEnd !== null && maybeExpectedDaySpan >= 0 ? ( + {maybeExpectedEnd !== null && maybeExpectedDaySpan > 0 ? ( } @@ -68,7 +69,9 @@ export function StartTimes() { {formattedTime(maybeExpectedEnd)} ) : ( - {formattedTime(maybeExpectedEnd)} + + {formattedTime(maybeExpectedEnd)} + )}
@@ -199,11 +202,10 @@ export function OffsetOverview() { const isPlaying = isPlaybackActive(playback); const correctedOffset = offset * -1; - const offsetState = getOffsetState(correctedOffset); - const offsetClasses = cx([style.offset, offsetState && style[offsetState]]); + const offsetState = getOffsetState(offset); const offsetText = getOffsetText(isPlaying ? correctedOffset : null); - return ; + return ; } export function ClockOverview() { diff --git a/apps/client/src/features/overview/composite/TimeLayout.module.scss b/apps/client/src/features/overview/composite/TimeLayout.module.scss index 04ac4e592..9f64c6094 100644 --- a/apps/client/src/features/overview/composite/TimeLayout.module.scss +++ b/apps/client/src/features/overview/composite/TimeLayout.module.scss @@ -22,34 +22,31 @@ .label { line-height: 0.9em; width: 7em; - } -} - -.row { - display: flex; - align-items: center; - gap: 0.5rem; - height: 2.25em; - - .label { - text-align: right; - width: 5em; + display: flex; + gap: 0.25rem; } - .clock { - font-size: 1.25rem; + &[data-state="over"] { + .label .over { + color: $playback-over; + } + .clock { + color: $playback-over; + } + } + + &[data-state="under"] { + .label .under { + color: $playback-under; + } + .clock { + color: $playback-under; + } + } + + &[data-state="muted"] { + .clock { + color: $muted-gray; + } } } - -.daySpan { - &::after { - content: "*"; - vertical-align: super; - font-size: 0.75em; - color: $info-blue; - } -} - -.muted { - color: $muted-gray; -} diff --git a/apps/client/src/features/overview/composite/TimeLayout.tsx b/apps/client/src/features/overview/composite/TimeLayout.tsx index 4e45a19d6..9bc8e6b76 100644 --- a/apps/client/src/features/overview/composite/TimeLayout.tsx +++ b/apps/client/src/features/overview/composite/TimeLayout.tsx @@ -1,6 +1,3 @@ -import { PropsWithChildren } from 'react'; - -import Tooltip from '../../../common/components/tooltip/Tooltip'; import { cx } from '../../../common/utils/styleUtils'; import style from './TimeLayout.module.scss'; @@ -25,40 +22,23 @@ export function TimeColumn({ label, value, muted, className, testId }: TimeLayou ); } -export function TimeRow({ label, value, daySpan, muted, className }: TimeLayoutProps) { - return ( -
- {label} - {daySpan ? ( - } - className={cx([style.clock, style.daySpan, className])} - > - {value} - - ) : ( - {value} - )} -
- ); +interface OverUnderProps { + state: 'over' | 'under' | 'muted' | null; + value: string; + testId: string; } -export function TimeElementsRow({ label, value, daySpan, muted, className }: PropsWithChildren) { +export function OverUnder({ state, value, testId }: OverUnderProps) { return ( -
- {label} - {daySpan ? ( - } - className={cx([style.clock, style.daySpan, className])} - > - {value} - - ) : ( - {value} - )} +
+
+ Over + / + Under +
+ + {value} +
); } diff --git a/apps/client/src/features/rundown/entry-editor/BlockEditor.tsx b/apps/client/src/features/rundown/entry-editor/BlockEditor.tsx index fb1811c3f..376e856ec 100644 --- a/apps/client/src/features/rundown/entry-editor/BlockEditor.tsx +++ b/apps/client/src/features/rundown/entry-editor/BlockEditor.tsx @@ -8,6 +8,7 @@ import NullableTimeInput from '../../../common/components/input/time-input/Nulla import AppLink from '../../../common/components/link/app-link/AppLink'; import { useEntryActions } from '../../../common/hooks/useEntryAction'; import useCustomFields from '../../../common/hooks-query/useCustomFields'; +import { getOffsetState } from '../../../common/utils/offset'; import { enDash, timerPlaceholder } from '../../../common/utils/styleUtils'; import TextLikeInput from '../../../views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput'; @@ -54,7 +55,7 @@ export default function BlockEditor({ block }: BlockEditorProps) { const isEditor = window.location.pathname.includes('editor'); const planOffset = typeof block.targetDuration !== 'number' ? null : block.duration - block.targetDuration; - const planOffsetLabel = planOffset !== null && planOffset > 0 ? 'behind' : 'ahead'; + const planOffsetLabel = planOffset !== null ? getOffsetState(planOffset * -1) : null; return (
diff --git a/apps/client/src/features/rundown/entry-editor/composite/EventEditorImage.module.scss b/apps/client/src/features/rundown/entry-editor/composite/EventEditorImage.module.scss index 6f3bddef7..da8888e06 100644 --- a/apps/client/src/features/rundown/entry-editor/composite/EventEditorImage.module.scss +++ b/apps/client/src/features/rundown/entry-editor/composite/EventEditorImage.module.scss @@ -1,6 +1,6 @@ .imageContainer { - width: 100%; - height: 100%; + width: 75px; + height: 75px; background-color: $gray-1250; display: grid; place-content: center; diff --git a/apps/client/src/features/rundown/rundown-event/composite/RundownEventChip.module.scss b/apps/client/src/features/rundown/rundown-event/composite/RundownEventChip.module.scss index 1d636bd1a..a98c07977 100644 --- a/apps/client/src/features/rundown/rundown-event/composite/RundownEventChip.module.scss +++ b/apps/client/src/features/rundown/rundown-event/composite/RundownEventChip.module.scss @@ -6,11 +6,11 @@ justify-self: end; &.over { - color: $ontime-delay-text; + color: $playback-over; } &.under { - color: $playback-ahead; + color: $playback-under; } &.due { diff --git a/apps/client/src/features/rundown/rundown-milestone/RundownMilestone.tsx b/apps/client/src/features/rundown/rundown-milestone/RundownMilestone.tsx index 6caeae62c..14a8df5ea 100644 --- a/apps/client/src/features/rundown/rundown-milestone/RundownMilestone.tsx +++ b/apps/client/src/features/rundown/rundown-milestone/RundownMilestone.tsx @@ -59,7 +59,8 @@ export default function RundownMilestone({ colour, cue, entryId, hasCursor, titl updateEntry({ id: entryId, [field]: value }); }; - const handleDelete = () => { + const handleDelete = (event: MouseEvent) => { + event.stopPropagation(); deleteEntry([entryId]); }; diff --git a/apps/client/src/theme/_ontimeStyles.scss b/apps/client/src/theme/_ontimeStyles.scss index 639db84d7..4d55660da 100644 --- a/apps/client/src/theme/_ontimeStyles.scss +++ b/apps/client/src/theme/_ontimeStyles.scss @@ -38,6 +38,9 @@ $playback-ahead: $green-500; $active-indicator: #8bb33d; $text-black: $gray-1350; +$playback-over: #F57C13; +$playback-under: $green-500; + // interface panels $bg-container-l1: $gray-1350; $bg-container-l2: $gray-1300; diff --git a/apps/client/src/views/common/schedule/Schedule.scss b/apps/client/src/views/common/schedule/Schedule.scss index 65159b706..f8fa3585a 100644 --- a/apps/client/src/views/common/schedule/Schedule.scss +++ b/apps/client/src/views/common/schedule/Schedule.scss @@ -50,12 +50,12 @@ $indeterminate-width: clamp(32px, 3vw, 48px); color: $ontime-delay-text; } - &--ahead { - color: $green-500; + &--over { + color: $playback-over; } - &--behind { - color: $orange-500; + &--under { + color: $playback-under; } } diff --git a/apps/client/src/views/common/schedule/ScheduleItem.tsx b/apps/client/src/views/common/schedule/ScheduleItem.tsx index 7674a9bfb..9bfbbe70e 100644 --- a/apps/client/src/views/common/schedule/ScheduleItem.tsx +++ b/apps/client/src/views/common/schedule/ScheduleItem.tsx @@ -1,4 +1,5 @@ import { useRuntimeOffset } from '../../../common/hooks/useSocket'; +import { getOffsetState } from '../../../common/utils/offset'; import { cx } from '../../../common/utils/styleUtils'; import { formatTime } from '../../../common/utils/time'; import SuperscriptTime from '../../../features/viewers/common/superscript-time/SuperscriptTime'; @@ -121,11 +122,7 @@ function ProjectedTime(props: OffsetTimeProps) { const projectedOffset = offset - delay; const projectedTime = formatTime(time - offset, formatOptions); + const projectedState = getOffsetState(projectedOffset); - return ( - 0 && 'entry-times--ahead', projectedOffset < 0 && 'entry-times--behind'])} - time={projectedTime} - /> - ); + return ; } diff --git a/apps/client/src/views/countdown/Countdown.scss b/apps/client/src/views/countdown/Countdown.scss index ef297fcff..e4e652f7b 100644 --- a/apps/client/src/views/countdown/Countdown.scss +++ b/apps/client/src/views/countdown/Countdown.scss @@ -141,12 +141,12 @@ $item-height: 3.5rem; color: $delay-color; } - .sub__schedule--ahead { - color: $green-500; + .sub__schedule--over { + color: $playback-over } - .sub__schedule--behind { - color: $orange-500; + .sub__schedule--under { + color: $playback-under } .sub__title { diff --git a/apps/client/src/views/countdown/CountdownSubscriptions.tsx b/apps/client/src/views/countdown/CountdownSubscriptions.tsx index 5d411eb14..6d20f6305 100644 --- a/apps/client/src/views/countdown/CountdownSubscriptions.tsx +++ b/apps/client/src/views/countdown/CountdownSubscriptions.tsx @@ -7,6 +7,7 @@ import { useFadeOutOnInactivity } from '../../common/hooks/useFadeOutOnInactivit import useFollowComponent from '../../common/hooks/useFollowComponent'; import { useCurrentDay, useRuntimeOffset } from '../../common/hooks/useSocket'; import { ViewExtendedTimer } from '../../common/models/TimeManager.type'; +import { getOffsetState } from '../../common/utils/offset'; import { cx } from '../../common/utils/styleUtils'; import { throttle } from '../../common/utils/throttle'; import FollowButton from '../../features/operator/follow-button/FollowButton'; @@ -134,14 +135,13 @@ function ProjectedSchedule(props: ProjectedScheduleProps) { // offset is negative if we are ahead const projectedOffset = offset - delay; - - const classes = cx([projectedOffset > 0 && 'sub__schedule--ahead', projectedOffset < 0 && 'sub__schedule--behind']); + const projectState = getOffsetState(projectedOffset); return ( <> diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DurationInput.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DurationInput.tsx index bb251de28..74a28edad 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DurationInput.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/DurationInput.tsx @@ -103,7 +103,7 @@ function DurationInput({ onClick={handleFakeFocus} onFocus={handleFakeFocus} muted={!lockedValue} - offset={delayed ? 'behind' : undefined} + offset={delayed ? 'over' : undefined} ref={textRef} > {children} diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss index 9a43cb6a7..ceaa156e9 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.module.scss @@ -14,12 +14,12 @@ overflow: hidden; - &.ahead { - color: $playback-ahead; + &.under { + color: $playback-under; } - &.behind { - color: $ontime-delay-text; + &.over { + color: $playback-over; } &.muted { diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx index d8bbe4910..e8b200428 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TextLikeInput.tsx @@ -5,7 +5,7 @@ import { cx } from '../../../../common/utils/styleUtils'; import style from './TextLikeInput.module.scss'; interface TextLikeInputProps extends HTMLAttributes { - offset?: 'ahead' | 'behind'; + offset?: 'over' | 'under' | 'muted' | null; muted?: boolean; } diff --git a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx index 7e89967be..8e7f9d132 100644 --- a/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx +++ b/apps/client/src/views/cuesheet/cuesheet-table/cuesheet-table-elements/TimeInput.tsx @@ -105,7 +105,7 @@ function TimeInputDuration({ onClick={handleFakeFocus} onFocus={handleFakeFocus} muted={!lockedValue} - offset={delayed ? 'behind' : undefined} + offset={delayed ? 'over' : undefined} ref={textRef} > {children} diff --git a/apps/client/src/views/studio/StudioTimers.scss b/apps/client/src/views/studio/StudioTimers.scss index 121eb8a58..ea3dbd023 100644 --- a/apps/client/src/views/studio/StudioTimers.scss +++ b/apps/client/src/views/studio/StudioTimers.scss @@ -63,12 +63,12 @@ } } - .ahead { - color: $playback-ahead; + .over { + color: $playback-over; } - .behind { - color: $ontime-delay-text; + .under { + color: $playback-under; } .muted {