feat: ✨ add missing relation in crud between Document and DocumentCategory

This commit is contained in:
K Muller 2025-12-17 11:21:55 +01:00
parent 541c871bcb
commit becfa30b0a
10 changed files with 67 additions and 8 deletions

View file

@ -7,6 +7,7 @@ use App\Enums\YearsEnum;
use App\Http\Requests\Back\Document\StoreDocumentRequest;
use App\Http\Requests\Back\Document\UpdateDocumentRequest;
use App\Models\Document;
use App\Models\DocumentCategory;
use App\Models\DocumentTag;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@ -92,7 +93,7 @@ class DocumentController extends Controller
public function show(Document $document): \Inertia\Response
{
return Inertia::render('Document/Show', [
'data' => CommonResource::make($document->load('tags')),
'data' => CommonResource::make($document->load('tags', 'categories')),
'modelName' => $this->modelName,
'enumDocumentStatus' => DocumentStatusEnum::toArray(),
'enumYears' => YearsEnum::toArray()
@ -112,6 +113,7 @@ class DocumentController extends Controller
$document->inertiaFill($request->validated());
if ($document->saveOrFail()) {
$document->tags()->syncWithoutDetaching($request->input('tags', []));
$document->categories()->syncWithoutDetaching($request->input('categories', []));
return redirect()->route('back.' . $this->modelName . '.edit', $document);
}
return redirect()->route('back.' . $this->modelName . '.create');
@ -127,12 +129,13 @@ class DocumentController extends Controller
public function edit(Document $document): \Inertia\Response
{
$documentTags = DocumentTag::all();
$documentCategories = DocumentCategory::all();
return Inertia::render('Document/Edit', [
'data' => CommonResource::make($document->load('tags')),
'data' => CommonResource::make($document->load('tags', 'categories')),
'enumDocumentStatus' => DocumentStatusEnum::toArray(),
'enumYears' => YearsEnum::toArray(),
'tags' => $documentTags,
'categories' => $documentCategories,
'modelName' => $this->modelName
]);
}
@ -150,6 +153,7 @@ class DocumentController extends Controller
$document->inertiaFill($request->validated());
if ($document->saveOrFail()) {
$document->tags()->sync($request->input('tags', []));
$document->categories()->sync($request->input('categories', []));
return redirect()->route('back.' . $this->modelName . '.edit', $document);
}
return redirect()->route('back.' . $this->modelName . '.edit', $document);

View file

@ -48,6 +48,8 @@ class StoreDocumentRequest extends FormRequest
'path' => $this->pictureRules('required|file|mimes:pdf|max:5120'),
'tags' => 'nullable|array',
'tags.*' => 'integer|exists:document_tags,id',
'categories' => 'nullable|array',
'categories.*' => 'integer|exists:document_categories,id',
];
}
}

View file

@ -74,6 +74,9 @@ return [
"exercice" => [
"placeholder" => "choose an exercice"
],
"category" => [
"placeholder" => "choose a category"
]
],
"form_title_additional_files" => "Additionnal files",
"form_subtitle_additional_files" => "Add additionnal files",

View file

