JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour

File "appearance-layout.tsx"

Full Path: /home/markqprx/iniasli.pro/client/admin/appearance/appearance-layout.tsx
File size: 4.48 KB
MIME-type: text/html
Charset: utf-8

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>
  );
}