mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-05 23:43:57 +00:00
feat: event description (#481)
* feat: enable configuration of <Info> panel * feat: add description field to event data
This commit is contained in:
@@ -13,14 +13,14 @@ import {
|
||||
requestPutEvent,
|
||||
requestReorderEvent,
|
||||
} from '../api/eventsApi';
|
||||
import { useLocalEvent } from '../stores/localEvent';
|
||||
import { useEditorSettings } from '../stores/editorSettings';
|
||||
|
||||
/**
|
||||
* @description Set of utilities for events
|
||||
*/
|
||||
export const useEventAction = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const eventSettings = useLocalEvent((state) => state.eventSettings);
|
||||
const eventSettings = useEditorSettings((state) => state.eventSettings);
|
||||
const defaultPublic = eventSettings.defaultPublic;
|
||||
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { EventData } from 'ontime-types';
|
||||
|
||||
export const eventDataPlaceholder: EventData = {
|
||||
title: '',
|
||||
description: '',
|
||||
publicUrl: '',
|
||||
publicInfo: '',
|
||||
backstageUrl: '',
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { booleanFromLocalStorage } from '../utils/localStorage';
|
||||
|
||||
type EditorSettings = {
|
||||
showQuickEntry: boolean;
|
||||
startTimeIsLastEnd: boolean;
|
||||
defaultPublic: boolean;
|
||||
showNif: boolean;
|
||||
};
|
||||
|
||||
type EditorSettingsStore = {
|
||||
eventSettings: EditorSettings;
|
||||
setLocalEventSettings: (newState: EditorSettings) => void;
|
||||
setShowQuickEntry: (showQuickEntry: boolean) => void;
|
||||
setStartTimeIsLastEnd: (startTimeIsLastEnd: boolean) => void;
|
||||
setDefaultPublic: (defaultPublic: boolean) => void;
|
||||
setShowNif: (showNif: boolean) => void;
|
||||
};
|
||||
|
||||
enum EditorSettingsKeys {
|
||||
ShowQuickEntry = 'ontime-show-quick-entry',
|
||||
StartTimeIsLastEnd = 'ontime-start-is-last-end',
|
||||
DefaultPublic = 'ontime-default-public',
|
||||
ShowNif = 'ontime-show-nif',
|
||||
}
|
||||
|
||||
export const useEditorSettings = create<EditorSettingsStore>((set) => ({
|
||||
eventSettings: {
|
||||
showQuickEntry: booleanFromLocalStorage(EditorSettingsKeys.ShowQuickEntry, false),
|
||||
startTimeIsLastEnd: booleanFromLocalStorage(EditorSettingsKeys.ShowQuickEntry, true),
|
||||
defaultPublic: booleanFromLocalStorage(EditorSettingsKeys.ShowQuickEntry, true),
|
||||
showNif: booleanFromLocalStorage(EditorSettingsKeys.ShowNif, true),
|
||||
},
|
||||
|
||||
setLocalEventSettings: (value) =>
|
||||
set(() => {
|
||||
localStorage.setItem(EditorSettingsKeys.ShowQuickEntry, String(value.showQuickEntry));
|
||||
localStorage.setItem(EditorSettingsKeys.StartTimeIsLastEnd, String(value.startTimeIsLastEnd));
|
||||
localStorage.setItem(EditorSettingsKeys.DefaultPublic, String(value.defaultPublic));
|
||||
return { eventSettings: value };
|
||||
}),
|
||||
|
||||
setShowQuickEntry: (showQuickEntry) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(EditorSettingsKeys.ShowQuickEntry, String(showQuickEntry));
|
||||
return { eventSettings: { ...state.eventSettings, showQuickEntry } };
|
||||
}),
|
||||
|
||||
setStartTimeIsLastEnd: (startTimeIsLastEnd) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(EditorSettingsKeys.StartTimeIsLastEnd, String(startTimeIsLastEnd));
|
||||
return { eventSettings: { ...state.eventSettings, startTimeIsLastEnd } };
|
||||
}),
|
||||
|
||||
setDefaultPublic: (defaultPublic) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(EditorSettingsKeys.DefaultPublic, String(defaultPublic));
|
||||
return { eventSettings: { ...state.eventSettings, defaultPublic } };
|
||||
}),
|
||||
|
||||
setShowNif: (showNif) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(EditorSettingsKeys.ShowNif, String(showNif));
|
||||
return { eventSettings: { ...state.eventSettings, showNif } };
|
||||
}),
|
||||
}));
|
||||
@@ -1,57 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
import { booleanFromLocalStorage } from '../utils/localStorage';
|
||||
|
||||
type EventSettings = {
|
||||
showQuickEntry: boolean;
|
||||
startTimeIsLastEnd: boolean;
|
||||
defaultPublic: boolean;
|
||||
};
|
||||
|
||||
type LocalEventStore = {
|
||||
eventSettings: EventSettings;
|
||||
setLocalEventSettings: (newState: EventSettings) => void;
|
||||
setShowQuickEntry: (showQuickEntry: boolean) => void;
|
||||
setStartTimeIsLastEnd: (startTimeIsLastEnd: boolean) => void;
|
||||
setDefaultPublic: (defaultPublic: boolean) => void;
|
||||
};
|
||||
|
||||
enum LocalEventKeys {
|
||||
ShowQuickEntry = 'ontime-show-quick-entry',
|
||||
StartTimeIsLastEnd = 'ontime-start-is-last-end',
|
||||
DefaultPublic = 'ontime-default-public',
|
||||
}
|
||||
|
||||
export const useLocalEvent = create<LocalEventStore>((set) => ({
|
||||
eventSettings: {
|
||||
showQuickEntry: booleanFromLocalStorage(LocalEventKeys.ShowQuickEntry, false),
|
||||
startTimeIsLastEnd: booleanFromLocalStorage(LocalEventKeys.ShowQuickEntry, true),
|
||||
defaultPublic: booleanFromLocalStorage(LocalEventKeys.ShowQuickEntry, true),
|
||||
},
|
||||
|
||||
setLocalEventSettings: (value) =>
|
||||
set(() => {
|
||||
localStorage.setItem(LocalEventKeys.ShowQuickEntry, String(value.showQuickEntry));
|
||||
localStorage.setItem(LocalEventKeys.StartTimeIsLastEnd, String(value.startTimeIsLastEnd));
|
||||
localStorage.setItem(LocalEventKeys.DefaultPublic, String(value.defaultPublic));
|
||||
return { eventSettings: value };
|
||||
}),
|
||||
|
||||
setShowQuickEntry: (showQuickEntry) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(LocalEventKeys.ShowQuickEntry, String(showQuickEntry));
|
||||
return { eventSettings: { ...state.eventSettings, showQuickEntry } };
|
||||
}),
|
||||
|
||||
setStartTimeIsLastEnd: (startTimeIsLastEnd) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(LocalEventKeys.StartTimeIsLastEnd, String(startTimeIsLastEnd));
|
||||
return { eventSettings: { ...state.eventSettings, startTimeIsLastEnd } };
|
||||
}),
|
||||
|
||||
setDefaultPublic: (defaultPublic) =>
|
||||
set((state) => {
|
||||
localStorage.setItem(LocalEventKeys.DefaultPublic, String(defaultPublic));
|
||||
return { eventSettings: { ...state.eventSettings, defaultPublic } };
|
||||
}),
|
||||
}));
|
||||
@@ -19,6 +19,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
color: $label-gray;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.labels {
|
||||
font-size: $inner-section-text-size;
|
||||
display: flex;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useInfoPanel } from '../../common/hooks/useSocket';
|
||||
import { useEditorSettings } from '../../common/stores/editorSettings';
|
||||
|
||||
import InfoHeader from './info-header/InfoHeader';
|
||||
import CollapsableInfo from './CollapsableInfo';
|
||||
@@ -8,6 +9,7 @@ import InfoTitles from './InfoTitles';
|
||||
|
||||
export default function Info() {
|
||||
const data = useInfoPanel();
|
||||
const showNif = useEditorSettings((state) => state.eventSettings.showNif);
|
||||
|
||||
const titlesNow = {
|
||||
title: data.titles.titleNow || '',
|
||||
@@ -32,9 +34,11 @@ export default function Info() {
|
||||
return (
|
||||
<>
|
||||
<InfoHeader selected={selected} />
|
||||
<CollapsableInfo title='Network Info'>
|
||||
<InfoNif />
|
||||
</CollapsableInfo>
|
||||
{showNif && (
|
||||
<CollapsableInfo title='Network Info'>
|
||||
<InfoNif />
|
||||
</CollapsableInfo>
|
||||
)}
|
||||
<CollapsableInfo title='Playing Now'>
|
||||
<InfoTitles data={titlesNow} />
|
||||
</CollapsableInfo>
|
||||
|
||||
@@ -6,9 +6,12 @@ export default function InfoHeader({ selected }: { selected: string }) {
|
||||
const { data } = useEventData();
|
||||
|
||||
return (
|
||||
<div className={style.panelHeader}>
|
||||
<span className={style.title}>{data?.title || ''}</span>
|
||||
<span className={style.selected}>{selected}</span>
|
||||
</div>
|
||||
<>
|
||||
<div className={style.panelHeader}>
|
||||
<span className={style.title}>{data?.title || ''}</span>
|
||||
<span className={style.selected}>{selected}</span>
|
||||
</div>
|
||||
<div className={style.description}>{data?.description || ''}</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,18 @@ export default function QuickStart({ onClose, isOpen }: QuickStartProps) {
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.entryRow}>
|
||||
<label className={styles.sectionTitle}>
|
||||
Event description
|
||||
<Input
|
||||
variant='ontime-filled-on-light'
|
||||
size='sm'
|
||||
maxLength={100}
|
||||
placeholder='Euro Love, Malmö 2024'
|
||||
{...register('description')}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className={styles.entryRow}>
|
||||
<label className={styles.sectionTitle}>
|
||||
Public Info
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Switch } from '@chakra-ui/react';
|
||||
|
||||
import { useLocalEvent } from '../../../common/stores/localEvent';
|
||||
import { useEditorSettings } from '../../../common/stores/editorSettings';
|
||||
import ModalSplitInput from '../ModalSplitInput';
|
||||
|
||||
import style from './SettingsModal.module.scss';
|
||||
|
||||
export default function EditorSettings() {
|
||||
const eventSettings = useLocalEvent((state) => state.eventSettings);
|
||||
const setShowQuickEntry = useLocalEvent((state) => state.setShowQuickEntry);
|
||||
const setStartTimeIsLastEnd = useLocalEvent((state) => state.setStartTimeIsLastEnd);
|
||||
const setDefaultPublic = useLocalEvent((state) => state.setDefaultPublic);
|
||||
const eventSettings = useEditorSettings((state) => state.eventSettings);
|
||||
const setShowQuickEntry = useEditorSettings((state) => state.setShowQuickEntry);
|
||||
const setStartTimeIsLastEnd = useEditorSettings((state) => state.setStartTimeIsLastEnd);
|
||||
const setDefaultPublic = useEditorSettings((state) => state.setDefaultPublic);
|
||||
const setShowNif = useEditorSettings((state) => state.setShowNif);
|
||||
|
||||
return (
|
||||
<div className={style.sectionContainer}>
|
||||
@@ -39,6 +40,18 @@ export default function EditorSettings() {
|
||||
onChange={(event) => setDefaultPublic(event.target.checked)}
|
||||
/>
|
||||
</ModalSplitInput>
|
||||
<span className={style.title}>Info settings</span>
|
||||
<ModalSplitInput
|
||||
field=''
|
||||
title='Show network'
|
||||
description='Whether to available show network interfaces in the panel'
|
||||
>
|
||||
<Switch
|
||||
variant='ontime-on-light'
|
||||
defaultChecked={eventSettings.showQuickEntry}
|
||||
onChange={(event) => setShowNif(event.target.checked)}
|
||||
/>
|
||||
</ModalSplitInput>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,21 @@ export default function EventDataForm() {
|
||||
{...register('title')}
|
||||
/>
|
||||
</ModalInput>
|
||||
<ModalInput
|
||||
field='description'
|
||||
title='Event description'
|
||||
description='Free field, shown in editor'
|
||||
error={errors.description?.message}
|
||||
>
|
||||
<Input
|
||||
{...inputProps}
|
||||
variant='ontime-filled-on-light'
|
||||
maxLength={100}
|
||||
placeholder='Euro Love, Malmö 2024'
|
||||
isDisabled={disableInputs}
|
||||
{...register('description')}
|
||||
/>
|
||||
</ModalInput>
|
||||
<div style={{ height: '16px' }} />
|
||||
<ModalInput field='publicInfo' title='Public Info' description='Information shown in public screens'>
|
||||
<Textarea
|
||||
|
||||
@@ -6,7 +6,7 @@ import { OntimeRundown, Playback, SupportedEvent } from 'ontime-types';
|
||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
||||
import { useRundownEditor } from '../../common/hooks/useSocket';
|
||||
import { AppMode, useAppMode } from '../../common/stores/appModeStore';
|
||||
import { useLocalEvent } from '../../common/stores/localEvent';
|
||||
import { useEditorSettings } from '../../common/stores/editorSettings';
|
||||
import { cloneEvent, getFirstEvent, getNextEvent, getPreviousEvent } from '../../common/utils/eventsManager';
|
||||
|
||||
import QuickAddBlock from './quick-add-block/QuickAddBlock';
|
||||
@@ -26,7 +26,7 @@ export default function Rundown(props: RundownProps) {
|
||||
|
||||
const featureData = useRundownEditor();
|
||||
const { addEvent, reorderEvent } = useEventAction();
|
||||
const eventSettings = useLocalEvent((state) => state.eventSettings);
|
||||
const eventSettings = useEditorSettings((state) => state.eventSettings);
|
||||
const defaultPublic = eventSettings.defaultPublic;
|
||||
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
||||
const showQuickEntry = eventSettings.showQuickEntry;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent } from 'ontim
|
||||
|
||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
||||
import { useAppMode } from '../../common/stores/appModeStore';
|
||||
import { useLocalEvent } from '../../common/stores/localEvent';
|
||||
import { useEditorSettings } from '../../common/stores/editorSettings';
|
||||
import { useEmitLog } from '../../common/stores/logger';
|
||||
import { cloneEvent } from '../../common/utils/eventsManager';
|
||||
import { calculateDuration } from '../../common/utils/timesManager';
|
||||
@@ -61,7 +61,7 @@ export default function RundownEntry(props: RundownEntryProps) {
|
||||
}
|
||||
}, [cursor, data.id, openId, setCursor, setEditId]);
|
||||
|
||||
const eventSettings = useLocalEvent((state) => state.eventSettings);
|
||||
const eventSettings = useEditorSettings((state) => state.eventSettings);
|
||||
const defaultPublic = eventSettings.defaultPublic;
|
||||
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button, Checkbox, Tooltip } from '@chakra-ui/react';
|
||||
import { SupportedEvent } from 'ontime-types';
|
||||
|
||||
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||
import { useLocalEvent } from '../../../common/stores/localEvent';
|
||||
import { useEditorSettings } from '../../../common/stores/editorSettings';
|
||||
import { useEmitLog } from '../../../common/stores/logger';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
@@ -25,7 +25,7 @@ const QuickAddBlock = (props: QuickAddBlockProps) => {
|
||||
const doStartTime = useRef<HTMLInputElement | null>(null);
|
||||
const doPublic = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const eventSettings = useLocalEvent((state) => state.eventSettings);
|
||||
const eventSettings = useEditorSettings((state) => state.eventSettings);
|
||||
const defaultPublic = eventSettings.defaultPublic;
|
||||
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
||||
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { RequestHandler } from 'express';
|
||||
|
||||
import { EventData } from 'ontime-types';
|
||||
|
||||
import { removeUndefined } from '../utils/parserUtils.js';
|
||||
import { failEmptyObjects } from '../utils/routerUtils.js';
|
||||
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
||||
|
||||
// Create controller for GET request to 'event'
|
||||
export const getEventData = async (req, res) => {
|
||||
export const getEventData: RequestHandler = async (req, res) => {
|
||||
res.json(DataProvider.getEventData());
|
||||
};
|
||||
|
||||
// Create controller for POST request to 'event'
|
||||
export const postEventData = async (req, res) => {
|
||||
export const postEventData: RequestHandler = async (req, res) => {
|
||||
if (failEmptyObjects(req.body, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const newEvent = removeUndefined({
|
||||
const newEvent: Partial<EventData> = removeUndefined({
|
||||
title: req.body?.title,
|
||||
description: req.body?.description,
|
||||
publicUrl: req.body?.publicUrl,
|
||||
publicInfo: req.body?.publicInfo,
|
||||
backstageUrl: req.body?.backstageUrl,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { body, validationResult } from 'express-validator';
|
||||
|
||||
export const eventDataSanitizer = [
|
||||
body('title').optional().isString().trim(),
|
||||
body('description').optional().isString().trim(),
|
||||
body('publicUrl').optional().isString().trim(),
|
||||
body('publicInfo').optional().isString().trim(),
|
||||
body('backstageUrl').optional().isString().trim(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Alias, EventData, LogOrigin } from 'ontime-types';
|
||||
|
||||
import { RequestHandler } from 'express';
|
||||
import fs from 'fs';
|
||||
import { networkInterfaces } from 'os';
|
||||
|
||||
@@ -325,10 +326,11 @@ export const dbUpload = async (req, res) => {
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/ontime/new'
|
||||
export const postNew = async (req, res) => {
|
||||
export const postNew: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const newEventData: Omit<EventData, 'endMessage'> = {
|
||||
const newEventData: EventData = {
|
||||
title: req.body?.title ?? '',
|
||||
description: req.body?.description ?? '',
|
||||
publicUrl: req.body?.publicUrl ?? '',
|
||||
publicInfo: req.body?.publicInfo ?? '',
|
||||
backstageUrl: req.body?.backstageUrl ?? '',
|
||||
|
||||
@@ -4,6 +4,7 @@ export const dbModel: DatabaseModel = {
|
||||
rundown: [],
|
||||
eventData: {
|
||||
title: '',
|
||||
description: '',
|
||||
publicUrl: '',
|
||||
publicInfo: '',
|
||||
backstageUrl: '',
|
||||
|
||||
@@ -96,6 +96,7 @@ export const parseEventData = (data, enforce): EventData => {
|
||||
newEventData = {
|
||||
...dbModel.eventData,
|
||||
title: e.title || dbModel.eventData.title,
|
||||
description: e.description || dbModel.eventData.description,
|
||||
publicUrl: e.publicUrl || dbModel.eventData.publicUrl,
|
||||
publicInfo: e.publicInfo || dbModel.eventData.publicInfo,
|
||||
backstageUrl: e.backstageUrl || dbModel.eventData.backstageUrl,
|
||||
|
||||
@@ -229,6 +229,7 @@
|
||||
],
|
||||
"eventData": {
|
||||
"title": "All about Carlos demo event",
|
||||
"description": "Demo event for Ontime",
|
||||
"publicUrl": "www.getontime.no",
|
||||
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
||||
"backstageUrl": "www.getontime.no",
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
],
|
||||
"eventData": {
|
||||
"title": "All about Carlos demo event",
|
||||
"description": "Demo event for Ontime",
|
||||
"publicUrl": "www.getontime.no",
|
||||
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
||||
"backstageUrl": "www.getontime.no",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
],
|
||||
"eventData": {
|
||||
"title": "Ontime demo event",
|
||||
"description": "Demo event for Ontime",
|
||||
"publicUrl": "www.getontime.no",
|
||||
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
||||
"backstageUrl": "www.getontime.no",
|
||||
|
||||
Vendored
+1
@@ -95,6 +95,7 @@
|
||||
],
|
||||
"eventData": {
|
||||
"title": "All about Carlos demo event",
|
||||
"description": "Demo event for Ontime",
|
||||
"publicUrl": "www.getontime.no",
|
||||
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
|
||||
"backstageUrl": "www.getontime.no",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export type EventData = {
|
||||
title: string;
|
||||
description: string;
|
||||
publicUrl: string;
|
||||
publicInfo: string;
|
||||
backstageUrl: string;
|
||||
|
||||
Reference in New Issue
Block a user