mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
feat: Ajout de la fratrie / Gestion des index de fratrie / Gestion des
required
This commit is contained in:
@ -5,6 +5,7 @@ export default function InputText({
|
||||
value,
|
||||
onChange,
|
||||
errorMsg,
|
||||
errorLocalMsg,
|
||||
placeholder,
|
||||
className,
|
||||
required,
|
||||
@ -20,7 +21,11 @@ export default function InputText({
|
||||
{required && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
<div
|
||||
className={`mt-1 flex items-center border border-gray-200 rounded-md ${errorMsg ? 'border-red-500' : ''} hover:border-gray-400 focus-within:border-gray-500`}
|
||||
className={`mt-1 flex items-center border rounded-md ${
|
||||
errorMsg || errorLocalMsg
|
||||
? 'border-red-500 hover:border-red-700'
|
||||
: 'border-gray-200 hover:border-gray-400'
|
||||
} ${!errorMsg && !errorLocalMsg ? 'focus-within:border-gray-500' : ''}`}
|
||||
>
|
||||
<input
|
||||
type={type}
|
||||
|
||||
@ -49,6 +49,7 @@ export default function InscriptionFormShared({
|
||||
photo: null,
|
||||
last_name: '',
|
||||
first_name: '',
|
||||
gender: '',
|
||||
address: '',
|
||||
birth_date: '',
|
||||
birth_place: '',
|
||||
@ -370,14 +371,18 @@ export default function InscriptionFormShared({
|
||||
|
||||
// Vérifier si le mode de paiement sélectionné est un prélèvement SEPA
|
||||
const isSepaPayment =
|
||||
formData.registration_payment === '1' || formData.tuition_payment === '1';
|
||||
formData.registration_payment === 1 || formData.tuition_payment === 1;
|
||||
|
||||
// Préparer les données JSON
|
||||
const jsonData = {
|
||||
student: {
|
||||
...formData,
|
||||
guardians: guardians,
|
||||
siblings: siblings,
|
||||
siblings: siblings.map(({ id, ...rest }) =>
|
||||
id && typeof id === 'string' && id.startsWith('temp-')
|
||||
? rest
|
||||
: { id, ...rest }
|
||||
), // Supprimer les IDs temporaires
|
||||
},
|
||||
establishment: selectedEstablishmentId,
|
||||
status: isSepaPayment ? 8 : 3,
|
||||
@ -398,6 +403,8 @@ export default function InscriptionFormShared({
|
||||
formDataToSend.append('photo', formData.photo);
|
||||
}
|
||||
|
||||
console.log('submit : ', jsonData);
|
||||
|
||||
// Appeler la fonction onSubmit avec les données FormData
|
||||
onSubmit(formDataToSend);
|
||||
};
|
||||
|
||||
@ -82,18 +82,18 @@ export default function PaymentMethodSelector({
|
||||
label="Mode de Paiement"
|
||||
placeHolder="Sélectionner un mode de paiement"
|
||||
selected={formData.registration_payment}
|
||||
callback={(e) => onChange('registration_payment', e.target.value)}
|
||||
callback={(e) =>
|
||||
onChange('registration_payment', parseInt(e.target.value, 10))
|
||||
}
|
||||
choices={registrationPaymentModes.map((mode) => ({
|
||||
value: mode.mode,
|
||||
label:
|
||||
paymentModesOptions.find((option) => option.id === mode.mode)
|
||||
?.name || 'Mode inconnu',
|
||||
}))}
|
||||
errorMsg={getError('registration_payment')}
|
||||
errorLocalMsg={getLocalError('registration_payment')}
|
||||
required
|
||||
errorMsg={
|
||||
getError('registration_payment') ||
|
||||
getLocalError('registration_payment')
|
||||
}
|
||||
/>
|
||||
|
||||
<RadioList
|
||||
@ -143,17 +143,18 @@ export default function PaymentMethodSelector({
|
||||
label="Mode de Paiement"
|
||||
placeHolder="Sélectionner un mode de paiement"
|
||||
selected={formData.tuition_payment}
|
||||
callback={(e) => onChange('tuition_payment', e.target.value)}
|
||||
callback={(e) =>
|
||||
onChange('tuition_payment', parseInt(e.target.value, 10))
|
||||
}
|
||||
choices={tuitionPaymentModes.map((mode) => ({
|
||||
value: mode.mode,
|
||||
label:
|
||||
paymentModesOptions.find((option) => option.id === mode.mode)
|
||||
?.name || 'Mode inconnu',
|
||||
}))}
|
||||
errorMsg={getError('tuition_payment')}
|
||||
errorLocalMsg={getLocalError('tuition_payment')}
|
||||
required
|
||||
errorMsg={
|
||||
getError('tuition_payment') || getLocalError('tuition_payment')
|
||||
}
|
||||
/>
|
||||
|
||||
<RadioList
|
||||
@ -167,7 +168,10 @@ export default function PaymentMethodSelector({
|
||||
label: option.name,
|
||||
}))}
|
||||
formData={formData}
|
||||
handleChange={(e) => onChange('tuition_payment_plan', e.target.value)}
|
||||
handleChange={(e) => {
|
||||
const value = parseInt(e.target.value, 10);
|
||||
onChange('tuition_payment_plan', value); // Convertir la valeur en entier
|
||||
}}
|
||||
fieldName="tuition_payment_plan"
|
||||
className="mt-4"
|
||||
errorMsg={
|
||||
|
||||
@ -33,6 +33,7 @@ export default function ResponsableInputFields({
|
||||
};
|
||||
|
||||
const getLocalError = (index, field) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (
|
||||
// Student Form
|
||||
(field === 'last_name' &&
|
||||
@ -43,7 +44,8 @@ export default function ResponsableInputFields({
|
||||
guardians[index].first_name.trim() === '')) ||
|
||||
(field === 'associated_profile_email' &&
|
||||
(!guardians[index].associated_profile_email ||
|
||||
guardians[index].associated_profile_email.trim() === '')) ||
|
||||
guardians[index].associated_profile_email.trim() === '' ||
|
||||
!emailRegex.test(guardians[index].associated_profile_email))) ||
|
||||
(field === 'birth_date' &&
|
||||
(!guardians[index].birth_date ||
|
||||
guardians[index].birth_date.trim() === '')) ||
|
||||
@ -138,10 +140,8 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'last_name', event.target.value);
|
||||
}}
|
||||
errorMsg={
|
||||
getError(index, 'last_name') ||
|
||||
getLocalError(index, 'last_name')
|
||||
}
|
||||
errorMsg={getError('last_name')}
|
||||
errorLocalMsg={getLocalError(index, 'last_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
@ -152,10 +152,8 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'first_name', event.target.value);
|
||||
}}
|
||||
errorMsg={
|
||||
getError(index, 'first_name') ||
|
||||
getLocalError(index, 'first_name')
|
||||
}
|
||||
errorMsg={getError('first_name')}
|
||||
errorLocalMsg={getLocalError(index, 'first_name')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -173,11 +171,9 @@ export default function ResponsableInputFields({
|
||||
event.target.value
|
||||
);
|
||||
}}
|
||||
errorMsg={getError('associated_profile_email')}
|
||||
errorLocalMsg={getLocalError(index, 'associated_profile_email')}
|
||||
required
|
||||
errorMsg={
|
||||
getError(index, 'associated_profile_email') ||
|
||||
getLocalError(index, 'associated_profile_email')
|
||||
}
|
||||
/>
|
||||
<InputPhone
|
||||
name="telephoneResponsable"
|
||||
@ -186,8 +182,9 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'phone', event.target.value);
|
||||
}}
|
||||
errorMsg={getError('phone')}
|
||||
errorLocalMsg={getLocalError(index, 'phone')}
|
||||
required
|
||||
errorMsg={getError(index, 'phone')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -200,11 +197,9 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'birth_date', event.target.value);
|
||||
}}
|
||||
errorMsg={getError('birth_date')}
|
||||
errorLocalMsg={getLocalError(index, 'birth_date')}
|
||||
required
|
||||
errorMsg={
|
||||
getError(index, 'birth_date') ||
|
||||
getLocalError(index, 'birth_date')
|
||||
}
|
||||
/>
|
||||
<InputText
|
||||
name="professionResponsable"
|
||||
@ -214,11 +209,9 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'profession', event.target.value);
|
||||
}}
|
||||
errorMsg={getError('profession')}
|
||||
errorLocalMsg={getLocalError(index, 'profession')}
|
||||
required
|
||||
errorMsg={
|
||||
getError(index, 'profession') ||
|
||||
getLocalError(index, 'profession')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -231,10 +224,9 @@ export default function ResponsableInputFields({
|
||||
onChange={(event) => {
|
||||
onGuardiansChange(item.id, 'address', event.target.value);
|
||||
}}
|
||||
errorMsg={getError('address')}
|
||||
errorLocalMsg={getLocalError(index, 'address')}
|
||||
required
|
||||
errorMsg={
|
||||
getError(index, 'address') || getLocalError(index, 'address')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import InputText from '@/components/InputText';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Trash2, Plus, Users } from 'lucide-react';
|
||||
import SectionHeader from '@/components/SectionHeader';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export default function SiblingInputFields({
|
||||
siblings,
|
||||
@ -67,6 +68,7 @@ export default function SiblingInputFields({
|
||||
setSiblings([
|
||||
...siblings,
|
||||
{
|
||||
id: `temp-${uuidv4()}`,
|
||||
last_name: '',
|
||||
first_name: '',
|
||||
birth_date: '',
|
||||
@ -105,10 +107,8 @@ export default function SiblingInputFields({
|
||||
onChange={(event) => {
|
||||
onSiblingsChange(item.id, 'last_name', event.target.value);
|
||||
}}
|
||||
errorMsg={
|
||||
getError(index, 'last_name') ||
|
||||
getLocalError(index, 'last_name')
|
||||
}
|
||||
errorMsg={getError('last_name')}
|
||||
errorLocalMsg={getLocalError(index, 'last_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
@ -119,10 +119,8 @@ export default function SiblingInputFields({
|
||||
onChange={(event) => {
|
||||
onSiblingsChange(item.id, 'first_name', event.target.value);
|
||||
}}
|
||||
errorMsg={
|
||||
getError(index, 'first_name') ||
|
||||
getLocalError(index, 'first_name')
|
||||
}
|
||||
errorMsg={getError('first_name')}
|
||||
errorLocalMsg={getLocalError(index, 'first_name')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@ -136,10 +134,8 @@ export default function SiblingInputFields({
|
||||
onChange={(event) => {
|
||||
onSiblingsChange(item.id, 'birth_date', event.target.value);
|
||||
}}
|
||||
errorMsg={
|
||||
getError(index, 'birth_date') ||
|
||||
getLocalError(index, 'birth_date')
|
||||
}
|
||||
errorMsg={getError('birth_date')}
|
||||
errorLocalMsg={getLocalError(index, 'birth_date')}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,11 @@ const levels = [
|
||||
{ value: '9', label: 'CM2' },
|
||||
];
|
||||
|
||||
const genders = [
|
||||
{ value: '1', label: 'Garçon' },
|
||||
{ value: '2', label: 'Fille' },
|
||||
];
|
||||
|
||||
export default function StudentInfoForm({
|
||||
studentId,
|
||||
formData,
|
||||
@ -46,6 +51,7 @@ export default function StudentInfoForm({
|
||||
photo: photoPath,
|
||||
last_name: data?.student?.last_name || '',
|
||||
first_name: data?.student?.first_name || '',
|
||||
gender: data?.student?.gender || '',
|
||||
address: data?.student?.address || '',
|
||||
birth_date: data?.student?.birth_date || '',
|
||||
birth_place: data?.student?.birth_place || '',
|
||||
@ -109,6 +115,8 @@ export default function StudentInfoForm({
|
||||
(!formData.last_name || formData.last_name.trim() === '')) ||
|
||||
(field === 'first_name' &&
|
||||
(!formData.first_name || formData.first_name.trim() === '')) ||
|
||||
(field === 'gender' &&
|
||||
(!formData.gender || String(formData.gender).trim() === '')) ||
|
||||
(field === 'nationality' &&
|
||||
(!formData.nationality || formData.nationality.trim() === '')) ||
|
||||
(field === 'birth_date' &&
|
||||
@ -124,8 +132,7 @@ export default function StudentInfoForm({
|
||||
(!formData.attending_physician ||
|
||||
formData.attending_physician.trim() === '')) ||
|
||||
(field === 'level' &&
|
||||
(!formData.level || String(formData.level).trim() === '')) ||
|
||||
(field === 'photo' && !formData.photo)
|
||||
(!formData.level || String(formData.level).trim() === ''))
|
||||
) {
|
||||
return 'Champs requis';
|
||||
}
|
||||
@ -156,37 +163,44 @@ export default function StudentInfoForm({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 space-y-8">
|
||||
<SectionHeader
|
||||
icon={User}
|
||||
title={`Informations de l'élève`}
|
||||
description={`Remplissez les champs requis`}
|
||||
/>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<InputText
|
||||
name="last_name"
|
||||
label="Nom"
|
||||
value={formData.last_name}
|
||||
onChange={(e) => onChange('last_name', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('last_name') || getLocalError('last_name')}
|
||||
errorMsg={getError('last_name')}
|
||||
errorLocalMsg={getLocalError('last_name')}
|
||||
/>
|
||||
<InputText
|
||||
name="first_name"
|
||||
label="Prénom"
|
||||
value={formData.first_name}
|
||||
onChange={(e) => onChange('first_name', e.target.value)}
|
||||
errorMsg={getError('first_name') || getLocalError('first_name')}
|
||||
errorMsg={getError('first_name')}
|
||||
errorLocalMsg={getLocalError('first_name')}
|
||||
required
|
||||
/>
|
||||
<InputText
|
||||
name="nationality"
|
||||
label="Nationalité"
|
||||
value={formData.nationality}
|
||||
<SelectChoice
|
||||
name="gender"
|
||||
label="Genre"
|
||||
placeHolder="Sélectionner un genre"
|
||||
selected={formData.gender}
|
||||
callback={(e) => onChange('gender', e.target.value)}
|
||||
choices={genders}
|
||||
required
|
||||
onChange={(e) => onChange('nationality', e.target.value)}
|
||||
errorMsg={getError('nationality') || getLocalError('nationality')}
|
||||
errorMsg={getError('gender')}
|
||||
errorLocalMsg={getLocalError('gender')}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<InputText
|
||||
name="birth_date"
|
||||
type="date"
|
||||
@ -194,7 +208,8 @@ export default function StudentInfoForm({
|
||||
value={formData.birth_date}
|
||||
onChange={(e) => onChange('birth_date', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_date') || getLocalError('birth_date')}
|
||||
errorMsg={getError('birth_date')}
|
||||
errorLocalMsg={getLocalError('birth_date')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_place"
|
||||
@ -202,7 +217,8 @@ export default function StudentInfoForm({
|
||||
value={formData.birth_place}
|
||||
onChange={(e) => onChange('birth_place', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('birth_place') || getLocalError('birth_place')}
|
||||
errorMsg={getError('birth_place')}
|
||||
errorLocalMsg={getLocalError('birth_place')}
|
||||
/>
|
||||
<InputText
|
||||
name="birth_postal_code"
|
||||
@ -210,31 +226,39 @@ export default function StudentInfoForm({
|
||||
value={formData.birth_postal_code}
|
||||
onChange={(e) => onChange('birth_postal_code', e.target.value)}
|
||||
required
|
||||
errorMsg={
|
||||
getError('birth_postal_code') ||
|
||||
getLocalError('birth_postal_code')
|
||||
}
|
||||
errorMsg={getError('birth_postal_code')}
|
||||
errorLocalMsg={getLocalError('birth_postal_code')}
|
||||
/>
|
||||
<div className="md:col-span-2">
|
||||
<InputText
|
||||
name="address"
|
||||
label="Adresse"
|
||||
value={formData.address}
|
||||
onChange={(e) => onChange('address', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('address') || getLocalError('address')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<InputText
|
||||
name="nationality"
|
||||
label="Nationalité"
|
||||
value={formData.nationality}
|
||||
required
|
||||
onChange={(e) => onChange('nationality', e.target.value)}
|
||||
errorMsg={getError('nationality')}
|
||||
errorLocalMsg={getLocalError('nationality')}
|
||||
/>
|
||||
<InputText
|
||||
name="address"
|
||||
label="Adresse"
|
||||
value={formData.address}
|
||||
onChange={(e) => onChange('address', e.target.value)}
|
||||
required
|
||||
errorMsg={getError('address')}
|
||||
errorLocalMsg={getLocalError('address')}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<InputText
|
||||
name="attending_physician"
|
||||
label="Médecin Traitant"
|
||||
value={formData.attending_physician}
|
||||
onChange={(e) => onChange('attending_physician', e.target.value)}
|
||||
required
|
||||
errorMsg={
|
||||
getError('attending_physician') ||
|
||||
getLocalError('attending_physician')
|
||||
}
|
||||
errorMsg={getError('attending_physician')}
|
||||
errorLocalMsg={getLocalError('attending_physician')}
|
||||
/>
|
||||
<SelectChoice
|
||||
name="level"
|
||||
@ -244,25 +268,18 @@ export default function StudentInfoForm({
|
||||
callback={(e) => onChange('level', e.target.value)}
|
||||
choices={levels}
|
||||
required
|
||||
errorMsg={getError('level') || getLocalError('level')}
|
||||
errorMsg={getError('level')}
|
||||
errorLocalMsg={getLocalError('level')}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 gap-8">
|
||||
<FileUpload
|
||||
selectionMessage="Sélectionnez une photo"
|
||||
onFileSelect={(file) => handlePhotoUpload(file)}
|
||||
existingFile={formData.photo}
|
||||
errorMsg={getError('photo')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section pour l'upload des fichiers */}
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 mt-6">
|
||||
<SectionHeader
|
||||
icon={User}
|
||||
title={`Photo de l'élève`}
|
||||
description={`Ajoutez une photo de votre enfant`}
|
||||
/>
|
||||
<FileUpload
|
||||
selectionMessage="Sélectionnez une photo à uploader"
|
||||
onFileSelect={(file) => handlePhotoUpload(file)}
|
||||
existingFile={formData.photo}
|
||||
required
|
||||
errorMsg={getError('photo') || getLocalError('photo')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -8,9 +8,11 @@ export default function SelectChoice({
|
||||
callback,
|
||||
selected,
|
||||
errorMsg,
|
||||
errorLocalMsg,
|
||||
IconItem,
|
||||
disabled = false,
|
||||
}) {
|
||||
const isPlaceholderSelected = selected === ''; // Vérifie si le placeholder est sélectionné
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
@ -22,7 +24,11 @@ export default function SelectChoice({
|
||||
{required && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
<div
|
||||
className={`mt-1 flex items-center border border-gray-200 rounded-md ${errorMsg ? 'border-red-500' : ''} ${disabled ? '' : 'hover:border-gray-400 focus-within:border-gray-500'}`}
|
||||
className={`mt-1 flex items-center border rounded-md ${
|
||||
errorMsg || errorLocalMsg
|
||||
? 'border-red-500 hover:border-red-700'
|
||||
: 'border-gray-200 hover:border-gray-400'
|
||||
} ${disabled ? '' : 'focus-within:border-gray-500'}`}
|
||||
>
|
||||
{IconItem && (
|
||||
<span className="inline-flex items-center px-3 text-gray-500 text-sm">
|
||||
@ -30,7 +36,9 @@ export default function SelectChoice({
|
||||
</span>
|
||||
)}
|
||||
<select
|
||||
className={`flex-1 px-3 py-2 block w-full sm:text-sm border-none focus:ring-0 outline-none rounded-md ${disabled ? 'bg-gray-100' : ''}`}
|
||||
className={`flex-1 px-3 py-2 block w-full sm:text-sm border-none focus:ring-0 outline-none rounded-md ${
|
||||
disabled ? 'bg-gray-100' : ''
|
||||
} ${isPlaceholderSelected ? 'italic text-gray-500' : 'not-italic text-gray-800'}`} // Applique le style classique si une option autre que le placeholder est sélectionnée
|
||||
type={type}
|
||||
id={name}
|
||||
name={name}
|
||||
@ -38,9 +46,17 @@ export default function SelectChoice({
|
||||
onChange={callback}
|
||||
disabled={disabled}
|
||||
>
|
||||
<option value="">{placeHolder?.toLowerCase()}</option>
|
||||
{choices.map(({ value, label }, index) => (
|
||||
<option key={value} value={value}>
|
||||
{/* Placeholder en italique */}
|
||||
<option value="" className="italic text-gray-500">
|
||||
{placeHolder?.toLowerCase()}
|
||||
</option>
|
||||
{/* Autres options sans italique */}
|
||||
{choices.map(({ value, label }) => (
|
||||
<option
|
||||
key={value}
|
||||
value={value}
|
||||
className="not-italic text-gray-800"
|
||||
>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user