mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
feat: Ajout de la configuration des tarifs de l'école [#18]
This commit is contained in:
committed by
Luc SORIGNET
parent
147a70135d
commit
5a0e65bb75
@ -5,8 +5,12 @@ import ResponsableInputFields from '@/components/Inscription/ResponsableInputFie
|
||||
import Loader from '@/components/Loader';
|
||||
import Button from '@/components/Button';
|
||||
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
|
||||
import FileUpload from '@/app/[locale]/admin/subscriptions/components/FileUpload';
|
||||
import Table from '@/components/Table';
|
||||
import { fetchRegisterFormFileTemplate, createRegistrationFormFile } from '@/app/lib/subscriptionAction';
|
||||
import { Download, Upload } from 'lucide-react';
|
||||
import { BASE_URL } from '@/utils/Url';
|
||||
import DraggableFileUpload from '@/app/[locale]/admin/subscriptions/components/DraggableFileUpload';
|
||||
import Modal from '@/components/Modal';
|
||||
|
||||
const levels = [
|
||||
{ value:'1', label: 'TPS - Très Petite Section'},
|
||||
@ -20,7 +24,8 @@ export default function InscriptionFormShared({
|
||||
csrfToken,
|
||||
onSubmit,
|
||||
cancelUrl,
|
||||
isLoading = false
|
||||
isLoading = false,
|
||||
errors = {} // Nouvelle prop pour les erreurs
|
||||
}) {
|
||||
|
||||
const [formData, setFormData] = useState(() => ({
|
||||
@ -41,6 +46,11 @@ export default function InscriptionFormShared({
|
||||
);
|
||||
|
||||
const [uploadedFiles, setUploadedFiles] = useState([]);
|
||||
const [fileTemplates, setFileTemplates] = useState([]);
|
||||
const [fileName, setFileName] = useState("");
|
||||
const [file, setFile] = useState("");
|
||||
const [showUploadModal, setShowUploadModal] = useState(false);
|
||||
const [currentTemplateId, setCurrentTemplateId] = useState(null);
|
||||
|
||||
// Mettre à jour les données quand initialData change
|
||||
useEffect(() => {
|
||||
@ -58,6 +68,9 @@ export default function InscriptionFormShared({
|
||||
level: initialData.level || ''
|
||||
});
|
||||
setGuardians(initialData.guardians || []);
|
||||
fetchRegisterFormFileTemplate().then((data) => {
|
||||
setFileTemplates(data);
|
||||
});
|
||||
}
|
||||
}, [initialData]);
|
||||
|
||||
@ -65,8 +78,22 @@ export default function InscriptionFormShared({
|
||||
setFormData(prev => ({...prev, [field]: value}));
|
||||
};
|
||||
|
||||
const handleFileUpload = (file, fileName) => {
|
||||
setUploadedFiles([...uploadedFiles, { file, fileName }]);
|
||||
const handleFileUpload = async (file, fileName) => {
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
data.append('name',fileName);
|
||||
data.append('template', currentTemplateId);
|
||||
data.append('register_form', formData.id);
|
||||
|
||||
try {
|
||||
await createRegistrationFormFile(data, csrfToken);
|
||||
// Optionnellement, rafraîchir la liste des fichiers
|
||||
fetchRegisterFormFileTemplate().then((data) => {
|
||||
setFileTemplates(data);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error uploading file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
@ -80,12 +107,31 @@ export default function InscriptionFormShared({
|
||||
onSubmit(data);
|
||||
};
|
||||
|
||||
const getError = (field) => {
|
||||
return errors?.student?.[field]?.[0];
|
||||
};
|
||||
|
||||
const getGuardianError = (index, field) => {
|
||||
return errors?.student?.guardians?.[index]?.[field]?.[0];
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ name: 'Nom du fichier', transform: (row) => row.last_name },
|
||||
{ name: 'Nom du fichier', transform: (row) => row.name },
|
||||
{ name: 'Fichier à Remplir', transform: (row) => row.is_required ? 'Oui' : 'Non' },
|
||||
{ name: 'Fichier de référence', transform: (row) => row.file && <div className="flex items-center justify-center gap-2"> <a href={`${BASE_URL}${row.file}`} target='_blank' className="text-blue-500 hover:text-blue-700">
|
||||
<Download size={16} />
|
||||
</a> </div>},
|
||||
{ name: 'Actions', transform: (row) => (
|
||||
<a href={URL.createObjectURL(row.fichier)} target='_blank' className="text-blue-500 hover:text-blue-700">
|
||||
Télécharger
|
||||
</a>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
{row.is_required &&
|
||||
<button className="text-emerald-500 hover:text-emerald-700" type="button" onClick={() => {
|
||||
setCurrentTemplateId(row.id);
|
||||
setShowUploadModal(true);
|
||||
}}>
|
||||
<Upload size={16} />
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
) },
|
||||
];
|
||||
|
||||
@ -105,12 +151,14 @@ export default function InscriptionFormShared({
|
||||
value={formData.last_name}
|
||||
onChange={(e) => updateFormField('last_name', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('last_name')}
|
||||
/>
|
||||
<InputText
|
||||
name="first_name"
|
||||
label="Prénom"
|
||||
value={formData.first_name}
|
||||
onChange={(e) => updateFormField('first_name', e.target.value)}
|
||||
errorMsg={getError('first_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
@ -126,18 +174,22 @@ export default function InscriptionFormShared({
|
||||
value={formData.birth_date}
|
||||
onChange={(e) => updateFormField('birth_date', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_place"
|
||||
label="Lieu de Naissance"
|
||||
value={formData.birth_place}
|
||||
onChange={(e) => updateFormField('birth_place', e.target.value)}
|
||||
errorMsg={getError('birth_place')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_postal_code"
|
||||
label="Code Postal de Naissance"
|
||||
value={formData.birth_postal_code}
|
||||
onChange={(e) => updateFormField('birth_postal_code', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_postal_code')}
|
||||
/>
|
||||
<div className="md:col-span-2">
|
||||
<InputText
|
||||
@ -145,6 +197,7 @@ export default function InscriptionFormShared({
|
||||
label="Adresse"
|
||||
value={formData.address}
|
||||
onChange={(e) => updateFormField('address', e.target.value)}
|
||||
errorMsg={getError('address')}
|
||||
/>
|
||||
</div>
|
||||
<InputText
|
||||
@ -152,14 +205,17 @@ export default function InscriptionFormShared({
|
||||
label="Médecin Traitant"
|
||||
value={formData.attending_physician}
|
||||
onChange={(e) => updateFormField('attending_physician', e.target.value)}
|
||||
errorMsg={getError('attending_physician')}
|
||||
/>
|
||||
<SelectChoice
|
||||
name="level"
|
||||
label="Niveau"
|
||||
placeHolder="Sélectionner un niveau"
|
||||
selected={formData.level}
|
||||
callback={(e) => updateFormField('level', e.target.value)}
|
||||
choices={levels}
|
||||
required
|
||||
errorMsg={getError('level')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -184,6 +240,7 @@ export default function InscriptionFormShared({
|
||||
newArray.splice(index, 1);
|
||||
setGuardians(newArray);
|
||||
}}
|
||||
errors={errors?.student?.guardians || []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -191,14 +248,13 @@ export default function InscriptionFormShared({
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<h2 className="text-xl font-bold mb-4 text-gray-800">Fichiers à remplir</h2>
|
||||
<Table
|
||||
data={uploadedFiles}
|
||||
data={fileTemplates}
|
||||
columns={columns}
|
||||
itemsPerPage={5}
|
||||
currentPage={1}
|
||||
totalPages={1}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
<FileUpload onFileUpload={handleFileUpload} />
|
||||
</div>
|
||||
|
||||
{/* Boutons de contrôle */}
|
||||
@ -207,6 +263,44 @@ export default function InscriptionFormShared({
|
||||
<Button type="submit" text="Valider" primary />
|
||||
</div>
|
||||
</form>
|
||||
<Modal
|
||||
isOpen={showUploadModal}
|
||||
setIsOpen={setShowUploadModal}
|
||||
title="Téléverser un fichier"
|
||||
ContentComponent={() => (
|
||||
<>
|
||||
<DraggableFileUpload
|
||||
className="w-full"
|
||||
fileName={fileName}
|
||||
onFileSelect={(selectedFile) => {
|
||||
setFile(selectedFile);
|
||||
setFileName(selectedFile.name);
|
||||
}}
|
||||
>
|
||||
<input type="hidden" name="template" value={currentTemplateId} />
|
||||
<input type="hidden" name="register_form" value={formData.id} />
|
||||
</DraggableFileUpload>
|
||||
<div className="mt-4 flex justify-center space-x-4">
|
||||
<Button
|
||||
text="Annuler"
|
||||
onClick={() => {
|
||||
setShowUploadModal(false);
|
||||
setCurrentTemplateId(null);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
text="Valider"
|
||||
onClick={() => {
|
||||
setShowUploadModal(false);
|
||||
handleFileUpload(file, fileName);
|
||||
setCurrentTemplateId(null);
|
||||
}}
|
||||
primary={true}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -5,8 +5,12 @@ import React from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import 'react-phone-number-input/style.css'
|
||||
|
||||
export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian}) {
|
||||
const t = useTranslations('ResponsableInputFields');
|
||||
export default function ResponsableInputFields({guardians, onGuardiansChange, addGuardian, deleteGuardian, errors = []}) {
|
||||
const t = useTranslations('ResponsableInputFields');
|
||||
|
||||
const getError = (index, field) => {
|
||||
return errors[index]?.[field]?.[0];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
@ -33,6 +37,8 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('lastname')}
|
||||
value={item.last_name}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "last_name", event.target.value)}}
|
||||
errorMsg={getError(index, 'last_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="prenomResponsable"
|
||||
@ -40,6 +46,8 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('firstname')}
|
||||
value={item.first_name}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "first_name", event.target.value)}}
|
||||
errorMsg={getError(index, 'first_name')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -50,12 +58,14 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('email')}
|
||||
value={item.email}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "email", event.target.value)}}
|
||||
errorMsg={getError(index, 'email')}
|
||||
/>
|
||||
<InputPhone
|
||||
name="telephoneResponsable"
|
||||
label={t('phone')}
|
||||
value={item.phone}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "phone", event)}}
|
||||
errorMsg={getError(index, 'phone')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -66,6 +76,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('birthdate')}
|
||||
value={item.birth_date}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "birth_date", event.target.value)}}
|
||||
errorMsg={getError(index, 'birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="professionResponsable"
|
||||
@ -73,6 +84,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('profession')}
|
||||
value={item.profession}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "profession", event.target.value)}}
|
||||
errorMsg={getError(index, 'profession')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -83,6 +95,7 @@ export default function ResponsableInputFields({guardians, onGuardiansChange, ad
|
||||
label={t('address')}
|
||||
value={item.address}
|
||||
onChange={(event) => {onGuardiansChange(item.id, "address", event.target.value)}}
|
||||
errorMsg={getError(index, 'address')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user