ldc-groupe/resources/ts/back/Pages/DynamicPages/Form.tsx
2025-10-17 14:52:51 +02:00

332 lines
14 KiB
TypeScript

// * Packages
import { useForm, usePage } from "@inertiajs/react";
import { BookText, Cog, ExternalLinkIcon, Eye, ShieldAlert } from "lucide-react";
import { route } from "ziggy-js";
import { FormEvent, useEffect } from "react";
// * Components shadcn
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import {
Dialog,
DialogHeader,
DialogTitle,
DialogDescription,
DialogClose,
DialogContent,
DialogFooter,
DialogTrigger,
} from "@/components/ui/dialog";
// * Other
import SimpleString from "@/back/Components/Inputs/SimpleString";
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
import SimpleWysiwyg from "@/back/Components/Inputs/SimpleWysiwyg";
import EnumSelect from "@/back/Components/Inputs/EnumSelect";
import ModelSelect from "@/back/Components/Inputs/ModelSelect";
import DynamicInputs from "@/back/Components/DynamicInputs";
import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
import ConfigTable from "@/back/Components/PageConfigs/ConfigTable";
import useTrans from "@/hooks/use-translations";
import InputError from "@/back/Components/InputError";
import str from "@/hooks/use-string";
import { useToast } from "@/hooks/use-toast";
import { Methods } from "@/lib/enums";
import { AppEnv, AuthProp, DynamicField, DynamicPageModel, LaravelEnum, PageConfigModel } from "@/back/types/global";
export default function Form({
model,
modelName,
modelRoute,
action,
method,
}: {
model?: DynamicPageModel;
modelName: string;
modelRoute: string;
action: string;
method: number;
}) {
/** Set messages success/errors */
const messages = {} as Record<string, string>;
messages.titleSuccess = useTrans(
method === Methods.POST ? "Création réussie !" : "Mise à jour réussie !",
);
messages.descriptionSuccess = str(
useTrans(
method === Methods.POST ? "crud.messages.has_been_created" : "crud.messages.has_been_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
messages.titleError = useTrans(
method === Methods.POST ? "Création annulée !" : "Mise à jour annulée !",
);
messages.descriptionError = str(
useTrans(
method === Methods.POST ? "crud.messages.cannot_be_created" : "crud.messages.cannot_be_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
/** Global */
const { locale } = usePage().props.appEnv as AppEnv;
const userModel = (usePage().props.auth as AuthProp).user;
const enumUserRole = usePage().props.enumUserRole as LaravelEnum;
const enumDynamicPageStatus = usePage().props.enumDynamicPageStatus as LaravelEnum;
const pageConfigModels = usePage().props.pageConfigModels as Array<PageConfigModel>;
const configOptions = [{
'id': 0,
'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, ...pageConfigModels];
configOptions[0].label[locale] = useTrans('back.config_none_selected');
const contentAssociateToModel = str(
useTrans("back.form_associate_to", {
params: { attribute: useTrans("validation.attributes.content") },
choice: 1,
}),
).ucFirst().value()
const modalTitle = useTrans('back.config_modal_title');
const modalDescription = useTrans('back.config_modal_descripion');
const modalClose = useTrans("back.close");
const anchorAccessPage = useTrans("back.access_model", {
params: {
model: useTrans('models.classes.dynamic-page', { choice: 1 })
}
});
const getConfig = (id: number) => configOptions.find(objet => objet.id == id)?.config[locale] as DynamicField[];
const { toast } = useToast();
/** Form section */
const { post, patch, setData, data } = useForm({
title: model?.title ? model?.title[locale]: "",
content: model?.content ? model?.content[locale]: "",
config_id: model?.config_id ?? configOptions[0].id,
status: model?.status ?? enumDynamicPageStatus.draft.value,
isLocked: model?.isLocked ?? false,
});
const routeOptions = () => {
return {
preserveScroll: true,
onSuccess: () => {
toast({
title: messages.titleSuccess,
description: messages.descriptionSuccess,
});
},
onError: () => {
toast({
variant: "destructive",
title: messages.titleError,
description: messages.descriptionError,
});
},
};
};
const submit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const routeUrl = route(`back.${modelRoute}.${action}`, model?.id);
switch (method) {
case Methods.POST:
post(routeUrl, routeOptions());
break;
case Methods.PATCH:
patch(routeUrl, routeOptions());
break;
default:
throw new Error("The method must be post or patch");
}
};
useEffect(() => {
setData("title", model?.title ? model?.title[locale]: "");
setData("content", model?.content ? model?.content[locale]: "");
setData("config_id", model?.config_id ?? configOptions[0].id);
setData("status", model?.status ?? enumDynamicPageStatus.draft.value);
setData("isLocked", model?.isLocked ?? false);
}, [locale]);
return (
<form onSubmit={submit}>
<InputError all />
<FieldsetWrapper
title={useTrans("back.form_title_global")}
icon={<BookText className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
className="sm:grid-cols-2 gap-4"
>
<SimpleString
name="title"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans(
"validation.attributes.title",
),
},
choice: 1,
}),
).ucFirst().value()}
value={data.title}
setData={setData}
/>
<div className="flex flex-row justify-center items-start">
<EnumSelect
name="status"
options={enumDynamicPageStatus}
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.status"),
},
choice: 1,
}),
).ucFirst().value()}
placeholder={"ex: " + enumDynamicPageStatus.draft.label}
selected={String(data.status)}
setData={setData}
/>
{model && model.status === enumDynamicPageStatus.published.value && (
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
type="button"
className="ms-1 mt-5"
asChild
>
<a href={`/${model.slug}`} target="_blank">
<ExternalLinkIcon />
</a>
</Button>
</TooltipTrigger>
<TooltipContent>
{anchorAccessPage}
</TooltipContent>
</Tooltip>
)}
</div>
</FieldsetWrapper>
<FieldsetWrapper
title={useTrans("back.form_title_page")}
icon={<Cog className="mr-1" />}
description={useTrans("back.form_subtitle_page")}
className="gap-4"
>
<div className="grid sm:grid-cols-2 gap-4 border-b border-input pb-4">
<div className="flex justify-start items-end">
<ModelSelect
name="config_id"
options={configOptions}
field="label"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans(
"validation.custom.config",
),
},
choice: 2,
}),
).ucFirst().value()}
placeholder={"ex: "}
selected={String(data.config_id)}
required={false}
setData={setData}
/>
{userModel.role === enumUserRole.conceptor.value && data.config_id != 0 && (
<Dialog>
<DialogTrigger asChild>
<Button
variant="secondary"
type="button"
className="ms-1"
>
<Eye />
</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">
<ConfigTable config={getConfig(data.config_id)} />
</div>
<DialogFooter>
<DialogClose asChild>
<Button variant="secondary">
{modalClose}
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
)}
</div>
</div>
{data.config_id == 0 ? (
<SimpleWysiwyg
name="content"
model="DynamicPage"
label={contentAssociateToModel}
value={String(data.content)}
setData={setData}
/>
) : (
<DynamicInputs
name="content"
config={getConfig(data.config_id)}
content={typeof data.content === "object" ? data.content : {}}
setData={setData}
/>
)}
</FieldsetWrapper>
{userModel.role === enumUserRole.conceptor.value && (
<FieldsetWrapper
className="gap-4"
title={useTrans("back.form_title_isLocked")}
icon={<ShieldAlert className="mr-1" />}
description={useTrans("back.form_subtitle_isLocked")}
inline
>
<SimpleSwitch
name="isLocked"
checked={data.isLocked}
setData={setData}
/>
</FieldsetWrapper>
)}
<div className="text-center">
<Tooltip>
<TooltipTrigger asChild>
<Button type="submit">
{str(useTrans("crud.actions.save")).ucFirst().value()}
</Button>
</TooltipTrigger>
<TooltipContent>
{useTrans("crud.helpers.save_model", {
params: {
model: useTrans("models.classes.dynamic-page", {
choice: 1,
}),
},
})}
</TooltipContent>
</Tooltip>
</div>
</form>
);
}