@ -271,7 +271,8 @@ return [
"document" => [
"exercice" => "exercice|exercices",
"tag" => "etiquette|etiquettes",
"path" => "file"
"path" => "file",
"category" => "category|categories"
],
"documentTag" => [
"documents" => "associated file(s)"

View file

@ -73,6 +73,9 @@ return [
"exercice" => [
"placeholder" => "choisir un exercice"
],
"category" => [
"placeholder" => "choisir une catégorie"
]
],
"form_title_additional_files" => "Fichier supplémentaire",
"form_subtitle_additional_files" => "Ajouter des fichiers supplémentaires",

View file

@ -269,7 +269,8 @@ return [
"document" => [
"exercice" => "exercice|exercices",
"tag" => "étiquette|étiquettes",
"path" => "fichier"
"path" => "fichier",
"category" => "catégorie|catégories"
],
"documentTag" => [
"documents" => "document(s) associé(s)"

View file

@ -1049,7 +1049,7 @@ export const MultiSelect = forwardRef<MultiSelectRef, MultiSelectProps>(
}
}}
aria-label={`Remove ${option.label} from selection`}
className="ml-2 h-4 w-4 cursor-pointer hover:bg-white/20 rounded-sm p-0.5 -m-0.5 focus:outline-none focus:ring-1 focus:ring-white/50">
className="ml-2 h-4 w-4 cursor-pointer hover:bg-white/20 rounded-sm -m-0.5 focus:outline-none focus:ring-1 focus:ring-white/50">
<XCircle
className={cn(
"h-3 w-3",

View file

@ -10,7 +10,7 @@ import { MultiSelect } from "@/back/Components/Inputs/MultiSelect";
import SimpleDatePicker from "@/back/Components/Inputs/SimpleDatePicker";
import CompleteInputFile from "@/back/Components/Inputs/CompleteInputFile";
import { Methods } from "@/lib/enums";
import { AppEnv, DynamicFieldTypes, DocumentModel, DocumentTagModel, LaravelEnum } from "@/back/types/global";
import { AppEnv, DynamicFieldTypes, DocumentModel, DocumentTagModel, LaravelEnum, DocumentCategoryModel } from "@/back/types/global";
import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import { mapPivotOptions } from "@/lib/mapPivotOptions";
@ -35,7 +35,9 @@ export default function Form({
const enumDocStatus = usePage().props.enumDocumentStatus as LaravelEnum;
const enumYears = usePage().props.enumYears as LaravelEnum;
const tags = usePage().props.tags as DocumentTagModel[];
const categories = usePage().props.categories as DocumentCategoryModel[];
const { options, defaultValue } = mapPivotOptions(tags, model?.tags, "name", locale);
const { options: categoriesOptions, defaultValue: categoryDefaultValue } = mapPivotOptions(categories, model?.categories, "name", locale);
/** Form section */
const { post, setData, data, reset } = useForm({
@ -45,6 +47,7 @@ export default function Form({
status: model?.status ?? enumDocStatus.unpublished.value,
path: model?.path ? model?.path[locale] : "",
tags: model?.tags ? defaultValue : [],
categories: model?.categories ? defaultValue : [],
published_at: model?.published_at ? model?.published_at : null,
unpublished_at: model?.unpublished_at ? model?.unpublished_at : null
});
@ -190,6 +193,30 @@ export default function Form({
defaultValue={defaultValue}
options={options}
/>
<MultiSelect
label={str(
useTrans("back.form_associate_to_plural", {
params: {
attribute: useTrans("validation.custom.document.category", { choice: 2 }),
},
choice: 2
}),
).ucFirst().value()}
multiple={true}
name="categories"
required={false}
variant="inverted"
placeholder={str(
useTrans("back.document.category.placeholder")
).ucFirst().value()}
singleLine={true}
setData={setData}
defaultValue={categoryDefaultValue}
options={categoriesOptions}
/>
</div>
<div className="col-span-1 md:col-span-2 mt-3">
<EnumSelect
name="exercice"
options={enumYears}
@ -204,7 +231,7 @@ export default function Form({
placeholder={str(
useTrans("back.document.exercice.placeholder")
).ucFirst().value()}
selected={String(data.exercice)}
selected={Number(data.exercice)}
setData={setData}
/>
</div>

View file

@ -12,6 +12,7 @@ import { useFormatedDate } from "@/hooks/use-formated-value";
import {
AppEnv,
AuthProp,
DocumentCategoryModel,
DocumentModel,
DocumentTagModel,
SingleResource,
@ -88,6 +89,22 @@ export default function Show({ data, modelName }: { data: SingleResource, modelN
);
}
},
{
accessorKey: "categories",
header: str(useTrans("validation.custom.document.category", {choice:2})).ucFirst().value(),
cell: ({ value }: { value?: DocumentCategoryModel[] }) => {
if (!value || value.length === 0) return null;
return (
<div className="flex flex-wrap gap-1">
{value.map((item, i) => (
<DocumentFinanceTagBadge value={item.name[appEnv.locale]} key={i} />
))}
</div>
);
}
},
{
accessorKey: "created_at",

View file

@ -184,6 +184,7 @@ type DocumentModel = {
unpublished_at: Date | null;
exercice: number | null;
tags: DocumentTagModel[];
categories: DocumentCategoryModel[];
created_at: Date;
updated_at: Date;
}