mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-15 20:33:47 +00:00
V3 (#657)
* refactor: cleanup routes * style: smaller base font * chore: upgrade dependencies * chore: lock node version to electron * refactor: pass HTTP to integration controller (#652) * refactor: deprecate onair control * refactor: remove playback router * Several project files user folder (#617) * chore: automated screenshots (#667) * feat: app settings (#658) * refactor: remove deprecated event data (#674) * Studio clock (#663) --------- Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Feat: reorder events with alt+ctrl + arrow up/down (#645) * Warning and danger per event (#677) --------- Co-authored-by: Fabian Posenau <fabian@fphome.de> * refactor: stabilise actionHandler (#683) Co-authored-by: Fabian Posenau <fabian@fphome.de> * improvement: hide seconds (#675) * wip: overview (#688) * fix: focus cursor (#695) * refactor: update lower third (#665) * Refactor/time formatting (#696) --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * feat: multiple selection (#703) --------- Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com> Co-authored-by: Alex <ac@omnivox.dk> * fix: test - go to `Edit mode` befor tying to click `Event options` button (#708) * refactor: runtime service (#715) * fix: issue with loosing cursor position on message (#719) * remove info panel (#721) * Event editor continue (#722) * update API - part (#709) --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * refactor: update timers (#729) * feat: many timers (#706) --------- Co-authored-by: arc-alex <ac@omnivox.dk> * refactor: excel cleanup (#734) * refactor: allow import of blocks and skip import (#735) * Project manager (#697) * refactor: UI for linking events (#763) * upgraded pipeline actions (#777) * Over under (#771) * custom fields (#744) --------- Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Sheets settings (#774) --------- Co-authored-by: arc-alex <ac@omnivox.dk> * style: tweaks to lower thirds (#785) * refactor: delays account for gaps (#784) * refactor: partial state updates (#780) * feat: generate crash report (#787) * Sheet use limited input device auth flow (#782) --------- Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com> Co-authored-by: Carlos Valente <carlosvalente@pm.me> * Custom fields views (#789) * refactor: deprecate presenter and subtitle (#795) * refactor: organise API around resources (#798) --------- Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com> * Time to end (#804) * Skip fixes (#805) * fix: onair derives from playback * Param nav (#822) --------- Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk> * refactor: download files from interface (#831) * Quick options (#814) * End pause (#832) * chore: bump node version in docker (#834) * refactor: follow in run mode (#840) * fix: uncaught error in http integration (#837) * Apply project (#843) Co-authored-by: Matteo Gheza <matteo.gheza07@gmail.com> Co-authored-by: Ary <arylmoraesn@gmail.com> Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk> Co-authored-by: Fabian Posenau <19673098+kellhogs@users.noreply.github.com> Co-authored-by: Fabian Posenau <fabian@fphome.de> Co-authored-by: Alex Rohleder <alexrohleder96@gmail.com> Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com> Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com> Co-authored-by: Fabian Posenau <fabianpos99+github@gmail.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
.overview {
|
||||
grid-area: overview;
|
||||
font-size: $inner-section-text-size;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
padding-inline: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
@include ellipsis-overflow;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1rem;
|
||||
color: $label-gray;
|
||||
@include ellipsis-overflow;
|
||||
}
|
||||
|
||||
.ahead {
|
||||
color: $green-500;
|
||||
}
|
||||
|
||||
.behind {
|
||||
color: $ontime-delay-text;
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { memo, useMemo } from 'react';
|
||||
import { millisToString } from 'ontime-utils';
|
||||
|
||||
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
|
||||
import { useRuntimeOverview, useRuntimePlaybackOverview, useTimer } from '../../common/hooks/useSocket';
|
||||
import useProjectData from '../../common/hooks-query/useProjectData';
|
||||
import { enDash } from '../../common/utils/styleUtils';
|
||||
|
||||
import { TimeColumn, TimeRow } from './composite/TimeLayout';
|
||||
import { calculateEndAndDaySpan, formatedTime, getOffsetText } from './overviewUtils';
|
||||
|
||||
import style from './Overview.module.scss';
|
||||
|
||||
export const EditorOverview = memo(_EditorOverview);
|
||||
|
||||
function _EditorOverview({ children }: { children: React.ReactNode }) {
|
||||
const { plannedEnd, plannedStart, actualStart, expectedEnd } = useRuntimeOverview();
|
||||
|
||||
const [maybePlannedEnd, maybePlannedDaySpan] = useMemo(() => calculateEndAndDaySpan(plannedEnd), [plannedEnd]);
|
||||
const plannedEndText = formatedTime(maybePlannedEnd);
|
||||
|
||||
const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]);
|
||||
const expectedEndText = formatedTime(maybeExpectedEnd);
|
||||
|
||||
return (
|
||||
<div className={style.overview}>
|
||||
<ErrorBoundary>
|
||||
<div className={style.nav}>{children}</div>
|
||||
<div className={style.info}>
|
||||
<TitlesOverview />
|
||||
<div>
|
||||
<TimeRow label='Planned start' value={formatedTime(plannedStart)} className={style.start} />
|
||||
<TimeRow label='Actual start' value={formatedTime(actualStart)} className={style.start} />
|
||||
</div>
|
||||
<ProgressOverview />
|
||||
<RuntimeOverview />
|
||||
<div>
|
||||
<TimeRow label='Planned end' value={plannedEndText} className={style.end} daySpan={maybePlannedDaySpan} />
|
||||
<TimeRow
|
||||
label='Expected end'
|
||||
value={expectedEndText}
|
||||
className={style.end}
|
||||
daySpan={maybeExpectedDaySpan}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const CuesheetOverview = memo(_CuesheetOverview);
|
||||
|
||||
function _CuesheetOverview({ children }: { children: React.ReactNode }) {
|
||||
const { plannedEnd, expectedEnd } = useRuntimeOverview();
|
||||
|
||||
const [maybePlannedEnd, maybePlannedDaySpan] = useMemo(() => calculateEndAndDaySpan(plannedEnd), [plannedEnd]);
|
||||
const plannedEndText = formatedTime(maybePlannedEnd);
|
||||
|
||||
const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]);
|
||||
const expectedEndText = formatedTime(maybeExpectedEnd);
|
||||
|
||||
return (
|
||||
<div className={style.overview}>
|
||||
<ErrorBoundary>
|
||||
<div className={style.nav}>{children}</div>
|
||||
<div className={style.info}>
|
||||
<TitlesOverview />
|
||||
<TimerOverview />
|
||||
<RuntimeOverview />
|
||||
<div>
|
||||
<TimeRow label='Planned end' value={plannedEndText} className={style.end} daySpan={maybePlannedDaySpan} />
|
||||
<TimeRow
|
||||
label='Expected end'
|
||||
value={expectedEndText}
|
||||
className={style.end}
|
||||
daySpan={maybeExpectedDaySpan}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TitlesOverview() {
|
||||
const { data } = useProjectData();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={style.title}>{data.title}</div>
|
||||
<div className={style.description}>{data.description}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TimerOverview() {
|
||||
const {current} = useTimer();
|
||||
|
||||
const display = millisToString(current);
|
||||
|
||||
return <TimeColumn label='Running timer' value={display} />;
|
||||
}
|
||||
|
||||
function ProgressOverview() {
|
||||
const { numEvents, selectedEventIndex } = useRuntimePlaybackOverview();
|
||||
|
||||
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
|
||||
const ofTotal = numEvents || enDash;
|
||||
const progressText = numEvents ? `${current} of ${ofTotal}` : '-';
|
||||
|
||||
return <TimeColumn label='Progress' value={progressText} />;
|
||||
}
|
||||
|
||||
function RuntimeOverview() {
|
||||
const { clock, offset } = useRuntimePlaybackOverview();
|
||||
|
||||
const offsetText = getOffsetText(offset);
|
||||
const offsetClasses = offset === null ? undefined : offset > 0 ? style.behind : style.ahead;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TimeColumn label='Offset' value={offsetText} className={offsetClasses} />
|
||||
<TimeColumn label='Time now' value={formatedTime(clock)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
.label {
|
||||
color: $label-gray;
|
||||
font-size: calc(1rem - 2px);
|
||||
width: 10em; // a number large enough to force right alignment
|
||||
}
|
||||
|
||||
.clock {
|
||||
text-align: left;
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 0.5px;
|
||||
min-width: 5em;
|
||||
|
||||
&::after {
|
||||
content: '\200b';
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.label {
|
||||
line-height: 0.9em;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
height: 2.25em;
|
||||
|
||||
.label {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.clock {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.daySpan {
|
||||
&::after {
|
||||
content: "*";
|
||||
vertical-align: super;
|
||||
font-size: 0.75em;
|
||||
color: $blue-500;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
|
||||
import { cx } from '../../../common/utils/styleUtils';
|
||||
|
||||
import style from './TimeLayout.module.scss';
|
||||
|
||||
interface TimeLayoutProps {
|
||||
label: string;
|
||||
value: string;
|
||||
daySpan?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TimeColumn({ label, value, className }: TimeLayoutProps) {
|
||||
return (
|
||||
<div className={style.column}>
|
||||
<span className={style.label}>{label}</span>
|
||||
<span className={cx([style.clock, className])}>{value}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TimeRow({ label, value, daySpan, className }: TimeLayoutProps) {
|
||||
return (
|
||||
<div className={style.row}>
|
||||
<span className={style.label}>{label}</span>
|
||||
{daySpan ? (
|
||||
<Tooltip label={`Event spans over ${daySpan + 1} days`}>
|
||||
<span className={cx([style.clock, style.daySpan, className])}>{value}</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span className={cx([style.clock, className])}>{value}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { MaybeNumber } from 'ontime-types';
|
||||
import { dayInMs, millisToString } from 'ontime-utils';
|
||||
|
||||
import { enDash, timerPlaceholder } from '../../common/utils/styleUtils';
|
||||
|
||||
/**
|
||||
* Encapsulates the logic for formatting time in overview
|
||||
* @param time
|
||||
* @returns
|
||||
*/
|
||||
export function formatedTime(time: MaybeNumber) {
|
||||
return millisToString(time, { fallback: timerPlaceholder });
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a day span from a number range
|
||||
* @param end
|
||||
* @returns
|
||||
*/
|
||||
export function calculateEndAndDaySpan(end: MaybeNumber): [MaybeNumber, number] {
|
||||
let maybeEnd = end;
|
||||
let maybeDaySpan = 0;
|
||||
if (end !== null) {
|
||||
if (end > dayInMs) {
|
||||
maybeEnd = end % dayInMs;
|
||||
maybeDaySpan = Math.floor(end / dayInMs);
|
||||
}
|
||||
}
|
||||
return [maybeEnd, maybeDaySpan];
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats offset text
|
||||
* @param offset
|
||||
* @returns
|
||||
*/
|
||||
export function getOffsetText(offset: MaybeNumber): string {
|
||||
if (offset === null) {
|
||||
return enDash;
|
||||
}
|
||||
const isAhead = offset <= 0;
|
||||
let offsetText = millisToString(Math.abs(offset), { fallback: enDash });
|
||||
if (offsetText !== enDash) {
|
||||
offsetText = isAhead ? `+${offsetText}` : `${enDash}${offsetText}`;
|
||||
}
|
||||
return offsetText;
|
||||
}
|
||||
Reference in New Issue
Block a user