254 lines
10 KiB
TypeScript
254 lines
10 KiB
TypeScript
// * Packages
|
|
import { usePage } from "@inertiajs/react";
|
|
// * Other
|
|
import SimpleString from "@/back/Components/Inputs/SimpleString";
|
|
import SimpleNumber from "@/back/Components/Inputs/SimpleNumber";
|
|
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
|
|
import SimpleWysiwyg from "@/back/Components/Inputs/SimpleWysiwyg";
|
|
import { DynamicFieldTypes, DynamicField } from "@/back/types/global";
|
|
import str from "@/hooks/use-string";
|
|
import useTrans from "@/hooks/use-translations";
|
|
import ImageCropper from "./Inputs/ImageCropper";
|
|
|
|
type DynamicInputsProps = {
|
|
name: string;
|
|
config: DynamicField[];
|
|
content: Record<string, string | boolean | number> | null;
|
|
setData: (name: string, value: string | boolean | File | null) => void;
|
|
reset: (name?: string) => void;
|
|
};
|
|
|
|
type MinMax = {
|
|
min?: number;
|
|
max?: number;
|
|
};
|
|
|
|
export default function DynamicInputs({
|
|
name,
|
|
config,
|
|
content = {},
|
|
setData,
|
|
reset,
|
|
}: DynamicInputsProps) {
|
|
const enumDynamicFieldTypes = usePage().props.enumDynamicFieldTypes as DynamicFieldTypes;
|
|
const transAssociateTo = useTrans("back.form_associate_to");
|
|
const transUnsupportedType = useTrans("back.unsupported_type");
|
|
|
|
/**
|
|
* Get a translation for a specific field label.
|
|
* Need to recreate useTrans hook to avoid hook error.
|
|
* @param fieldLabel string
|
|
* @returns string
|
|
*/
|
|
const getTrans = (fieldLabel: string): string => {
|
|
return str(`${transAssociateTo.split("|")[0].replace(":attribute", fieldLabel)}`).ucFirst().value();
|
|
};
|
|
|
|
/**
|
|
* Get limits from laravel validation.
|
|
* @param string validation
|
|
* @return MinMax
|
|
*/
|
|
const extractMinMaxFromValidation = (validation: string): MinMax => {
|
|
const result: MinMax = {};
|
|
|
|
// Regex for between match.
|
|
const betweenMatch = validation.match(/between:(\d+),(\d+)/);
|
|
if (betweenMatch) {
|
|
result.min = Number(betweenMatch[1]);
|
|
result.max = Number(betweenMatch[2]);
|
|
return result;
|
|
}
|
|
|
|
// Regex for minimum match.
|
|
const minMatch = validation.match(/min:(\d+)/);
|
|
if (minMatch) {
|
|
result.min = Number(minMatch[1]);
|
|
}
|
|
|
|
// Regex for maximum match.
|
|
const maxMatch = validation.match(/max:(\d+)/);
|
|
if (maxMatch) {
|
|
result.max = Number(maxMatch[1]);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get required from laravel validation.
|
|
* @param string validation
|
|
* @return boolean
|
|
*/
|
|
const extractRequiredValidation = (validation: string): boolean => {
|
|
return validation.split("|").includes("required");
|
|
};
|
|
|
|
/**
|
|
* Extracts a number value from a Laravel validation rule.
|
|
*
|
|
* Supports both:
|
|
* - Simple rules like "max:255"
|
|
* - Complex rules like "dimensions:width=350,height=250"
|
|
*
|
|
* @param validation Full validation string (e.g. "required|max:255|dimensions:width=350,height=250")
|
|
* @param ruleName Rule to look for (e.g. "max" or "dimensions")
|
|
* @param key Optional. If the rule contains key=value pairs (e.g. "width=350"), specify the key
|
|
* @returns number|null
|
|
*/
|
|
const extractLaravelValidationValue = (
|
|
validation: string,
|
|
ruleName: string,
|
|
key?: string
|
|
): string | null => {
|
|
const rules = validation.split("|");
|
|
const rule = rules.find(r => r.startsWith(`${ruleName}:`));
|
|
|
|
if (!rule) return null;
|
|
|
|
const [, rawValue] = rule.split(":");
|
|
|
|
if (key) {
|
|
// Complex case like dimensions:width=350,height=250
|
|
const pairs = rawValue.split(",");
|
|
const match = pairs.find(pair => pair.startsWith(`${key}=`));
|
|
if (match) {
|
|
const [, value] = match.split("=");
|
|
return value;
|
|
}
|
|
} else {
|
|
return rawValue;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 items-start gap-6">
|
|
{Object.entries(config).map(([id, field]) => {
|
|
const typeEnum = Object.values(enumDynamicFieldTypes).find(
|
|
(def) => def.value === field.type,
|
|
);
|
|
|
|
const fieldName = `${name}.${field.key}`;
|
|
const fieldValue = content?.[field.key] ?? "";
|
|
|
|
switch (field.type) {
|
|
// String input.
|
|
case enumDynamicFieldTypes.string.value:
|
|
return (
|
|
<SimpleString
|
|
key={id}
|
|
name={fieldName}
|
|
label={getTrans(field.label)}
|
|
required={extractRequiredValidation(field.validation)}
|
|
value={String(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
);
|
|
// Wysiwyg input.
|
|
case enumDynamicFieldTypes.text.value:
|
|
return (
|
|
<div key={id} className="col-span-1 md:col-span-2">
|
|
<SimpleWysiwyg
|
|
name={fieldName}
|
|
model="DynamicPage"
|
|
label={getTrans(field.label)}
|
|
value={String(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
</div>
|
|
);
|
|
// Numbers input.
|
|
case enumDynamicFieldTypes.number.value:
|
|
return (
|
|
<SimpleNumber
|
|
key={id}
|
|
name={fieldName}
|
|
label={getTrans(field.label)}
|
|
limits={extractMinMaxFromValidation(field.validation)}
|
|
required={extractRequiredValidation(field.validation)}
|
|
value={Number(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
);
|
|
// Switch input.
|
|
case enumDynamicFieldTypes.boolean.value:
|
|
return (
|
|
<div key={id} className="grid grid-cols-1 gap-1.5">
|
|
<label
|
|
htmlFor={`#${fieldName}`}
|
|
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
>
|
|
{`${getTrans(field.label)} ${extractRequiredValidation(field.validation) ? " *" : ""}`}
|
|
</label>
|
|
<SimpleSwitch
|
|
name={fieldName}
|
|
checked={Boolean(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
</div>
|
|
);
|
|
// Url input.
|
|
case enumDynamicFieldTypes.url.value:
|
|
return (
|
|
<SimpleString
|
|
key={id}
|
|
name={fieldName}
|
|
label={getTrans(field.label)}
|
|
type="url"
|
|
placeholder="ex: https://..."
|
|
required={extractRequiredValidation(field.validation)}
|
|
value={String(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
);
|
|
case enumDynamicFieldTypes.image.value:
|
|
return (
|
|
<ImageCropper
|
|
key={id}
|
|
fileName="fichier"
|
|
name={fieldName}
|
|
label={getTrans(field.label)}
|
|
setData={setData}
|
|
reset={reset}
|
|
value={String(fieldValue)}
|
|
required={extractRequiredValidation(field.validation)}
|
|
maxFileSize={extractLaravelValidationValue(field.validation, 'max')}
|
|
allowedFormats={extractLaravelValidationValue(field.validation, 'mimes')}
|
|
validationRules={{
|
|
width: extractLaravelValidationValue(field.validation, "dimensions", 'width'),
|
|
height: extractLaravelValidationValue(field.validation, "dimensions", 'height')
|
|
}}
|
|
/>
|
|
);
|
|
// Email input.
|
|
case enumDynamicFieldTypes.email.value:
|
|
return (
|
|
<SimpleString
|
|
key={id}
|
|
name={fieldName}
|
|
label={getTrans(field.label)}
|
|
type="email"
|
|
placeholder="ex: john.doe@ldc.fr"
|
|
required={extractRequiredValidation(field.validation)}
|
|
value={String(fieldValue)}
|
|
setData={setData}
|
|
/>
|
|
);
|
|
default:
|
|
return (
|
|
<p
|
|
key={id}
|
|
className="text-muted-foreground text-sm"
|
|
>
|
|
{transUnsupportedType} {typeEnum?.label ?? field.type}
|
|
</p>
|
|
);
|
|
}
|
|
})}
|
|
</div>
|
|
);
|
|
}
|