mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-04-03 16:51:26 +00:00
feat: Ajout des composants manquant dans le FormTemplateBuilder [N3WTS-17]
This commit is contained in:
@ -6,6 +6,11 @@ import * as LucideIcons from 'lucide-react';
|
||||
import Button from './Button';
|
||||
import DjangoCSRFToken from '../DjangoCSRFToken';
|
||||
import WisiwigTextArea from './WisiwigTextArea';
|
||||
import RadioList from './RadioList';
|
||||
import CheckBox from './CheckBox';
|
||||
import ToggleSwitch from './ToggleSwitch';
|
||||
import InputPhone from './InputPhone';
|
||||
import FileUpload from './FileUpload';
|
||||
|
||||
/*
|
||||
* Récupère une icône Lucide par son nom.
|
||||
@ -60,6 +65,9 @@ const formConfigTest = {
|
||||
export default function FormRenderer({
|
||||
formConfig = formConfigTest,
|
||||
csrfToken,
|
||||
onFormSubmit = (data) => {
|
||||
alert(JSON.stringify(data, null, 2));
|
||||
}, // Callback de soumission personnalisé (optionnel)
|
||||
}) {
|
||||
const {
|
||||
handleSubmit,
|
||||
@ -68,19 +76,103 @@ export default function FormRenderer({
|
||||
reset,
|
||||
} = useForm();
|
||||
|
||||
const onSubmit = (data) => {
|
||||
// Fonction utilitaire pour envoyer les données au backend
|
||||
const sendFormDataToBackend = async (formData) => {
|
||||
try {
|
||||
// Cette fonction peut être remplacée par votre propre implémentation
|
||||
// Exemple avec fetch:
|
||||
const response = await fetch('/api/submit-form', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
// Les en-têtes sont automatiquement définis pour FormData
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Erreur HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
logger.debug('Envoi réussi:', result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error("Erreur lors de l'envoi:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
logger.debug('=== DÉBUT onSubmit ===');
|
||||
logger.debug('Réponses :', data);
|
||||
|
||||
const formattedData = {
|
||||
//TODO: idDossierInscriptions: 123,
|
||||
formId: formConfig.id,
|
||||
responses: { ...data },
|
||||
};
|
||||
try {
|
||||
// Vérifier si nous avons des fichiers dans les données
|
||||
const hasFiles = Object.keys(data).some((key) => {
|
||||
return (
|
||||
data[key] instanceof FileList ||
|
||||
(data[key] && data[key][0] instanceof File)
|
||||
);
|
||||
});
|
||||
|
||||
if (hasFiles) {
|
||||
// Utiliser FormData pour l'envoi de fichiers
|
||||
const formData = new FormData();
|
||||
|
||||
// Ajouter l'ID du formulaire
|
||||
formData.append('formId', formConfig.id.toString());
|
||||
|
||||
// Traiter chaque champ et ses valeurs
|
||||
Object.keys(data).forEach((key) => {
|
||||
const value = data[key];
|
||||
|
||||
if (
|
||||
value instanceof FileList ||
|
||||
(value && value[0] instanceof File)
|
||||
) {
|
||||
// Gérer les champs de type fichier
|
||||
if (value.length > 0) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
formData.append(`files.${key}`, value[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Gérer les autres types de champs
|
||||
formData.append(
|
||||
`data.${key}`,
|
||||
value !== undefined ? value.toString() : ''
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (onFormSubmit) {
|
||||
// Utiliser le callback personnalisé si fourni
|
||||
await onFormSubmit(formData, true);
|
||||
} else {
|
||||
// Sinon, utiliser la fonction par défaut
|
||||
await sendFormDataToBackend(formData);
|
||||
alert('Formulaire avec fichier(s) envoyé avec succès');
|
||||
}
|
||||
} else {
|
||||
// Pas de fichier, on peut utiliser JSON
|
||||
const formattedData = {
|
||||
formId: formConfig.id,
|
||||
responses: { ...data },
|
||||
};
|
||||
|
||||
if (onFormSubmit) {
|
||||
// Utiliser le callback personnalisé si fourni
|
||||
await onFormSubmit(formattedData, false);
|
||||
} else {
|
||||
// Afficher un message pour démonstration
|
||||
alert('Données reçues : ' + JSON.stringify(formattedData, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
reset(); // Réinitialiser le formulaire après soumission
|
||||
} catch (error) {
|
||||
logger.error('Erreur lors de la soumission du formulaire:', error);
|
||||
alert(`Erreur lors de l'envoi du formulaire: ${error.message}`);
|
||||
}
|
||||
|
||||
//TODO: ENVOYER LES DONNÉES AU BACKEND
|
||||
alert('Données reçues : ' + JSON.stringify(formattedData, null, 2));
|
||||
reset(); // Réinitialiser le formulaire après soumission
|
||||
logger.debug('=== FIN onSubmit ===');
|
||||
};
|
||||
|
||||
@ -132,7 +224,14 @@ export default function FormRenderer({
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
rules={{ required: field.required }}
|
||||
rules={{
|
||||
required: field.required,
|
||||
pattern: field.validation?.pattern
|
||||
? new RegExp(field.validation.pattern)
|
||||
: undefined,
|
||||
minLength: field.validation?.minLength,
|
||||
maxLength: field.validation?.maxLength,
|
||||
}}
|
||||
render={({ field: { onChange, value, name } }) => (
|
||||
<InputTextIcon
|
||||
label={field.label}
|
||||
@ -153,6 +252,29 @@ export default function FormRenderer({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'phone' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
rules={{ required: field.required }}
|
||||
render={({ field: { onChange, value, name } }) => (
|
||||
<InputPhone
|
||||
label={field.label}
|
||||
required={field.required}
|
||||
name={name}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
errorMsg={
|
||||
errors[field.id]
|
||||
? field.required
|
||||
? `${field.label} est requis`
|
||||
: 'Champ invalide'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'select' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
@ -178,6 +300,111 @@ export default function FormRenderer({
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'radio' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
rules={{ required: field.required }}
|
||||
render={({ field: { onChange, value, name } }) => (
|
||||
<RadioList
|
||||
items={field.options.map((option, idx) => ({
|
||||
id: idx,
|
||||
label: option,
|
||||
}))}
|
||||
formData={{
|
||||
[field.id]: value
|
||||
? field.options.findIndex((o) => o === value)
|
||||
: '',
|
||||
}}
|
||||
handleChange={(e) =>
|
||||
onChange(field.options[parseInt(e.target.value)])
|
||||
}
|
||||
fieldName={field.id}
|
||||
sectionLabel={field.label}
|
||||
required={field.required}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'checkbox' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
defaultValue={field.checked || false}
|
||||
rules={{ required: field.required }}
|
||||
render={({ field: { onChange, value, name } }) => (
|
||||
<div>
|
||||
<CheckBox
|
||||
item={{ id: field.id, label: field.label }}
|
||||
formData={{ [field.id]: value || false }}
|
||||
handleChange={(e) => onChange(e.target.checked)}
|
||||
fieldName={field.id}
|
||||
itemLabelFunc={(item) => item.label}
|
||||
horizontal={field.horizontal || false}
|
||||
/>
|
||||
{errors[field.id] && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{field.required
|
||||
? `${field.label} est requis`
|
||||
: 'Champ invalide'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'toggle' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
defaultValue={field.checked || false}
|
||||
rules={{ required: field.required }}
|
||||
render={({ field: { onChange, value, name } }) => (
|
||||
<div>
|
||||
<ToggleSwitch
|
||||
name={field.id}
|
||||
label={field.label + (field.required ? ' *' : '')}
|
||||
checked={value || false}
|
||||
onChange={(e) => onChange(e.target.checked)}
|
||||
/>
|
||||
{errors[field.id] && (
|
||||
<p className="text-red-500 text-sm mt-1">
|
||||
{field.required
|
||||
? `${field.label} est requis`
|
||||
: 'Champ invalide'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'file' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
control={control}
|
||||
rules={{ required: field.required }}
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FileUpload
|
||||
selectionMessage={field.label}
|
||||
required={field.required}
|
||||
uploadedFileName={value ? value[0]?.name : null}
|
||||
onFileSelect={(file) => {
|
||||
// Créer un objet de type FileList similaire pour la compatibilité
|
||||
const dataTransfer = new DataTransfer();
|
||||
dataTransfer.items.add(file);
|
||||
onChange(dataTransfer.files);
|
||||
}}
|
||||
errorMsg={
|
||||
errors[field.id]
|
||||
? field.required
|
||||
? `${field.label} est requis`
|
||||
: 'Champ invalide'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{field.type === 'textarea' && (
|
||||
<Controller
|
||||
name={field.id}
|
||||
|
||||
Reference in New Issue
Block a user