mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-23 16:09:11 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bc6c3e852 | |||
| 257f2259e0 | |||
| 8ae3bb2df0 | |||
| ff1496d29f | |||
| 04455c08ef | |||
| 6c86ece1ff | |||
| 9a2fa527c6 | |||
| 165920b9f2 | |||
| ee3b3c0735 | |||
| ecaf0a209b |
@@ -73,13 +73,29 @@ export async function getAliases(): Promise<Alias[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description HTTP request to mutate aliases
|
* @description HTTP request to create an alias
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
export async function postAliases(data: Alias[]) {
|
export async function postAlias(data: Alias) {
|
||||||
return axios.post(`${ontimeURL}/aliases`, data);
|
return axios.post(`${ontimeURL}/aliases`, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description HTTP request to update aliases
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export async function updateAliases(data: Partial<Alias>) {
|
||||||
|
return axios.put(`${ontimeURL}/aliases`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description HTTP request to delete alias
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
export async function deleteAlias(alias: string) {
|
||||||
|
return axios.delete(`${ontimeURL}/aliases/${alias}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description HTTP request to retrieve user fields
|
* @description HTTP request to retrieve user fields
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import IntegrationsPanel from './panel/integrations-panel/IntegrationsPanel';
|
|||||||
import LogPanel from './panel/log-panel/LogPanel';
|
import LogPanel from './panel/log-panel/LogPanel';
|
||||||
import ProjectPanel from './panel/project-panel/ProjectPanel';
|
import ProjectPanel from './panel/project-panel/ProjectPanel';
|
||||||
import ProjectSettingsPanel from './panel/project-settings-panel/ProjectSettingsPanel';
|
import ProjectSettingsPanel from './panel/project-settings-panel/ProjectSettingsPanel';
|
||||||
|
import UrlPresetPanel from './panel/url-preset-panel/UrlPresetPanel';
|
||||||
import PanelContent from './panel-content/PanelContent';
|
import PanelContent from './panel-content/PanelContent';
|
||||||
import PanelList from './panel-list/PanelList';
|
import PanelList from './panel-list/PanelList';
|
||||||
import { useSettingsStore } from './settingsStore';
|
import { useSettingsStore } from './settingsStore';
|
||||||
@@ -30,6 +31,7 @@ export default function AppSettings() {
|
|||||||
{selectedPanel === 'project' && <ProjectPanel />}
|
{selectedPanel === 'project' && <ProjectPanel />}
|
||||||
{selectedPanel === 'integrations' && <IntegrationsPanel />}
|
{selectedPanel === 'integrations' && <IntegrationsPanel />}
|
||||||
{selectedPanel === 'project_settings' && <ProjectSettingsPanel />}
|
{selectedPanel === 'project_settings' && <ProjectSettingsPanel />}
|
||||||
|
{selectedPanel === 'url_presets' && <UrlPresetPanel />}
|
||||||
{selectedPanel === 'about' && <AboutPanel />}
|
{selectedPanel === 'about' && <AboutPanel />}
|
||||||
{selectedPanel === 'log' && <LogPanel />}
|
{selectedPanel === 'log' && <LogPanel />}
|
||||||
</PanelContent>
|
</PanelContent>
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { Button, Input } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import style from './UrlPresetPanel.module.scss';
|
||||||
|
|
||||||
|
export type UrlPresetFormValues = {
|
||||||
|
alias?: string;
|
||||||
|
pathAndParams?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UrlPresetFormProps {
|
||||||
|
alias?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
pathAndParams?: string;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSubmit: (values: UrlPresetFormValues) => Promise<void>;
|
||||||
|
submitError: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UrlPresetForm({
|
||||||
|
onSubmit,
|
||||||
|
onCancel,
|
||||||
|
submitError,
|
||||||
|
alias,
|
||||||
|
enabled,
|
||||||
|
pathAndParams,
|
||||||
|
}: UrlPresetFormProps) {
|
||||||
|
const {
|
||||||
|
handleSubmit,
|
||||||
|
register,
|
||||||
|
formState: { isSubmitting, isValid },
|
||||||
|
setFocus,
|
||||||
|
} = useForm<UrlPresetFormValues>({
|
||||||
|
defaultValues: { alias, pathAndParams, enabled },
|
||||||
|
values: { alias, pathAndParams, enabled },
|
||||||
|
resetOptions: {
|
||||||
|
keepDirtyValues: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFocus('alias');
|
||||||
|
}, [setFocus]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)} className={style.form}>
|
||||||
|
<div className={style.formInput}>
|
||||||
|
<Input
|
||||||
|
variant='ontime-filled'
|
||||||
|
size='sm'
|
||||||
|
maxLength={50}
|
||||||
|
placeholder='Alias'
|
||||||
|
autoComplete='off'
|
||||||
|
{...register('alias')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={style.formInput}>
|
||||||
|
<Input
|
||||||
|
variant='ontime-filled'
|
||||||
|
size='sm'
|
||||||
|
maxLength={100}
|
||||||
|
placeholder='Path and params'
|
||||||
|
autoComplete='off'
|
||||||
|
{...register('pathAndParams')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={style.actionButtons}>
|
||||||
|
<Button onClick={onCancel} variant='ontime-ghosted' size='sm'>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
isDisabled={!isValid}
|
||||||
|
type='submit'
|
||||||
|
isLoading={isSubmitting}
|
||||||
|
variant='ontime-filled'
|
||||||
|
padding='0 2em'
|
||||||
|
size='sm'
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{submitError && <span className={style.error}>{submitError}</span>}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { postAlias } from '../../../../common/api/ontimeApi';
|
||||||
|
import useAliases from '../../../../common/hooks-query/useAliases';
|
||||||
|
import ModalLoader from '../../../modals/modal-loader/ModalLoader';
|
||||||
|
import * as Panel from '../PanelUtils';
|
||||||
|
|
||||||
|
import UrlPresetForm, { UrlPresetFormValues } from './UrlPresetForm';
|
||||||
|
import UrlPresetListItem from './UrlPresetListItem';
|
||||||
|
|
||||||
|
interface UrlPresetListProps {
|
||||||
|
isCreatingPresetURL: boolean;
|
||||||
|
onToggleCreate: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UrlPresetList({ isCreatingPresetURL, onToggleCreate }: UrlPresetListProps) {
|
||||||
|
const { data, isFetching, refetch } = useAliases();
|
||||||
|
|
||||||
|
const handleRefetch = async () => {
|
||||||
|
await refetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitCreate = async (values: UrlPresetFormValues) => {
|
||||||
|
try {
|
||||||
|
// TODO: fix this
|
||||||
|
// @ts-ignore
|
||||||
|
await postAlias({
|
||||||
|
...values,
|
||||||
|
enabled: true,
|
||||||
|
});
|
||||||
|
await refetch();
|
||||||
|
} catch (error) {
|
||||||
|
// some error handling here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return <ModalLoader />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Panel.Table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Alias</th>
|
||||||
|
<th>Path and Params</th>
|
||||||
|
<th>Enabled</th>
|
||||||
|
<th />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{isCreatingPresetURL ? (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={99}>
|
||||||
|
<UrlPresetForm onCancel={onToggleCreate} onSubmit={handleSubmitCreate} submitError='' />
|
||||||
|
{/* {submitError && <span className={style.createSubmitError}>{submitError}</span>} */}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
) : null}
|
||||||
|
{(data || []).map((alias) => {
|
||||||
|
return (
|
||||||
|
<UrlPresetListItem
|
||||||
|
alias={alias.alias}
|
||||||
|
enabled={alias.enabled}
|
||||||
|
pathAndParams={alias.pathAndParams}
|
||||||
|
onRefetch={handleRefetch}
|
||||||
|
key={alias.alias}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Panel.Table>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { IconButton, Menu, MenuButton, MenuItem, MenuList, Switch } from '@chakra-ui/react';
|
||||||
|
import { IoEllipsisHorizontal } from '@react-icons/all-files/io5/IoEllipsisHorizontal';
|
||||||
|
|
||||||
|
import { deleteAlias, updateAliases } from '../../../../common/api/ontimeApi';
|
||||||
|
|
||||||
|
import UrlAliasForm, { UrlPresetFormValues } from './UrlPresetForm';
|
||||||
|
|
||||||
|
interface UrlPresetListItemProps {
|
||||||
|
alias: string;
|
||||||
|
enabled: boolean;
|
||||||
|
pathAndParams: string;
|
||||||
|
onRefetch: () => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UrlPresetListItem({ alias, enabled, pathAndParams, onRefetch }: UrlPresetListItemProps) {
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
|
||||||
|
const handleToggle = useCallback(async () => {
|
||||||
|
// TODO: Error handling
|
||||||
|
await updateAliases({
|
||||||
|
alias,
|
||||||
|
enabled: !enabled,
|
||||||
|
pathAndParams,
|
||||||
|
});
|
||||||
|
|
||||||
|
await onRefetch();
|
||||||
|
}, [alias, enabled, onRefetch, pathAndParams]);
|
||||||
|
|
||||||
|
const handleToggleEditMode = useCallback(() => {
|
||||||
|
setIsEditing(!isEditing);
|
||||||
|
}, [isEditing]);
|
||||||
|
|
||||||
|
const handleSubmitUpdate = useCallback(
|
||||||
|
async (values: UrlPresetFormValues) => {
|
||||||
|
try {
|
||||||
|
await updateAliases(values);
|
||||||
|
await onRefetch();
|
||||||
|
} catch (error) {
|
||||||
|
// some error handling here
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onRefetch],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleDelete = useCallback(async () => {
|
||||||
|
await deleteAlias(alias);
|
||||||
|
await onRefetch();
|
||||||
|
}, [alias, onRefetch]);
|
||||||
|
|
||||||
|
const handleRenderAliases = useMemo(() => {
|
||||||
|
if (!isEditing) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<td
|
||||||
|
style={{
|
||||||
|
width: '45%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{alias}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
style={{
|
||||||
|
width: '45%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pathAndParams}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<Switch variant='ontime-on-light' isChecked={enabled} onChange={handleToggle} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ActionMenu onDelete={handleDelete} onChangeEditMode={handleToggleEditMode} />
|
||||||
|
</td>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<td colSpan={99}>
|
||||||
|
<UrlAliasForm
|
||||||
|
alias={alias}
|
||||||
|
enabled={enabled}
|
||||||
|
pathAndParams={pathAndParams}
|
||||||
|
onCancel={handleToggleEditMode}
|
||||||
|
onSubmit={handleSubmitUpdate}
|
||||||
|
submitError=''
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [alias, enabled, handleDelete, handleSubmitUpdate, handleToggle, handleToggleEditMode, isEditing, pathAndParams]);
|
||||||
|
|
||||||
|
return <tr key={alias}>{handleRenderAliases}</tr>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActionMenu({ onChangeEditMode, onDelete }: { onChangeEditMode: () => void; onDelete: () => Promise<void> }) {
|
||||||
|
const handleEdit = () => {
|
||||||
|
onChangeEditMode();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu variant='ontime-on-dark' size='sm'>
|
||||||
|
<MenuButton
|
||||||
|
as={IconButton}
|
||||||
|
aria-label='Options'
|
||||||
|
icon={<IoEllipsisHorizontal />}
|
||||||
|
variant='ontime-ghosted'
|
||||||
|
size='sm'
|
||||||
|
/>
|
||||||
|
<MenuList>
|
||||||
|
<MenuItem onClick={handleEdit}>Edit</MenuItem>
|
||||||
|
<MenuItem onClick={onDelete}>Delete</MenuItem>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formInput {
|
||||||
|
flex: 2;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionButtons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
import ModalLink from '../../../modals/ModalLink';
|
||||||
|
import * as Panel from '../PanelUtils';
|
||||||
|
|
||||||
|
import UrlPresetList from './UrlPresetList';
|
||||||
|
|
||||||
|
import style from './UrlPresetPanel.module.scss';
|
||||||
|
|
||||||
|
const aliasesDocsUrl = 'https://ontime.gitbook.io/v2/features/url-aliases';
|
||||||
|
|
||||||
|
export default function UrlPresetPanel() {
|
||||||
|
const [isCreatingPresetURL, setIsCreatingPresetURL] = useState(false);
|
||||||
|
|
||||||
|
const handleToggleCreate = () => {
|
||||||
|
setIsCreatingPresetURL((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Panel.Header>URL Presets</Panel.Header>
|
||||||
|
<Panel.Section>
|
||||||
|
<Panel.Card>
|
||||||
|
<main>
|
||||||
|
<Alert status='info' variant='ontime-on-light-info'>
|
||||||
|
<AlertIcon />
|
||||||
|
<div className={style.column}>
|
||||||
|
<AlertTitle>URL Presets</AlertTitle>
|
||||||
|
<AlertDescription>
|
||||||
|
Custom presets allow providing a short name for any ontime URL. <br />
|
||||||
|
It serves two primary purposes: <br />
|
||||||
|
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
|
||||||
|
<ModalLink href={aliasesDocsUrl}>For more information, see the docs</ModalLink>
|
||||||
|
</AlertDescription>
|
||||||
|
</div>
|
||||||
|
</Alert>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Panel.SubHeader>
|
||||||
|
Manage URL presets
|
||||||
|
<Button variant='ontime-filled' onClick={handleToggleCreate}>
|
||||||
|
New
|
||||||
|
</Button>
|
||||||
|
</Panel.SubHeader>
|
||||||
|
</div>
|
||||||
|
<UrlPresetList isCreatingPresetURL={isCreatingPresetURL} onToggleCreate={handleToggleCreate} />
|
||||||
|
</main>
|
||||||
|
</Panel.Card>
|
||||||
|
</Panel.Section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ export const settingPanels: Readonly<SettingsOption[]> = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ id: 'log', label: 'Log', split: true },
|
{ id: 'log', label: 'Log', split: true },
|
||||||
|
{ id: 'url_presets', label: 'URL Presets' },
|
||||||
{
|
{
|
||||||
id: 'about',
|
id: 'about',
|
||||||
label: 'About',
|
label: 'About',
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { useFieldArray, useForm } from 'react-hook-form';
|
|
||||||
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button, IconButton, Input, Switch } from '@chakra-ui/react';
|
|
||||||
import { IoOpenOutline } from '@react-icons/all-files/io5/IoOpenOutline';
|
|
||||||
import { IoRemove } from '@react-icons/all-files/io5/IoRemove';
|
|
||||||
import { Alias } from 'ontime-types';
|
|
||||||
|
|
||||||
import { logAxiosError } from '../../../common/api/apiUtils';
|
|
||||||
import { postAliases } from '../../../common/api/ontimeApi';
|
|
||||||
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
|
|
||||||
import useAliases from '../../../common/hooks-query/useAliases';
|
|
||||||
import { useEmitLog } from '../../../common/stores/logger';
|
|
||||||
import { handleLinks } from '../../../common/utils/linkUtils';
|
|
||||||
import ModalLoader from '../modal-loader/ModalLoader';
|
|
||||||
import { inputProps } from '../modalHelper';
|
|
||||||
import ModalLink from '../ModalLink';
|
|
||||||
import OntimeModalFooter from '../OntimeModalFooter';
|
|
||||||
|
|
||||||
import style from './SettingsModal.module.scss';
|
|
||||||
|
|
||||||
const aliasesDocsUrl = 'https://ontime.gitbook.io/v2/features/url-aliases';
|
|
||||||
|
|
||||||
// we wrap the array in an object to be simplify react-hook-form
|
|
||||||
type Aliases = {
|
|
||||||
aliases: Alias[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AliasesForm() {
|
|
||||||
const { data, status, isFetching, refetch } = useAliases();
|
|
||||||
const { emitError } = useEmitLog();
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
handleSubmit,
|
|
||||||
register,
|
|
||||||
reset,
|
|
||||||
formState: { isSubmitting, isDirty, isValid },
|
|
||||||
} = useForm<Aliases>({
|
|
||||||
defaultValues: { aliases: data },
|
|
||||||
values: { aliases: data || [] },
|
|
||||||
resetOptions: {
|
|
||||||
keepDirtyValues: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const { fields, append, remove } = useFieldArray({
|
|
||||||
name: 'aliases',
|
|
||||||
control,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data) {
|
|
||||||
reset({ aliases: data });
|
|
||||||
}
|
|
||||||
}, [data, reset]);
|
|
||||||
|
|
||||||
const onSubmit = async (formData: Aliases) => {
|
|
||||||
try {
|
|
||||||
await postAliases(formData.aliases);
|
|
||||||
} catch (error) {
|
|
||||||
logAxiosError('Error saving aliases', error);
|
|
||||||
} finally {
|
|
||||||
await refetch();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onReset = () => {
|
|
||||||
reset({ aliases: data });
|
|
||||||
};
|
|
||||||
|
|
||||||
const addNew = () => {
|
|
||||||
if (fields.length > 20) {
|
|
||||||
emitError('Maximum amount of aliases reached (20)');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
append({
|
|
||||||
enabled: false,
|
|
||||||
alias: '',
|
|
||||||
pathAndParams: '',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const disableInputs = status === 'pending';
|
|
||||||
const hasTooManyOptions = fields.length >= 20;
|
|
||||||
|
|
||||||
if (isFetching) {
|
|
||||||
return <ModalLoader />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)} id='aliases' className={style.sectionContainer}>
|
|
||||||
<div style={{ height: '16px' }} />
|
|
||||||
<Alert status='info' variant='ontime-on-light-info'>
|
|
||||||
<AlertIcon />
|
|
||||||
<div className={style.column}>
|
|
||||||
<AlertTitle>URL Aliases</AlertTitle>
|
|
||||||
<AlertDescription>
|
|
||||||
Custom aliases allow providing a short name for any ontime URL. <br />
|
|
||||||
It serves two primary purposes: <br />
|
|
||||||
- Providing dynamic URLs for automation or unattended screens <br />- Simplifying complex URLs
|
|
||||||
<ModalLink href={aliasesDocsUrl}>For more information, see the docs</ModalLink>
|
|
||||||
</AlertDescription>
|
|
||||||
</div>
|
|
||||||
</Alert>
|
|
||||||
<div style={{ height: '16px' }} />
|
|
||||||
<ul className={style.aliases}>
|
|
||||||
{fields.map((alias, index) => {
|
|
||||||
return (
|
|
||||||
<li className={style.aliasRow} key={alias.id}>
|
|
||||||
<IconButton
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
aria-label='delete'
|
|
||||||
size='xs'
|
|
||||||
icon={<IoRemove />}
|
|
||||||
colorScheme='red'
|
|
||||||
isDisabled={disableInputs}
|
|
||||||
data-testid={`field__delete_${index}`}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
{...inputProps}
|
|
||||||
{...register(`aliases.${index}.alias`)}
|
|
||||||
width='12em'
|
|
||||||
size='xs'
|
|
||||||
variant='ontime-filled-on-light'
|
|
||||||
placeholder='URL Alias'
|
|
||||||
isDisabled={disableInputs}
|
|
||||||
data-testid={`field__alias_${index}`}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
{...inputProps}
|
|
||||||
{...register(`aliases.${index}.pathAndParams`)}
|
|
||||||
className={style.grow}
|
|
||||||
size='xs'
|
|
||||||
variant='ontime-filled-on-light'
|
|
||||||
placeholder='URL (portion after ontime Port)'
|
|
||||||
isDisabled={disableInputs}
|
|
||||||
data-testid={`field__url_${index}`}
|
|
||||||
/>
|
|
||||||
<TooltipActionBtn
|
|
||||||
clickHandler={(event) => handleLinks(event, alias.alias)}
|
|
||||||
tooltip='Test alias'
|
|
||||||
aria-label='Test alias'
|
|
||||||
size='xs'
|
|
||||||
variant='ontime-ghost-on-light'
|
|
||||||
icon={<IoOpenOutline />}
|
|
||||||
colorScheme='red'
|
|
||||||
isDisabled={disableInputs}
|
|
||||||
data-testid={`field__test_${index}`}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
{...register(`aliases.${index}.enabled`)}
|
|
||||||
variant='ontime-on-light'
|
|
||||||
isDisabled={disableInputs}
|
|
||||||
data-testid={`field__enable_${index}`}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
<Button
|
|
||||||
onClick={addNew}
|
|
||||||
className={style.shiftRight}
|
|
||||||
isDisabled={hasTooManyOptions}
|
|
||||||
size='xs'
|
|
||||||
colorScheme='blue'
|
|
||||||
variant='outline'
|
|
||||||
padding='0 2em'
|
|
||||||
>
|
|
||||||
Add new
|
|
||||||
</Button>
|
|
||||||
<OntimeModalFooter
|
|
||||||
formId='aliases'
|
|
||||||
handleRevert={onReset}
|
|
||||||
isDirty={isDirty}
|
|
||||||
isValid={isValid}
|
|
||||||
isSubmitting={isSubmitting}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ import { ModalBody, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/r
|
|||||||
|
|
||||||
import ModalWrapper from '../ModalWrapper';
|
import ModalWrapper from '../ModalWrapper';
|
||||||
|
|
||||||
import AliasesForm from './AliasesForm';
|
|
||||||
import AppSettingsModal from './AppSettings';
|
import AppSettingsModal from './AppSettings';
|
||||||
import CuesheetSettingsForm from './CuesheetSettingsForm';
|
import CuesheetSettingsForm from './CuesheetSettingsForm';
|
||||||
import EditorSettings from './EditorSettings';
|
import EditorSettings from './EditorSettings';
|
||||||
@@ -26,7 +25,7 @@ export default function SettingsModal(props: ModalManagerProps) {
|
|||||||
<Tab>Editor</Tab>
|
<Tab>Editor</Tab>
|
||||||
<Tab>Cuesheet</Tab>
|
<Tab>Cuesheet</Tab>
|
||||||
<Tab>Views</Tab>
|
<Tab>Views</Tab>
|
||||||
<Tab>URL Aliases</Tab>
|
<Tab>URL Presets</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
@@ -44,9 +43,6 @@ export default function SettingsModal(props: ModalManagerProps) {
|
|||||||
<TabPanel>
|
<TabPanel>
|
||||||
<ViewSettingsForm />
|
<ViewSettingsForm />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
|
||||||
<AliasesForm />
|
|
||||||
</TabPanel>
|
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|||||||
@@ -71,11 +71,17 @@ export class DataProvider {
|
|||||||
return data.aliases;
|
return data.aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove this when I change the data model
|
||||||
static async setAliases(newData: Alias[]) {
|
static async setAliases(newData: Alias[]) {
|
||||||
data.aliases = newData;
|
data.aliases = newData;
|
||||||
await this.persist();
|
await this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async addAlias(newData: Alias) {
|
||||||
|
data.aliases.push(newData);
|
||||||
|
await this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
static getUserFields() {
|
static getUserFields() {
|
||||||
return { ...data.userFields };
|
return { ...data.userFields };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { LogOrigin } from 'ontime-types';
|
import { LogOrigin } from 'ontime-types';
|
||||||
import type {
|
import type {
|
||||||
Alias,
|
|
||||||
DatabaseModel,
|
DatabaseModel,
|
||||||
GetInfo,
|
GetInfo,
|
||||||
HttpSettings,
|
HttpSettings,
|
||||||
@@ -19,7 +18,7 @@ import { copyFile, rename, writeFile } from 'fs/promises';
|
|||||||
|
|
||||||
import { fileHandler } from '../utils/parser.js';
|
import { fileHandler } from '../utils/parser.js';
|
||||||
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
||||||
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
|
import { failEmptyObjects } from '../utils/routerUtils.js';
|
||||||
import { runtimeService } from '../services/runtime-service/RuntimeService.js';
|
import { runtimeService } from '../services/runtime-service/RuntimeService.js';
|
||||||
import { eventStore } from '../stores/EventStore.js';
|
import { eventStore } from '../stores/EventStore.js';
|
||||||
import {
|
import {
|
||||||
@@ -160,30 +159,77 @@ export const getInfo = async (_req: Request, res: Response<GetInfo>) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create controller for POST request to '/ontime/aliases'
|
// Create controller for POST request to '/ontime/presets'
|
||||||
// Returns -
|
// Returns -
|
||||||
export const getAliases = async (_req: Request, res: Response) => {
|
export const getPresets = async (_req: Request, res: Response) => {
|
||||||
const aliases = DataProvider.getAliases();
|
const aliases = DataProvider.getAliases();
|
||||||
res.status(200).send(aliases);
|
res.status(200).send(aliases);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create controller for POST request to '/ontime/aliases'
|
// Create controller for POST request to '/ontime/presets'
|
||||||
// Returns ACK message
|
// Returns ACK message
|
||||||
export const postAliases = async (req: Request, res: Response) => {
|
export const postPreset = async (req: Request, res: Response) => {
|
||||||
if (failIsNotArray(req.body, res)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const newAliases: Alias[] = [];
|
const newAlias = {
|
||||||
req.body.forEach((a) => {
|
enabled: req.body.enabled,
|
||||||
newAliases.push({
|
alias: req.body.alias,
|
||||||
enabled: a.enabled,
|
pathAndParams: req.body.pathAndParams,
|
||||||
alias: a.alias,
|
};
|
||||||
pathAndParams: a.pathAndParams,
|
|
||||||
});
|
await DataProvider.addAlias(newAlias);
|
||||||
});
|
res.status(200).send(newAlias);
|
||||||
await DataProvider.setAliases(newAliases);
|
} catch (error) {
|
||||||
res.status(200).send(newAliases);
|
res.status(400).send({ message: String(error) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update controller for PUT request to '/ontime/presets/:preset'
|
||||||
|
// Returns ACK message
|
||||||
|
export const updatePreset = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { alias, enabled, pathAndParams } = req.body;
|
||||||
|
|
||||||
|
const aliases = DataProvider.getAliases();
|
||||||
|
|
||||||
|
const index = aliases.findIndex((a) => a.alias === alias);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return res.status(404).send({ message: `Alias ${alias} not found` });
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases[index] = {
|
||||||
|
alias,
|
||||||
|
enabled,
|
||||||
|
pathAndParams,
|
||||||
|
};
|
||||||
|
|
||||||
|
await DataProvider.setAliases(aliases);
|
||||||
|
|
||||||
|
res.status(200).send(aliases);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).send({ message: String(error) });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete controller for DELETE request to '/ontime/presets/:preset'
|
||||||
|
// Returns ACK message
|
||||||
|
export const deletePreset = async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const alias = req.params.alias;
|
||||||
|
|
||||||
|
const aliases = DataProvider.getAliases();
|
||||||
|
|
||||||
|
const index = aliases.findIndex((a) => a.alias === alias);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
return res.status(404).send({ message: `Alias ${alias} not found` });
|
||||||
|
}
|
||||||
|
|
||||||
|
aliases.splice(index, 1);
|
||||||
|
|
||||||
|
await DataProvider.setAliases(aliases);
|
||||||
|
|
||||||
|
res.status(200).send(aliases);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).send({ message: String(error) });
|
res.status(400).send({ message: String(error) });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ export const viewValidator = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Validates object for POST /ontime/aliases
|
* @description Validates object for POST /ontime/presets
|
||||||
*/
|
*/
|
||||||
export const validateAliases = [
|
export const validatePresets = [
|
||||||
body().isArray(),
|
body().isArray(),
|
||||||
body('*.enabled').isBoolean(),
|
body('*.enabled').isBoolean(),
|
||||||
body('*.alias').isString().trim(),
|
body('*.alias').isString().trim(),
|
||||||
@@ -39,6 +39,36 @@ export const validateAliases = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Validates object for POST /ontime/presets
|
||||||
|
*/
|
||||||
|
export const validateCreatePreset = [
|
||||||
|
body('alias').isString().trim(),
|
||||||
|
body('pathAndParams').isString().trim(),
|
||||||
|
body('enabled').isBoolean(),
|
||||||
|
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const errors = validationResult(req);
|
||||||
|
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Validates object for PUT /ontime/presets
|
||||||
|
*/
|
||||||
|
export const validateUpdatePreset = [
|
||||||
|
body('alias').isString().trim(),
|
||||||
|
body('pathAndParams').isString().trim().optional(),
|
||||||
|
body('enabled').isBoolean().optional(),
|
||||||
|
|
||||||
|
(req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const errors = validationResult(req);
|
||||||
|
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description Validates object for POST /ontime/userfields
|
* @description Validates object for POST /ontime/userfields
|
||||||
*/
|
*/
|
||||||
@@ -121,7 +151,7 @@ export const validatePatchProjectFile = [
|
|||||||
body('project').isObject().optional({ nullable: false }),
|
body('project').isObject().optional({ nullable: false }),
|
||||||
body('settings').isObject().optional({ nullable: false }),
|
body('settings').isObject().optional({ nullable: false }),
|
||||||
body('viewSettings').isObject().optional({ nullable: false }),
|
body('viewSettings').isObject().optional({ nullable: false }),
|
||||||
body('aliases').isArray().optional({ nullable: false }),
|
body('aliases').isArray().optional({ nullable: false }), // TODO
|
||||||
body('userFields').isObject().optional({ nullable: false }),
|
body('userFields').isObject().optional({ nullable: false }),
|
||||||
body('osc').isObject().optional({ nullable: false }),
|
body('osc').isObject().optional({ nullable: false }),
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { uploadFile } from '../utils/upload.js';
|
|||||||
import {
|
import {
|
||||||
dbDownload,
|
dbDownload,
|
||||||
dbUpload,
|
dbUpload,
|
||||||
getAliases,
|
|
||||||
getInfo,
|
getInfo,
|
||||||
getOSC,
|
getOSC,
|
||||||
getHTTP,
|
getHTTP,
|
||||||
@@ -12,7 +11,6 @@ import {
|
|||||||
getViewSettings,
|
getViewSettings,
|
||||||
patchPartialProjectFile,
|
patchPartialProjectFile,
|
||||||
poll,
|
poll,
|
||||||
postAliases,
|
|
||||||
postOSC,
|
postOSC,
|
||||||
postSettings,
|
postSettings,
|
||||||
postUserFields,
|
postUserFields,
|
||||||
@@ -32,10 +30,13 @@ import {
|
|||||||
postId,
|
postId,
|
||||||
getAuthentication,
|
getAuthentication,
|
||||||
getClientSecrect as getClientSecret,
|
getClientSecrect as getClientSecret,
|
||||||
|
postPreset,
|
||||||
|
updatePreset,
|
||||||
|
deletePreset,
|
||||||
|
getPresets,
|
||||||
} from '../controllers/ontimeController.js';
|
} from '../controllers/ontimeController.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
validateAliases,
|
|
||||||
validateOSC,
|
validateOSC,
|
||||||
validatePatchProjectFile,
|
validatePatchProjectFile,
|
||||||
validateSettings,
|
validateSettings,
|
||||||
@@ -48,6 +49,8 @@ import {
|
|||||||
validateSheetid,
|
validateSheetid,
|
||||||
validateWorksheet,
|
validateWorksheet,
|
||||||
validateSheetOptions,
|
validateSheetOptions,
|
||||||
|
validateCreatePreset,
|
||||||
|
validateUpdatePreset,
|
||||||
} from '../controllers/ontimeController.validate.js';
|
} from '../controllers/ontimeController.validate.js';
|
||||||
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
import { projectSanitiser } from '../controllers/projectController.validate.js';
|
||||||
import { sanitizeProjectFilename } from '../utils/sanitizeProjectFilename.js';
|
import { sanitizeProjectFilename } from '../utils/sanitizeProjectFilename.js';
|
||||||
@@ -82,10 +85,16 @@ router.get('/views', getViewSettings);
|
|||||||
router.post('/views', viewValidator, postViewSettings);
|
router.post('/views', viewValidator, postViewSettings);
|
||||||
|
|
||||||
// create route between controller and '/ontime/aliases' endpoint
|
// create route between controller and '/ontime/aliases' endpoint
|
||||||
router.get('/aliases', getAliases);
|
router.get('/presets', getPresets);
|
||||||
|
|
||||||
// create route between controller and '/ontime/aliases' endpoint
|
// create route between controller and '/ontime/aliases' endpoint
|
||||||
router.post('/aliases', validateAliases, postAliases);
|
router.post('/presets', validateCreatePreset, postPreset);
|
||||||
|
|
||||||
|
// create route between controller and /ontime/aliases' endpoint
|
||||||
|
router.put('/presets', validateUpdatePreset, updatePreset);
|
||||||
|
|
||||||
|
// create route between controller and '/ontime/aliases' endpoint
|
||||||
|
router.delete('/presets/:preset', deletePreset);
|
||||||
|
|
||||||
// create route between controller and '/ontime/aliases' endpoint
|
// create route between controller and '/ontime/aliases' endpoint
|
||||||
router.get('/userfields', getUserFields);
|
router.get('/userfields', getUserFields);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
parseSettings,
|
parseSettings,
|
||||||
parseUserFields,
|
parseUserFields,
|
||||||
parseViewSettings,
|
parseViewSettings,
|
||||||
|
parsePresets,
|
||||||
} from './parserFunctions.js';
|
} from './parserFunctions.js';
|
||||||
import { parseExcelDate } from './time.js';
|
import { parseExcelDate } from './time.js';
|
||||||
import { configService } from '../services/ConfigService.js';
|
import { configService } from '../services/ConfigService.js';
|
||||||
@@ -329,6 +330,7 @@ export const parseJson = async (jsonData: Partial<DatabaseModel>): Promise<Datab
|
|||||||
settings: parseSettings(jsonData) ?? dbModel.settings,
|
settings: parseSettings(jsonData) ?? dbModel.settings,
|
||||||
viewSettings: parseViewSettings(jsonData) ?? dbModel.viewSettings,
|
viewSettings: parseViewSettings(jsonData) ?? dbModel.viewSettings,
|
||||||
aliases: parseAliases(jsonData),
|
aliases: parseAliases(jsonData),
|
||||||
|
presets: parsePresets(jsonData),
|
||||||
userFields: parseUserFields(jsonData),
|
userFields: parseUserFields(jsonData),
|
||||||
osc: parseOsc(jsonData) ?? dbModel.osc,
|
osc: parseOsc(jsonData) ?? dbModel.osc,
|
||||||
http: parseHttp(jsonData) ?? dbModel.http,
|
http: parseHttp(jsonData) ?? dbModel.http,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
isOntimeBlock,
|
isOntimeBlock,
|
||||||
isOntimeCycle,
|
isOntimeCycle,
|
||||||
HttpSubscription,
|
HttpSubscription,
|
||||||
|
Presets,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
||||||
@@ -250,6 +251,32 @@ export const parseAliases = (data): Alias[] => {
|
|||||||
return newAliases;
|
return newAliases;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse presets portion of an entry
|
||||||
|
* @param {object} data - data object
|
||||||
|
* @returns {object} - event object data
|
||||||
|
*/
|
||||||
|
export const parsePresets = (data): Presets => {
|
||||||
|
const newPresets: { [key: string]: { alias: string; pathAndParams: string; enabled: boolean } } = {};
|
||||||
|
if ('presets' in data) {
|
||||||
|
console.log('Found Presets definition, importing...');
|
||||||
|
try {
|
||||||
|
for (const preset in data.presets) {
|
||||||
|
const newPreset = {
|
||||||
|
alias: data.presets[preset].alias,
|
||||||
|
pathAndParams: data.presets[preset].pathAndParams,
|
||||||
|
enabled: data.presets[preset].enabled,
|
||||||
|
};
|
||||||
|
newPresets[preset] = newPreset;
|
||||||
|
}
|
||||||
|
console.log(`Uploaded ${Object.keys(newPresets).length} preset(s)`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Error: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newPresets;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse userFields entry
|
* Parse userFields entry
|
||||||
* @param {object} data - data object
|
* @param {object} data - data object
|
||||||
|
|||||||
@@ -436,6 +436,13 @@
|
|||||||
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"presets": {
|
||||||
|
"test": {
|
||||||
|
"enabled": true,
|
||||||
|
"preset": "test",
|
||||||
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"userFields": {
|
"userFields": {
|
||||||
"user0": "user0",
|
"user0": "user0",
|
||||||
"user1": "user1",
|
"user1": "user1",
|
||||||
|
|||||||
@@ -436,6 +436,13 @@
|
|||||||
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"presets": {
|
||||||
|
"test": {
|
||||||
|
"enabled": true,
|
||||||
|
"preset": "test",
|
||||||
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"userFields": {
|
"userFields": {
|
||||||
"user0": "user0",
|
"user0": "user0",
|
||||||
"user1": "user1",
|
"user1": "user1",
|
||||||
|
|||||||
Vendored
+7
@@ -436,6 +436,13 @@
|
|||||||
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"presets": {
|
||||||
|
"test": {
|
||||||
|
"enabled": true,
|
||||||
|
"preset": "test",
|
||||||
|
"pathAndParams": "lower?bg=ff2&text=f00&size=0.6&transition=5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"userFields": {
|
"userFields": {
|
||||||
"user0": "user0",
|
"user0": "user0",
|
||||||
"user1": "user1",
|
"user1": "user1",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Settings } from './core/Settings.type.js';
|
|||||||
import { UserFields } from './core/UserFields.type.js';
|
import { UserFields } from './core/UserFields.type.js';
|
||||||
import { ViewSettings } from './core/Views.type.js';
|
import { ViewSettings } from './core/Views.type.js';
|
||||||
import { HttpSettings } from '../index.js';
|
import { HttpSettings } from '../index.js';
|
||||||
|
import { Presets } from './core/Presets.type.js';
|
||||||
|
|
||||||
export type DatabaseModel = {
|
export type DatabaseModel = {
|
||||||
rundown: OntimeRundown;
|
rundown: OntimeRundown;
|
||||||
@@ -13,6 +14,7 @@ export type DatabaseModel = {
|
|||||||
settings: Settings;
|
settings: Settings;
|
||||||
viewSettings: ViewSettings;
|
viewSettings: ViewSettings;
|
||||||
aliases: Alias[];
|
aliases: Alias[];
|
||||||
|
presets: Presets;
|
||||||
userFields: UserFields;
|
userFields: UserFields;
|
||||||
osc: OSCSettings;
|
osc: OSCSettings;
|
||||||
http: HttpSettings;
|
http: HttpSettings;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export type Presets = {
|
||||||
|
[key: string]: {
|
||||||
|
alias: string;
|
||||||
|
pathAndParams: string;
|
||||||
|
enabled: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -27,6 +27,9 @@ export type { TimeFormat } from './definitions/core/TimeFormat.type.js';
|
|||||||
// ---> Aliases
|
// ---> Aliases
|
||||||
export type { Alias } from './definitions/core/Alias.type.js';
|
export type { Alias } from './definitions/core/Alias.type.js';
|
||||||
|
|
||||||
|
// ---> Presets
|
||||||
|
export type { Presets } from './definitions/core/Presets.type.js';
|
||||||
|
|
||||||
// ---> User Fields
|
// ---> User Fields
|
||||||
export type { UserFields } from './definitions/core/UserFields.type.js';
|
export type { UserFields } from './definitions/core/UserFields.type.js';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user