feat: expose presets in view options

This commit is contained in:
Carlos Valente
2025-07-21 16:19:09 +02:00
committed by Carlos Valente
parent 5f8a5c4995
commit 307f0ffd34
16 changed files with 111 additions and 27 deletions
@@ -0,0 +1,45 @@
import { useSearchParams } from 'react-router-dom';
import { OntimeView, URLPreset } from 'ontime-types';
import { useViewUrlPresets } from '../../hooks-query/useUrlPresets';
import { cx } from '../../utils/styleUtils';
import Button from '../buttons/Button';
import style from './ViewParamsShare.module.scss';
/**
* Shows a list of presets for the current view
*/
export function ViewParamsShare({ target }: { target: OntimeView }) {
const { viewPresets } = useViewUrlPresets(target);
const [_, setSearchParams] = useSearchParams();
const handleRecall = (preset: URLPreset) => {
setSearchParams(`${preset.search}&alias=${preset.alias}`);
};
if (viewPresets.length === 0) {
return null;
}
return (
<div className={style.presetSection}>
{viewPresets.map((preset) => {
const active = window.location.search.includes(`alias=${preset.alias}`);
return (
<div key={preset.alias} className={cx([style.preset, active && style.active])}>
<div>{preset.alias}</div>
<Button
variant='subtle-white'
onClick={() => handleRecall(preset)}
disabled={active}
className={style.presetActions}
>
{active ? 'Applied' : 'Apply'}
</Button>
</div>
);
})}
</div>
);
}
@@ -1,7 +1,8 @@
import { FormEvent, memo } from 'react';
import { FormEvent, memo, useReducer } from 'react';
import { IoClose } from 'react-icons/io5';
import { useSearchParams } from 'react-router-dom';
import { Dialog } from '@base-ui-components/react/dialog';
import { OntimeView } from 'ontime-types';
import useViewSettings from '../../hooks-query/useViewSettings';
import Button from '../buttons/Button';
@@ -11,20 +12,23 @@ import Info from '../info/Info';
import { ViewOption } from './viewParams.types';
import { getURLSearchParamsFromObj } from './viewParams.utils';
import { useViewParamsEditorStore } from './viewParamsEditor.store';
import { ViewParamsShare } from './ViewParamShare';
import ViewParamsSection from './ViewParamsSection';
import style from './ViewParamsEditor.module.scss';
interface EditFormDrawerProps {
target: OntimeView;
viewOptions: ViewOption[];
}
export default memo(ViewParamsEditor);
function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
function ViewParamsEditor({ target, viewOptions }: EditFormDrawerProps) {
const [_, setSearchParams] = useSearchParams();
const { data: viewSettings } = useViewSettings();
const { isOpen, close } = useViewParamsEditorStore();
// TODO: we dont want this as a permanent option
const forceRender = useReducer((x) => x + 1, 0)[1];
const handleClose = () => {
close();
@@ -32,6 +36,7 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
const resetParams = () => {
setSearchParams();
forceRender();
};
const onParamsFormSubmit = (formEvent: FormEvent<HTMLFormElement>) => {
@@ -40,6 +45,7 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
const newParamsObject = Object.fromEntries(new FormData(formEvent.currentTarget));
const newSearchParams = getURLSearchParamsFromObj(newParamsObject, viewOptions);
setSearchParams(newSearchParams);
forceRender();
};
return (
@@ -64,6 +70,7 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
{viewSettings.overrideStyles && (
<Info className={style.info}>This view style is being modified by a custom CSS file.</Info>
)}
<ViewParamsShare target={target} />
<form id='edit-params-form' onSubmit={onParamsFormSubmit} className={style.sectionList}>
{viewOptions.map((section) => (
<ViewParamsSection
@@ -80,7 +87,7 @@ function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) {
Reset to default
</Button>
<Button variant='primary' size='large' form='edit-params-form' type='submit'>
Save
Apply
</Button>
</div>
</Dialog.Popup>
@@ -36,11 +36,11 @@
}
.closed {
transition: rotate $transition-time-feedback;
transition: rotate 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
rotate: 0deg;
}
.open {
transition: rotate $transition-time-feedback;
transition: rotate 300ms cubic-bezier(0.45, 1.005, 0, 1.005);
rotate: 180deg;
}
@@ -15,8 +15,7 @@ interface ViewParamsSectionProps {
options: ParamField[];
}
export default memo(ViewParamsSection);
function ViewParamsSection({ title, collapsible, options }: ViewParamsSectionProps) {
export default function ViewParamsSection({ title, collapsible, options }: ViewParamsSectionProps) {
const [collapsed, setCollapsed] = useLocalStorage({ key: `params-${title}`, defaultValue: false });
const handleCollapse = () => {
@@ -0,0 +1,22 @@
.presetSection {
background-color: $gray-1350;
display: flex;
flex-direction: column;
gap: 0.25rem;
padding: 1rem 0.5rem;
margin-bottom: 1rem;
}
.preset {
display: flex;
align-items: center;
gap: 0.5rem;
&.active {
color: $blue-500;
}
}
.presetActions {
margin-left: auto;
}
@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { URLPreset } from 'ontime-types';
import { OntimeView, URLPreset } from 'ontime-types';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { URL_PRESETS } from '../api/constants';
@@ -21,6 +22,14 @@ export default function useUrlPresets({ skip = false }: FetchProps = {}) {
return { data: data ?? [], status, isError, refetch };
}
export function useViewUrlPresets(view: OntimeView) {
const { data } = useUrlPresets();
const viewPresets = useMemo(() => data.filter((preset) => preset.target === view), [data, view]);
return { viewPresets };
}
export function useUpdateUrlPreset() {
const queryClient = useQueryClient();
+2 -2
View File
@@ -136,9 +136,9 @@ export function arePathsEquivalent(currentPath: string, newPath: string): boolea
* Generates a URL preset from a user given alias and URL.
*/
export function generateUrlPresetOptions(alias: string, userUrl: string): URLPreset {
let sanitisedUrl = userUrl.toLowerCase();
let sanitisedUrl = userUrl;
// we need to ensure the URL has a protocol, but it doesnt matter which
if (!checkRegex.startsWithHttp(sanitisedUrl)) {
if (!checkRegex.startsWithHttp(userUrl.toLowerCase())) {
sanitisedUrl = `http://${sanitisedUrl}`;
}