412 lines
13 KiB
TypeScript
412 lines
13 KiB
TypeScript
import { useForm, usePage } from "@inertiajs/react";
|
|
import {
|
|
LinkIcon,
|
|
BookTextIcon,
|
|
CogIcon,
|
|
EyeIcon,
|
|
ShieldAlertIcon,
|
|
} from "lucide-react";
|
|
|
|
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
|
|
import DynamicInputs from "@/back/Components/DynamicPages/DynamicInputs";
|
|
import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
|
|
import GoToFrontView from "@/back/Components/GoToFrontView";
|
|
import InputError from "@/back/Components/InputError";
|
|
import EnumSelect from "@/back/Components/Inputs/EnumSelect";
|
|
import { ModelSelect } from "@/back/Components/Inputs/ModelSelect";
|
|
import SimpleString from "@/back/Components/Inputs/SimpleString";
|
|
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
|
|
import Slugifycator from "@/back/Components/Inputs/Slugifycator";
|
|
import SimpleWysiwyg from "@/back/Components/Inputs/Wysiwyg/SimpleWysiwyg";
|
|
import ConfigDetails from "@/back/Components/PageConfigs/ConfigDetails";
|
|
import SubmitButton from "@/back/Components/SubmitButton";
|
|
import useCrudSubmit from "@/back/hooks/use-crud-submit";
|
|
import type {
|
|
ConfigField,
|
|
DynamicPageModel,
|
|
PageConfigModel,
|
|
} from "@/back/types/dynamic-pages";
|
|
import type { AuthProp, ConfigBackend, UserModel } from "@/back/types/global";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Dialog,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogDescription,
|
|
DialogClose,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog";
|
|
import str from "@/hooks/use-string";
|
|
import useTrans from "@/hooks/use-translations";
|
|
|
|
export default function Form({
|
|
model,
|
|
modelName,
|
|
modelRoute,
|
|
action,
|
|
method,
|
|
}: {
|
|
model?: DynamicPageModel;
|
|
modelName: string;
|
|
modelRoute: string;
|
|
action: string;
|
|
method: number;
|
|
}) {
|
|
/** Global */
|
|
const { locale, url } = usePage().props.appEnv as AppEnv;
|
|
const routeName = (usePage().props.configBackend as ConfigBackend).routes
|
|
.name;
|
|
const dynamicPageUrl = usePage().props.dynamicPageUrl as string | null;
|
|
const userModel = (usePage().props.auth as AuthProp).user;
|
|
const enumUserRole = usePage().props.enumUserRole as UserModel["role"][];
|
|
const getEnumRoleValue = (name: string) =>
|
|
enumUserRole.find((role) => role.name === name)?.value;
|
|
const isConceptor = userModel.role.value === getEnumRoleValue("conceptor");
|
|
const enumDynamicPageStatus = usePage().props
|
|
.enumDynamicPageStatus as LaravelEnum;
|
|
const pageConfigModels = usePage().props
|
|
.pageConfigModels as PageConfigModel[];
|
|
|
|
// Models filtered based on the presence of the locale.
|
|
const filteredPageConfigModels = pageConfigModels.filter(
|
|
(pageConfigModel) =>
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
pageConfigModel.label[locale] !== undefined &&
|
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
pageConfigModel.config[locale] !== undefined,
|
|
);
|
|
|
|
// Set empty config by default.
|
|
const defaultConfigId = 0;
|
|
const configOptions = [
|
|
{
|
|
id: defaultConfigId,
|
|
label: {},
|
|
config: {},
|
|
policies: {
|
|
view: true,
|
|
update: true,
|
|
duplicate: true,
|
|
delete: true,
|
|
},
|
|
view: null,
|
|
created_at: new Date(),
|
|
updated_at: new Date(),
|
|
deleted_at: new Date(),
|
|
} as PageConfigModel,
|
|
...filteredPageConfigModels,
|
|
];
|
|
configOptions[defaultConfigId].label[locale] = useTrans(
|
|
"uwil-dynamic-pages::trans.config_none_selected",
|
|
);
|
|
|
|
// Set translations.
|
|
const contentAssociateToModel = str(
|
|
useTrans("uwil-backend::back.form_associate_to", {
|
|
params: {
|
|
attribute: useTrans("uwil-dynamic-pages::trans.attributes.content"),
|
|
},
|
|
choice: 1,
|
|
}),
|
|
)
|
|
.ucFirst()
|
|
.value();
|
|
const modalTitle = useTrans("uwil-dynamic-pages::trans.config_modal_title");
|
|
const modalDescription = useTrans(
|
|
"uwil-dynamic-pages::trans.config_modal_descripion",
|
|
);
|
|
const modalClose = useTrans("uwil-backend::back.close");
|
|
const anchorAccessConfig = useTrans(
|
|
"uwil-dynamic-pages::trans.access_model",
|
|
{
|
|
params: {
|
|
model: useTrans("uwil-dynamic-pages::models.classes.page-configs", {
|
|
choice: 1,
|
|
}),
|
|
},
|
|
},
|
|
);
|
|
const transValidationRootLabel = useTrans(
|
|
"uwil-dynamic-pages::trans.attributes.root",
|
|
);
|
|
const transValidationParentPageLabel = useTrans(
|
|
"uwil-backend::back.form_associate_to",
|
|
{
|
|
params: {
|
|
attribute: useTrans("uwil-dynamic-pages::trans.attributes.parent_page"),
|
|
},
|
|
choice: 2,
|
|
},
|
|
);
|
|
const transAssociateToStatus = useTrans(
|
|
"uwil-backend::back.form_associate_to",
|
|
{
|
|
params: {
|
|
attribute: useTrans("uwil-dynamic-pages::trans.attributes.status"),
|
|
},
|
|
choice: 1,
|
|
},
|
|
);
|
|
const transFormTitleIsLocked = useTrans(
|
|
"uwil-dynamic-pages::trans.form_title_isLocked",
|
|
);
|
|
const transFormSubtitleIsLocked = useTrans(
|
|
"uwil-dynamic-pages::trans.form_subtitle_isLocked",
|
|
);
|
|
|
|
const getConfig = (id: number): ConfigField[] => {
|
|
return configOptions.find((obj) => obj.id === id)?.config[locale] ?? [];
|
|
};
|
|
|
|
/** Form section */
|
|
const { post, setData, data, reset } = useForm({
|
|
title: model?.title ? model.title[locale] : "",
|
|
slug: model?.slug ? model.slug[locale] : "",
|
|
content: model?.content ? model.content[locale] : "",
|
|
page_id: model?.page_id ?? null,
|
|
config_id: model?.config_id ?? configOptions[defaultConfigId].id,
|
|
status: model?.status ?? enumDynamicPageStatus.draft.value,
|
|
isLocked: model?.isLocked ?? false,
|
|
isRoot: model?.isRoot ?? false,
|
|
});
|
|
const defaultIsRootValue = !data.isRoot && data.page_id ? [data.page_id] : [];
|
|
const submit = useCrudSubmit({
|
|
method,
|
|
modelRoute,
|
|
action,
|
|
model,
|
|
post,
|
|
data,
|
|
modelNameTrans: useTrans(
|
|
`uwil-dynamic-pages::models.classes.${modelName}`,
|
|
{
|
|
choice: 1,
|
|
},
|
|
),
|
|
});
|
|
|
|
return (
|
|
<form onSubmit={submit} className="grid grid-cols-2 gap-4">
|
|
<InputError all />
|
|
<FieldsetWrapper
|
|
title={useTrans("uwil-backend::back.form_title_global")}
|
|
icon={<BookTextIcon className="mr-1" />}
|
|
description={useTrans("uwil-backend::back.form_subtitle_global")}
|
|
className="sm:grid-cols-2 gap-4"
|
|
>
|
|
<SimpleString
|
|
name="title"
|
|
label={str(
|
|
useTrans("uwil-backend::back.form_associate_to", {
|
|
params: {
|
|
attribute: useTrans(
|
|
"uwil-dynamic-pages::trans.attributes.title",
|
|
),
|
|
},
|
|
choice: 1,
|
|
}),
|
|
)
|
|
.ucFirst()
|
|
.value()}
|
|
value={data.title}
|
|
setData={setData}
|
|
/>
|
|
{(!data.isLocked || isConceptor) && (
|
|
<EnumSelect
|
|
name="status"
|
|
options={enumDynamicPageStatus}
|
|
label={str(transAssociateToStatus).ucFirst().value()}
|
|
placeholder={`ex: ${enumDynamicPageStatus.draft.label}`}
|
|
selected={data.status}
|
|
setData={setData}
|
|
/>
|
|
)}
|
|
</FieldsetWrapper>
|
|
<FieldsetWrapper
|
|
title={useTrans(
|
|
"uwil-dynamic-pages::trans.form_title_page_accessibility",
|
|
)}
|
|
icon={<LinkIcon className="mr-1" />}
|
|
description={useTrans(
|
|
"uwil-dynamic-pages::trans.form_subtitle_page_accessibility",
|
|
)}
|
|
className="sm:grid-cols-2 gap-4"
|
|
>
|
|
{isConceptor && (
|
|
<div className="col-span-1 md:col-span-2">
|
|
<SimpleSwitch
|
|
label={str(transValidationRootLabel).ucFirst().value()}
|
|
name="isRoot"
|
|
checked={data.isRoot}
|
|
setData={(name, value) => {
|
|
setData("page_id", null);
|
|
setData("slug", "");
|
|
// @ts-expect-error TS2345: 'name' type is dynamic, TypeScript cannot infer the correct key.
|
|
setData(name, value);
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
{!data.isRoot && (
|
|
<div className="col-span-2 grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<ModelSelect
|
|
name="page_id"
|
|
targetModel="dynamic_pages"
|
|
multiple={false}
|
|
field="title"
|
|
closeOnSelect
|
|
label={str(transValidationParentPageLabel).ucFirst().value()}
|
|
placeholder="ex: "
|
|
defaultValues={defaultIsRootValue}
|
|
exclude={model?.id}
|
|
required={false}
|
|
setData={setData}
|
|
disabled={data.isRoot}
|
|
/>
|
|
<Slugifycator
|
|
value={data.slug}
|
|
targetValue={data.title}
|
|
setData={setData}
|
|
disabled={data.isRoot}
|
|
/>
|
|
</div>
|
|
)}
|
|
<div className="col-span-2">
|
|
<GoToFrontView
|
|
modelName={useTrans(
|
|
`uwil-dynamic-pages::models.classes.${modelName}`,
|
|
{
|
|
choice: 1,
|
|
},
|
|
)}
|
|
pathField={
|
|
data.isRoot
|
|
? ""
|
|
: (dynamicPageUrl ?? "").replace(url, "").replace(/^\/+/, "")
|
|
}
|
|
isAvailable={
|
|
model?.status === enumDynamicPageStatus.published.value
|
|
}
|
|
/>
|
|
</div>
|
|
</FieldsetWrapper>
|
|
<FieldsetWrapper
|
|
title={useTrans("uwil-dynamic-pages::trans.form_title_page")}
|
|
icon={<CogIcon className="mr-1" />}
|
|
description={useTrans("uwil-dynamic-pages::trans.form_subtitle_page")}
|
|
className="gap-y-4"
|
|
>
|
|
<div className="grid sm:grid-cols-2 gap-4 border-b border-input pb-4">
|
|
<div className="flex justify-start items-end gap-1.5">
|
|
<ModelSelect
|
|
name="config_id"
|
|
targetModel="page_configs"
|
|
multiple={false}
|
|
closeOnSelect
|
|
field="label"
|
|
responsive
|
|
label={str(
|
|
useTrans("uwil-backend::back.form_associate_to", {
|
|
params: {
|
|
attribute: useTrans(
|
|
"uwil-dynamic-pages::trans.attributes.config",
|
|
),
|
|
},
|
|
choice: 2,
|
|
}),
|
|
)
|
|
.ucFirst()
|
|
.value()}
|
|
required={false}
|
|
placeholder="ex: John Doe"
|
|
defaultValues={data.config_id ? [data.config_id] : []}
|
|
setData={(_, value) => {
|
|
const id = Array.isArray(value)
|
|
? (value[0] ?? defaultConfigId)
|
|
: (value ?? defaultConfigId);
|
|
setData("config_id", id);
|
|
}}
|
|
/>
|
|
{isConceptor && data.config_id !== defaultConfigId && (
|
|
<Dialog>
|
|
<DialogTrigger asChild>
|
|
<Button variant="secondary" type="button" size="icon">
|
|
<EyeIcon />
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent className="max-w-4xl w-full max-h-[100vh]">
|
|
<DialogHeader>
|
|
<DialogTitle>{modalTitle}</DialogTitle>
|
|
<DialogDescription>{modalDescription}</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="max-h-[25rem] overflow-y-auto">
|
|
<ConfigDetails config={getConfig(data.config_id)} />
|
|
</div>
|
|
<DialogFooter>
|
|
<BtnOptionnalLinkWithTooltip
|
|
link={route(
|
|
`${routeName}page-configs.show`,
|
|
data.config_id,
|
|
)}
|
|
content={<CogIcon />}
|
|
tooltipContent={anchorAccessConfig}
|
|
variant="secondary"
|
|
/>
|
|
<DialogClose asChild>
|
|
<Button variant="secondary">{modalClose}</Button>
|
|
</DialogClose>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{!data.config_id || data.config_id === defaultConfigId ? (
|
|
<SimpleWysiwyg
|
|
name="content"
|
|
tableName="dynamic_pages"
|
|
label={contentAssociateToModel}
|
|
value={typeof data.content === "string" ? data.content : ""}
|
|
setData={setData}
|
|
/>
|
|
) : (
|
|
<DynamicInputs
|
|
name="content"
|
|
config={getConfig(data.config_id)}
|
|
content={typeof data.content === "object" ? data.content : null}
|
|
setData={setData}
|
|
// @ts-expect-error Types diff between custom reset and useForm reset.
|
|
reset={reset}
|
|
/>
|
|
)}
|
|
</FieldsetWrapper>
|
|
{isConceptor && (
|
|
<FieldsetWrapper
|
|
className="gap-4"
|
|
title={transFormTitleIsLocked}
|
|
icon={<ShieldAlertIcon className="mr-1" />}
|
|
description={transFormSubtitleIsLocked}
|
|
inline
|
|
>
|
|
<SimpleSwitch
|
|
name="isLocked"
|
|
checked={data.isLocked}
|
|
setData={setData}
|
|
/>
|
|
</FieldsetWrapper>
|
|
)}
|
|
<div className="col-span-2 text-center">
|
|
<SubmitButton
|
|
modelTrans={useTrans(
|
|
`uwil-dynamic-pages::models.classes.${modelName}`,
|
|
{
|
|
choice: 1,
|
|
},
|
|
)}
|
|
/>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|