Files
mtdb_movie/common/resources/client/admin/appearance/appearance-layout.tsx
maher 703f50a09d
Some checks failed
Build / run (push) Has been cancelled
first commit
2025-10-29 11:42:25 +01:00

153 lines
4.5 KiB
TypeScript
Executable File

import {Link, Navigate, Outlet, useLocation} from 'react-router-dom';
import {useEffect, useRef} from 'react';
import {IconButton} from '../../ui/buttons/icon-button';
import {CloseIcon} from '../../icons/material/Close';
import {Button} from '../../ui/buttons/button';
import {appearanceState, AppearanceValues} from './appearance-store';
import {useSaveAppearanceChanges} from './requests/save-appearance-changes';
import {useAppearanceValues} from './requests/appearance-values';
import {Trans} from '../../i18n/trans';
import {useForm, useFormContext} from 'react-hook-form';
import {Form} from '../../ui/forms/form';
import {ProgressCircle} from '../../ui/progress/progress-circle';
import {SectionHeader} from './section-header';
import {FileUploadProvider} from '../../uploads/uploader/file-upload-provider';
import {useAppearanceEditorMode} from './commands/use-appearance-editor-mode';
import {StaticPageTitle} from '../../seo/static-page-title';
import {useSettings} from '../../core/settings/use-settings';
export function AppearanceLayout() {
const {isAppearanceEditorActive} = useAppearanceEditorMode();
const {data} = useAppearanceValues();
const {base_url} = useSettings();
const iframeRef = useRef<HTMLIFrameElement>(null);
const {pathname} = useLocation();
useEffect(() => {
// only set defaults snapshot once on route init
if (data?.defaults && !appearanceState().defaults) {
appearanceState().setDefaults(data.defaults);
}
}, [data]);
useEffect(() => {
if (iframeRef.current) {
appearanceState().setIframeWindow(iframeRef.current.contentWindow!);
}
}, []);
useEffect(() => {
const sectionName = pathname.split('/')[3];
appearanceState().preview.navigate(sectionName);
}, [pathname]);
// make sure appearance editor iframe can't be nested
if (isAppearanceEditorActive) {
return <Navigate to="/admin" />;
}
return (
<div className="h-screen items-center md:flex">
<StaticPageTitle>
<Trans message="Appearance" />
</StaticPageTitle>
<Sidebar values={data?.values} />
<div className="relative h-full flex-auto">
<iframe
ref={iframeRef}
className="h-full w-full max-md:hidden"
src={`${base_url}?appearanceEditor=true`}
/>
</div>
</div>
);
}
interface SidebarProps {
values: AppearanceValues | undefined;
}
function Sidebar({values}: SidebarProps) {
const spinner = (
<div className="flex h-full flex-auto items-center justify-center">
<ProgressCircle isIndeterminate aria-label="Loading editor" />
</div>
);
return (
<div className="relative z-10 h-full w-full border-r bg shadow-lg @container md:w-320">
{values ? <AppearanceForm defaultValues={values} /> : spinner}
</div>
);
}
interface AppearanceFormProps {
defaultValues: AppearanceValues;
}
function AppearanceForm({defaultValues}: AppearanceFormProps) {
const form = useForm<AppearanceValues>({defaultValues});
const {watch, reset} = form;
const saveChanges = useSaveAppearanceChanges();
useEffect(() => {
const subscription = watch(value => {
appearanceState().preview.setValues(value as AppearanceValues);
});
return () => subscription.unsubscribe();
}, [watch]);
return (
<Form
className="flex h-full flex-col"
form={form}
onSubmit={values => {
saveChanges.mutate(values, {
onSuccess: () => reset(values),
});
}}
>
<Header isLoading={saveChanges.isPending} />
<SectionHeader />
<div className="flex-auto overflow-y-auto px-14 py-20">
<FileUploadProvider>
<Outlet />
</FileUploadProvider>
</div>
</Form>
);
}
interface HeaderProps {
isLoading: boolean;
}
function Header({isLoading}: HeaderProps) {
const {
formState: {dirtyFields},
} = useFormContext<AppearanceValues>();
const isDirty = Object.keys(dirtyFields).length;
return (
<div className="flex h-50 flex-shrink-0 items-center border-b pr-10">
<IconButton
border="border-r"
className="text-muted"
elementType={Link}
to=".."
>
<CloseIcon />
</IconButton>
<div className="pl-10">
<Trans message="Appearance editor" />
</div>
<Button
variant="flat"
color="primary"
className="ml-auto block"
disabled={!isDirty || isLoading}
type="submit"
>
{isDirty ? <Trans message="Save" /> : <Trans message="Saved" />}
</Button>
</div>
);
}