app/javascript/components/PageForm.tsx
import { useEffect } from "react";
import { putJson, postJson } from "../lib/request";
import useToastStore from "../stores/useToastStore";
import * as Pages from "../types/Pages";
import * as Template from "../types/Template";
import { Locale } from "../types";
import { openPreview } from "./PageForm/preview";
import useAttachments from "./Attachments/useAttachments";
import useImageGrid from "./ImageGrid/useImageGrid";
import useTags from "./TagEditor/useTags";
import usePage from "./PageForm/usePage";
import { PageFormContext } from "./PageForm/usePageFormContext";
import useTabs from "./PageForm/useTabs";
import pageParams from "./PageForm/pageParams";
import Content from "./PageForm/Content";
import UnconfiguredContent from "./PageForm/UnconfiguredContent";
import Metadata from "./PageForm/Metadata";
import Form from "./PageForm/Form";
import PageDescription from "./PageForm/PageDescription";
import Options from "./PageForm/Options";
import Tabs from "./PageForm/Tabs";
import TabPanel from "./PageForm/TabPanel";
import Files from "./PageForm/Files";
import Images from "./PageForm/Images";
type Props = {
locale: string;
locales: { [index: string]: Locale };
page: Pages.SerializedResource;
templates: Template.Config[];
authors: Pages.Author[];
statuses: Pages.StatusLabels;
};
export default function PageForm(props: Props) {
const [state, dispatch] = usePage({
locales: props.locales,
locale: props.locale,
page: props.page,
templates: props.templates
});
const { page, locale, locales } = state;
const filesState = useAttachments(page.page_files);
const imagesState = useImageGrid(page.page_images, true);
const [tagsState, tagsDispatch] = useTags(
page.tags_and_suggestions,
page.enabled_tags
);
const [tabs, tab, setTab] = useTabs(state);
const errorToast = useToastStore((state) => state.error);
const noticeToast = useToastStore((state) => state.notice);
const params = () => {
return pageParams(state, {
files: filesState,
images: imagesState,
tags: tagsState
});
};
useEffect(() => {
const parentParam = page.parent_page_id
? `?parent=${page.parent_page_id}`
: "";
const pageUrl =
`/admin/${locale}/pages/` +
(page.id ? `${page.id}/edit` : `new${parentParam}`) +
`#${tab}`;
if (history) {
history.replaceState(null, "", pageUrl);
}
}, [page.id, page.parent_page_id, locale, tab]);
const handlePreview = (evt: React.MouseEvent) => {
evt.preventDefault();
openPreview(`/${locale}/pages/preview`, {
page_id: `${page.id}`,
preview_page: JSON.stringify(params())
});
};
const handleSubmit = (evt: React.MouseEvent) => {
evt.preventDefault();
let method = postJson;
let url = `/admin/${locale}/pages.json`;
const data = {
page: pageParams(state, {
files: filesState,
images: imagesState,
tags: tagsState
})
};
if (page.id) {
method = putJson;
url = `/admin/${locale}/pages/${page.id}.json`;
}
method(url, data)
.then((response: Pages.SerializedResource) => {
dispatch({ type: "setPage", payload: response });
if (response.errors && response.errors.length > 0) {
errorToast("A validation error prevented the page from being saved.");
} else {
filesState.update(response.page_files);
imagesState.update(response.page_images);
tagsDispatch({
type: "update",
payload: {
tags: response.tags_and_suggestions,
enabled: response.enabled_tags
}
});
noticeToast("Your changes were saved");
}
})
.catch(() => {
errorToast("An error occured while saving your changes.");
});
};
return (
<PageFormContext.Provider
value={{
state: state,
dispatch: dispatch
}}>
<Form>
<main>
<PageDescription>
<Tabs tabs={tabs} tab={tab} setTab={setTab} />
</PageDescription>
<div className="content">
<TabPanel active={tab == "content"}>
<Content tagsState={tagsState} tagsDispatch={tagsDispatch} />
</TabPanel>
<TabPanel active={tab == "unconfigured-content"}>
<UnconfiguredContent />
</TabPanel>
<TabPanel active={tab == "images"}>
<Images locale={locale} locales={locales} state={imagesState} />
</TabPanel>
<TabPanel active={tab == "files"}>
<Files locale={locale} locales={locales} state={filesState} />
</TabPanel>
<TabPanel active={tab == "metadata"}>
<Metadata />
</TabPanel>
<div className="buttons">
<button type="button" onClick={handlePreview}>
Preview
</button>
<button type="submit" onClick={handleSubmit}>
Save
</button>
</div>
</div>
</main>
<aside className="sidebar">
<Options authors={props.authors} statuses={props.statuses} />
</aside>
</Form>
</PageFormContext.Provider>
);
}