332 lines
14 KiB
TypeScript
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>
|
|
);
|
|
}
|