deffiretiate main and custom edits

This commit is contained in:
arc-alex
2025-05-28 16:58:41 +02:00
parent 549c291f56
commit 0ff5f1b79b
9 changed files with 83 additions and 18 deletions
+10 -1
View File
@@ -20,8 +20,17 @@ export async function generateUrl(
baseUrl: string, baseUrl: string,
path: string, path: string,
lock: boolean, lock: boolean,
lockMainFields: boolean,
lockCustomFields: boolean,
authenticate: boolean, authenticate: boolean,
): Promise<string> { ): Promise<string> {
const res = await axios.post(`${sessionPath}/url`, { baseUrl, path, lock, authenticate }); const res = await axios.post(`${sessionPath}/url`, {
baseUrl,
path,
lock,
lockMainFields,
lockCustomFields,
authenticate,
});
return res.data.url; return res.data.url;
} }
+23 -5
View File
@@ -45,11 +45,14 @@ export function getRouteFromPreset(location: Path, urlPresets: URLPreset[]): str
const locked = searchParams.get('locked'); const locked = searchParams.get('locked');
const token = searchParams.get('token'); const token = searchParams.get('token');
const lmain = searchParams.get('lmain');
const lcustom = searchParams.get('lcustom');
// we need to check if the whole url is an alias // we need to check if the whole url is an alias
const foundPreset = urlPresets.find((preset) => preset.alias === removeTrailingSlash(currentURL) && preset.enabled); const foundPreset = urlPresets.find((preset) => preset.alias === removeTrailingSlash(currentURL) && preset.enabled);
if (foundPreset) { if (foundPreset) {
// if so, we can redirect to the preset path // if so, we can redirect to the preset path
return generatePathFromPreset(foundPreset.pathAndParams, foundPreset.alias, locked, token); return generatePathFromPreset(foundPreset.pathAndParams, foundPreset.alias, locked, token, lmain, lcustom);
} }
// if the current url is not an alias, we check if the alias is in the search parameters // if the current url is not an alias, we check if the alias is in the search parameters
@@ -63,7 +66,7 @@ export function getRouteFromPreset(location: Path, urlPresets: URLPreset[]): str
for (const preset of urlPresets) { for (const preset of urlPresets) {
// if the page has a known enabled alias, we check if we need to redirect // if the page has a known enabled alias, we check if we need to redirect
if (preset.alias === presetOnPage && preset.enabled) { if (preset.alias === presetOnPage && preset.enabled) {
const newPath = generatePathFromPreset(preset.pathAndParams, preset.alias, locked, token); const newPath = generatePathFromPreset(preset.pathAndParams, preset.alias, locked, token, lmain, lcustom);
if (!arePathsEquivalent(currentPath, newPath)) { if (!arePathsEquivalent(currentPath, newPath)) {
// if current path is out of date // if current path is out of date
// return new path so we can redirect // return new path so we can redirect
@@ -77,7 +80,14 @@ export function getRouteFromPreset(location: Path, urlPresets: URLPreset[]): str
/** /**
* Handles generating a path and search parameters from a preset * Handles generating a path and search parameters from a preset
*/ */
export function generatePathFromPreset(pathAndParams: string, alias: string, locked: string | null, token: string | null ): string { export function generatePathFromPreset(
pathAndParams: string,
alias: string,
locked: string | null,
token: string | null,
lmain: string | null,
lcustom: string | null,
): string {
const path = resolvePath(pathAndParams); const path = resolvePath(pathAndParams);
const searchParams = new URLSearchParams(path.search); const searchParams = new URLSearchParams(path.search);
@@ -93,6 +103,14 @@ export function generatePathFromPreset(pathAndParams: string, alias: string, loc
searchParams.set('token', token); searchParams.set('token', token);
} }
if (lmain) {
searchParams.set('lmain', lmain);
}
if (lcustom) {
searchParams.set('lcustom', lcustom);
}
// return path concatenated without the leading slash // return path concatenated without the leading slash
return `${path.pathname}?${searchParams}`.substring(1); return `${path.pathname}?${searchParams}`.substring(1);
} }
@@ -109,13 +127,13 @@ export function arePathsEquivalent(currentPath: string, newPath: string): boolea
// check path // check path
if (currentUrl.pathname !== newUrl.pathname) { if (currentUrl.pathname !== newUrl.pathname) {
return false return false;
} }
// check search params // check search params
// if the params match, we dont need further checks // if the params match, we dont need further checks
if (currentUrl.searchParams.toString() === newUrl.searchParams.toString()) { if (currentUrl.searchParams.toString() === newUrl.searchParams.toString()) {
return true return true;
} }
// if there is no match, we check the edge cases for the url sharing feature // if there is no match, we check the edge cases for the url sharing feature
+2 -1
View File
@@ -31,7 +31,8 @@ declare module '@tanstack/react-table' {
options: { options: {
showDelayedTimes: boolean; showDelayedTimes: boolean;
hideTableSeconds: boolean; hideTableSeconds: boolean;
allowEdits: boolean; allowMainEdits: boolean;
allowCustomEdits: boolean;
}; };
} }
} }
@@ -20,6 +20,8 @@ interface GenerateLinkFormOptions {
baseUrl: string; baseUrl: string;
path: string; path: string;
lock: boolean; lock: boolean;
lockMainFields: boolean;
lockCustomFields: boolean;
authenticate: boolean; authenticate: boolean;
} }
@@ -35,13 +37,15 @@ export default function GenerateLinkForm() {
handleSubmit, handleSubmit,
register, register,
setError, setError,
formState: { errors }, formState: { errors, dirtyFields },
} = useForm<GenerateLinkFormOptions>({ } = useForm<GenerateLinkFormOptions>({
mode: 'onChange', mode: 'onChange',
defaultValues: { defaultValues: {
baseUrl: currentHostName, baseUrl: currentHostName,
path: '', path: '',
lock: false, lock: false,
lockMainFields: false,
lockCustomFields: false,
authenticate: false, authenticate: false,
}, },
resetOptions: { resetOptions: {
@@ -53,7 +57,14 @@ export default function GenerateLinkForm() {
try { try {
setFormState('loading'); setFormState('loading');
const baseUrl = linkToOtherHost(options.baseUrl); const baseUrl = linkToOtherHost(options.baseUrl);
const url = await generateUrl(baseUrl, options.path, options.lock, options.authenticate); const url = await generateUrl(
baseUrl,
options.path,
options.lock,
options.lockMainFields,
options.lockCustomFields,
options.authenticate,
);
await copyToClipboard(url); await copyToClipboard(url);
setUrl(url); setUrl(url);
setFormState('success'); setFormState('success');
@@ -119,6 +130,18 @@ export default function GenerateLinkForm() {
/> />
<Switch variant='ontime' size='lg' {...register('lock')} /> <Switch variant='ontime' size='lg' {...register('lock')} />
</Panel.ListItem> </Panel.ListItem>
{dirtyFields.lock && (
<>
<Panel.ListItem>
<Panel.Field title='Lock main field edits' description='Prevent edits to main fields' />
<Switch variant='ontime' size='lg' {...register('lockMainFields')} />
</Panel.ListItem>
<Panel.ListItem>
<Panel.Field title='Lock custom field edits' description='Prevent edits to custom fields' />
<Switch variant='ontime' size='lg' {...register('lockCustomFields')} />
</Panel.ListItem>
</>
)}
<Panel.ListItem> <Panel.ListItem>
<Panel.Field title='Authenticate' description='Whether the URL should be pre-authenticated' /> <Panel.Field title='Authenticate' description='Whether the URL should be pre-authenticated' />
<Switch variant='ontime' size='lg' {...register('authenticate')} /> <Switch variant='ontime' size='lg' {...register('authenticate')} />
@@ -27,7 +27,8 @@ export default function CuesheetTable(props: CuesheetTableProps) {
const { updateEvent, updateTimer } = useEventAction(); const { updateEvent, updateTimer } = useEventAction();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const blockEdits = searchParams.get('locked') ?? false; const allowMainEdits = !searchParams.get('lmain');
const allowCustomEdits = !searchParams.get('lcustom');
const { followSelected, showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); const { followSelected, showDelayedTimes, hideTableSeconds } = useCuesheetOptions();
const { columnVisibility, columnOrder, columnSizing, resetColumnOrder, setColumnVisibility, setColumnSizing } = const { columnVisibility, columnOrder, columnSizing, resetColumnOrder, setColumnVisibility, setColumnSizing } =
@@ -81,7 +82,8 @@ export default function CuesheetTable(props: CuesheetTableProps) {
options: { options: {
showDelayedTimes, showDelayedTimes,
hideTableSeconds, hideTableSeconds,
allowEdits: !blockEdits, allowMainEdits,
allowCustomEdits,
}, },
}, },
}); });
@@ -37,7 +37,7 @@ function MakeStart({ getValue, row, table }: CellContext<OntimeRundownEntry, unk
onSubmit={update} onSubmit={update}
lockedValue={isStartLocked} lockedValue={isStartLocked}
delayed={delayValue !== 0} delayed={delayValue !== 0}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowMainEdits}
> >
{formattedTime} {formattedTime}
<DelayIndicator delayValue={delayValue} tooltipPrefix={millisToString(startTime)} /> <DelayIndicator delayValue={delayValue} tooltipPrefix={millisToString(startTime)} />
@@ -71,7 +71,7 @@ function MakeEnd({ getValue, row, table }: CellContext<OntimeRundownEntry, unkno
onSubmit={update} onSubmit={update}
lockedValue={isEndLocked} lockedValue={isEndLocked}
delayed={delayValue !== 0} delayed={delayValue !== 0}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowMainEdits}
> >
{formattedTime} {formattedTime}
<DelayIndicator delayValue={delayValue} tooltipPrefix={millisToString(endTime)} /> <DelayIndicator delayValue={delayValue} tooltipPrefix={millisToString(endTime)} />
@@ -97,7 +97,7 @@ function MakeDuration({ getValue, row, table }: CellContext<OntimeRundownEntry,
initialValue={duration} initialValue={duration}
onSubmit={update} onSubmit={update}
lockedValue={isDurationLocked} lockedValue={isDurationLocked}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowMainEdits}
> >
{formattedDuration} {formattedDuration}
</TimeInput> </TimeInput>
@@ -124,7 +124,7 @@ function MakeMultiLineField({ row, column, table }: CellContext<OntimeRundownEnt
<MultiLineCell <MultiLineCell
initialValue={initialValue} initialValue={initialValue}
handleUpdate={update} handleUpdate={update}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowMainEdits}
/> />
); );
} }
@@ -167,7 +167,7 @@ function MakeSingleLineField({ row, column, table }: CellContext<OntimeRundownEn
<SingleLineCell <SingleLineCell
initialValue={initialValue} initialValue={initialValue}
handleUpdate={update} handleUpdate={update}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowMainEdits}
/> />
); );
} }
@@ -191,7 +191,7 @@ function MakeCustomField({ row, column, table }: CellContext<OntimeRundownEntry,
<MultiLineCell <MultiLineCell
initialValue={initialValue} initialValue={initialValue}
handleUpdate={update} handleUpdate={update}
allowEdits={table.options.meta?.options.allowEdits} allowEdits={table.options.meta?.options.allowCustomEdits}
/> />
); );
} }
@@ -31,6 +31,8 @@ export async function generateUrl(req: Request, res: Response<GetUrl | ErrorResp
req.body.baseUrl, req.body.baseUrl,
req.body.path, req.body.path,
req.body.lock, req.body.lock,
req.body.lockMainFields,
req.body.lockCustomFields,
req.body.authenticate, req.body.authenticate,
); );
res.status(200).send({ url: url.toString() }); res.status(200).send({ url: url.toString() });
@@ -59,6 +59,8 @@ export function generateAuthenticatedUrl(
baseUrl: string, baseUrl: string,
path: string, path: string,
lock: boolean, lock: boolean,
lockMainFields: boolean,
lockCustomFields: boolean,
authenticate: boolean, authenticate: boolean,
prefix = routerPrefix, prefix = routerPrefix,
hash = hashedPassword, hash = hashedPassword,
@@ -72,5 +74,11 @@ export function generateAuthenticatedUrl(
if (lock) { if (lock) {
url.searchParams.append('locked', 'true'); url.searchParams.append('locked', 'true');
} }
if (lockMainFields) {
url.searchParams.append('lmain', 'true');
}
if (lockCustomFields) {
url.searchParams.append('lcustom', 'true');
}
return url; return url;
} }
@@ -5,6 +5,8 @@ export const validateGenerateUrl = [
body('baseUrl').exists().isString().notEmpty().trim(), body('baseUrl').exists().isString().notEmpty().trim(),
body('path').exists().isString().trim(), body('path').exists().isString().trim(),
body('lock').exists().isBoolean(), body('lock').exists().isBoolean(),
body('lockMainFields').exists().isBoolean(),
body('lockCustomFields').exists().isBoolean(),
body('authenticate').exists().isBoolean(), body('authenticate').exists().isBoolean(),
(req: Request, res: Response, next: NextFunction) => { (req: Request, res: Response, next: NextFunction) => {