diff --git a/lang/en/back.php b/lang/en/back.php
index 9a421473..254f206b 100644
--- a/lang/en/back.php
+++ b/lang/en/back.php
@@ -100,6 +100,7 @@ return [
"new_field" => "Add a new field",
"delete_field" => "Delete this field",
"dnd_field" => "Move this field",
+ "list_children_needed" => "Children are required for this type :enumLabel",
"empty_warning" => "No data recorded",
"page_locked" => "This page is currently locked",
"unsupported_type" => "Unsupported type:",
diff --git a/lang/fr/back.php b/lang/fr/back.php
index f2f0a06a..4593de19 100644
--- a/lang/fr/back.php
+++ b/lang/fr/back.php
@@ -100,6 +100,7 @@ return [
"new_field" => "Ajouter un nouveau champ",
"delete_field" => "Supprimer ce champ",
"dnd_field" => "Déplacer ce champ",
+ "list_children_needed" => "Des enfants sont nécessaires pour ce type :enumLabel",
"empty_warning" => "Aucune donnée enregistrée",
"page_locked" => "Cette page est actuellement vérouillée",
"unsupported_type" => "Type non supporté:",
diff --git a/resources/ts/back/Components/DynamicPages/DynamicInputs.tsx b/resources/ts/back/Components/DynamicPages/DynamicInputs.tsx
index f5946c74..2b74c28f 100644
--- a/resources/ts/back/Components/DynamicPages/DynamicInputs.tsx
+++ b/resources/ts/back/Components/DynamicPages/DynamicInputs.tsx
@@ -24,6 +24,7 @@ type DynamicInputsProps = {
reset: (name?: string) => void;
};
+// Allows to get specific input from enum value.
export default function DynamicInputs({
name,
config,
@@ -31,6 +32,7 @@ export default function DynamicInputs({
setData,
reset,
}: DynamicInputsProps) {
+ // Enums.
const enumDynamicFieldTypes = usePage().props.enumDynamicFieldTypes as DynamicFieldTypes;
// Translations.
@@ -49,12 +51,17 @@ export default function DynamicInputs({
return (
+ {/* Loop on all config elements. */}
{Object.entries(config).map(([id, field]) => {
+ // Get the enum value from the element type value.
const typeEnum = Object.values(enumDynamicFieldTypes).find(
(def) => def.value === field.type,
);
+ // Set the name of the current field.
const fieldName = `${name}.${field.key}`;
+
+ // Set the content value from the config key.
const fieldValue = content?.[field.key as keyof typeof content] ?? "";
switch (field.type) {
@@ -137,7 +144,7 @@ export default function DynamicInputs({
label={getTrans(field.label)}
setData={setData}
reset={reset}
- value={String(fieldValue)}
+ value={fieldValue as string | File | null}
required={extractRequiredValidation(field.validation)}
maxFileSize={extractLaravelValidationValue(field.validation, 'max')}
allowedFormats={extractLaravelValidationValue(field.validation, 'mimes')}
diff --git a/resources/ts/back/Components/DynamicPages/DynamicList.tsx b/resources/ts/back/Components/DynamicPages/DynamicList.tsx
index 32ba7902..f6d795de 100644
--- a/resources/ts/back/Components/DynamicPages/DynamicList.tsx
+++ b/resources/ts/back/Components/DynamicPages/DynamicList.tsx
@@ -1,7 +1,7 @@
// * Packages
import { CSS } from "@dnd-kit/utilities";
import { GripVerticalIcon, TrashIcon } from "lucide-react";
-import { closestCenter, DndContext, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
+import { closestCenter, DndContext, DragEndEvent, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
// * Components shadcn
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion";
@@ -26,6 +26,7 @@ type DynamicListProps = {
reset: (name?: string) => void;
}
+// Allows to get recursive list inputs.
export default function DynamicList({
name,
parentName,
@@ -40,34 +41,44 @@ export default function DynamicList({
if (!field.children && field.children!.length > 0) {
return (
- Des enfants sont nécessaires pour ce type {enumLabel}
+ {useTrans('back.list_children_needed', {
+ params: { enumLabel }
+ })}
);
}
- // Drag&Drop.
+ // Drag & drop module for user interactions (mouse, touch, keyboard or other).
const sensors = useSensors(useSensor(PointerSensor));
- // Set local id.
+ // Define a local identifier for each element to enable unique identification during drag and drop.
const localValue = value.map((elmt, index) => {
return { ...elmt, id: elmt.id ?? index + 1 };
});
+ // Order all elements depending on order values.
const sortedListItems = [...localValue].sort((a, b) => a.order - b.order);
+
+ // Extract some values from laravel rules validation.
const isRequired = extractRequiredValidation(field.validation);
const configLimitMax = extractMinMaxFromValidation(field.validation).max ?? 1;
+ /**
+ * Get all elements without each unique identification values.
+ * @param fields DynamicInternalField[]
+ * @returns Omit[]
+ */
const removeIds = (fields: DynamicInternalField[]): Omit[] => {
return fields.map(({ id, ...rest }) => {
const newObj: any = {};
for (const key in rest) {
const value = rest[key as keyof typeof rest];
- // Si c’est un tableau, on le traite récursivement
+ // If it is an array, we treat it recursively.
if (Array.isArray(value)) {
newObj[key] = removeIds(value);
}
- // Sinon, on garde la valeur telle quelle
+ // Otherwise, we keep the value as it is.
else {
newObj[key] = value;
}
@@ -76,6 +87,69 @@ export default function DynamicList({
});
};
+ /**
+ * Remove a specific element from the list.
+ * @param parentName string
+ * @param fieldKey string
+ * @param index number
+ * @return void
+ */
+ const handleRemoveField = (parentName: string, fieldKey: string, index: number) => {
+ // Get the current list of elements.
+ const list = Array.isArray(content?.[fieldKey as keyof typeof content])
+ // @ts-ignore
+ ? [...content[fieldKey as keyof typeof content]]
+ : [];
+ // Remove the targeted element.
+ list.splice(index, 1);
+ // Save the new list updated.
+ setData(`${parentName}.${fieldKey}`, list);
+ }
+
+ /**
+ * Reorder the elements list.
+ * @param event DragEndEvent
+ * @return void
+ */
+ const handleOnDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+
+ // Check the unique identification value.
+ if (!over || active.id === over.id) return;
+
+ // Get the two elements that are switchable by drag and drop.
+ const oldIndex = sortedListItems.findIndex((i) => i.id === active.id);
+ const newIndex = sortedListItems.findIndex((i) => i.id === over.id);
+
+ // Reorder all elements list to update visually all elements.
+ const reorderedList = arrayMove(sortedListItems, oldIndex, newIndex)
+ .map((item, index) => ({
+ ...item,
+ order: index + 1,
+ }));
+
+ // Save the new ordered list elements.
+ setData(`${parentName}.${field.key}`, reorderedList);
+ }
+
+ /**
+ * Add a new field to the list.
+ * @return void
+ */
+ const handleAddNewField = () => {
+ // Set new element with unique identification and order.
+ const newItem = Object.fromEntries(
+ (field.children ?? []).map(child => [child.key, ""])
+ );
+ const maxOrder = Math.max(0, ...sortedListItems.map((field) => field.order ?? 0)) + 1;
+ const maxIndex = Math.max(0, ...sortedListItems.map((field) => field.id ?? 0)) + 1;
+ const newIndex = value.length;
+ const newItemWithOrder = { ...newItem, id: maxIndex, order: maxOrder };
+
+ // Save the list with the new element.
+ setData(`${parentName}.${field.key}.${newIndex}`, newItemWithOrder);
+ }
+
return (