feat: Ajout des payementPlans dans le formulaire / ajout de la photo

This commit is contained in:
N3WT DE COMPET
2025-05-01 20:44:57 +02:00
parent 5851341235
commit d37aed5f64
11 changed files with 182 additions and 21 deletions

View File

@ -6,6 +6,9 @@ export default function FileUpload({
selectionMessage,
onFileSelect,
uploadedFileName,
existingFile,
required,
errorMsg,
}) {
const [localFileName, setLocalFileName] = useState(uploadedFileName || '');
const fileInputRef = useRef(null); // Utilisation de useRef pour cibler l'input
@ -31,7 +34,10 @@ export default function FileUpload({
return (
<div className="border p-4 rounded-md shadow-md">
<h3 className="text-lg font-semibold mb-4">{`${selectionMessage}`}</h3>
<h3 className="text-lg font-semibold mb-4">
{`${selectionMessage}`}
{required && <span className="text-red-500 ml-1">*</span>}
</h3>
<div
className="border-2 border-dashed border-gray-500 p-6 rounded-lg flex flex-col items-center justify-center cursor-pointer hover:border-emerald-500"
onClick={() => fileInputRef.current.click()} // Utilisation de la référence pour ouvrir l'explorateur
@ -52,10 +58,24 @@ export default function FileUpload({
Déposez votre fichier ici
</p>
<p className="text-sm text-gray-500 mt-2">
ou cliquez pour sélectionner un fichier PDF
ou cliquez pour sélectionner un fichier
</p>
</label>
</div>
{/* Affichage du fichier existant */}
{existingFile && !localFileName && (
<div className="mt-4 flex items-center space-x-4 bg-gray-100 p-3 rounded-md shadow-sm">
<CloudUpload className="w-6 h-6 text-emerald-500" />
<p className="text-sm font-medium text-gray-800">
<span className="font-semibold">
{existingFile.split('/').pop()}
</span>
</p>
</div>
)}
{/* Affichage du fichier sélectionné */}
{localFileName && (
<div className="mt-4 flex items-center space-x-4 bg-gray-100 p-3 rounded-md shadow-sm">
<CloudUpload className="w-6 h-6 text-emerald-500" />
@ -64,6 +84,9 @@ export default function FileUpload({
</p>
</div>
)}
{/* Message d'erreur */}
{errorMsg && <p className="mt-2 text-sm text-red-600">{errorMsg}</p>}
</div>
);
}

View File

@ -14,6 +14,8 @@ import {
import {
fetchRegistrationPaymentModes,
fetchTuitionPaymentModes,
fetchRegistrationPaymentPlans,
fetchTuitionPaymentPlans,
} from '@/app/actions/schoolAction';
import { BASE_URL } from '@/utils/Url';
import logger from '@/utils/logger';
@ -53,13 +55,14 @@ export default function InscriptionFormShared({
nationality: '',
attending_physician: '',
level: '',
registration_payment: '',
tuition_payment: '',
photo: '',
});
const [guardians, setGuardians] = useState([]);
const [registrationPaymentModes, setRegistrationPaymentModes] = useState([]);
const [tuitionPaymentModes, setTuitionPaymentModes] = useState([]);
const [registrationPaymentPlans, setRegistrationPaymentPlans] = useState([]);
const [tuitionPaymentPlans, setTuitionPaymentPlans] = useState([]);
// États pour la gestion des fichiers
const [uploadedFiles, setUploadedFiles] = useState([]);
@ -194,6 +197,12 @@ export default function InscriptionFormShared({
// Fetch data for tuition payment modes
handleTuitionPaymentModes();
// Fetch data for registration payment plans
handleRegistrationPaymentPlans();
// Fetch data for tuition payment plans
handleTuitionnPaymentPlans();
}
}, [selectedEstablishmentId]);
@ -223,6 +232,32 @@ export default function InscriptionFormShared({
);
};
const handleRegistrationPaymentPlans = () => {
fetchRegistrationPaymentPlans(selectedEstablishmentId)
.then((data) => {
const activePaymentPlans = data.filter(
(mode) => mode.is_active === true
);
setRegistrationPaymentPlans(activePaymentPlans);
})
.catch((error) =>
logger.error('Error fetching registration payment plans:', error)
);
};
const handleTuitionnPaymentPlans = () => {
fetchTuitionPaymentPlans(selectedEstablishmentId)
.then((data) => {
const activePaymentPlans = data.filter(
(mode) => mode.is_active === true
);
setTuitionPaymentPlans(activePaymentPlans);
})
.catch((error) =>
logger.error('Error fetching registration tuition plans:', error)
);
};
const handleFileUpload = (file, selectedFile) => {
if (!file || !selectedFile) {
logger.error('Données manquantes pour le téléversement.');
@ -344,8 +379,12 @@ export default function InscriptionFormShared({
status: isSepaPayment ? 8 : 3,
tuition_payment: formData.tuition_payment,
registration_payment: formData.registration_payment,
tuition_payment_plan: formData.tuition_payment_plan,
registration_payment_plan: formData.registration_payment_plan,
};
console.log('jsonData : ', jsonData);
// Créer un objet FormData
const formDataToSend = new FormData();
@ -450,6 +489,8 @@ export default function InscriptionFormShared({
setFormData={setFormData}
registrationPaymentModes={registrationPaymentModes}
tuitionPaymentModes={tuitionPaymentModes}
registrationPaymentPlans={registrationPaymentPlans}
tuitionPaymentPlans={tuitionPaymentPlans}
errors={errors}
setIsPageValid={setIsPage3Valid}
/>

View File

@ -1,11 +1,14 @@
import React, { useEffect } from 'react';
import SelectChoice from '@/components/SelectChoice';
import RadioList from '@/components/RadioList';
export default function PaymentMethodSelector({
formData,
setFormData,
registrationPaymentModes,
tuitionPaymentModes,
registrationPaymentPlans,
tuitionPaymentPlans,
errors,
setIsPageValid,
}) {
@ -14,6 +17,7 @@ export default function PaymentMethodSelector({
(field) => getLocalError(field) !== ''
);
setIsPageValid(isValid);
console.log('formdata : ', formData);
}, [formData, setIsPageValid]);
const paymentModesOptions = [
@ -23,19 +27,31 @@ export default function PaymentMethodSelector({
{ id: 4, name: 'Espèce' },
];
const paymentPlansOptions = [
{ id: 1, name: '1 fois' },
{ id: 3, name: '3 fois' },
{ id: 10, name: '10 fois' },
{ id: 12, name: '12 fois' },
];
const getError = (field) => {
return errors?.student?.[field]?.[0];
};
const getLocalError = (field) => {
if (
// Student Form
(field === 'registration_payment' &&
(!formData.registration_payment ||
String(formData.registration_payment).trim() === '')) ||
(field === 'tuition_payment' &&
(!formData.tuition_payment ||
String(formData.tuition_payment).trim() === ''))
String(formData.tuition_payment).trim() === '')) ||
(field === 'registration_payment_plan' &&
(!formData.registration_payment_plan ||
String(formData.registration_payment_plan).trim() === '')) ||
(field === 'tuition_payment_plan' &&
(!formData.tuition_payment_plan ||
String(formData.tuition_payment_plan).trim() === ''))
) {
return 'Champs requis';
}
@ -48,13 +64,12 @@ export default function PaymentMethodSelector({
return (
<>
{/* Frais d'inscription */}
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
{/* Titre */}
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">
Frais d'inscription
</h2>
{/* Section d'information */}
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
<p className="text-gray-700 text-sm mb-2">
<strong className="text-gray-900">Montant :</strong>{' '}
@ -80,15 +95,42 @@ export default function PaymentMethodSelector({
getLocalError('registration_payment')
}
/>
<RadioList
sectionLabel="Choisissez une option"
required
items={paymentPlansOptions
.filter((option) =>
registrationPaymentPlans.some(
(plan) => plan.frequency === option.id
)
)
.map((option) => ({
id: option.id,
label: option.name,
}))}
formData={{
...formData,
registration_payment_plan: parseInt(
formData.registration_payment_plan,
10
), // S'assurer que la valeur est un entier
}}
handleChange={(e) => {
const value = parseInt(e.target.value, 10);
onChange('registration_payment_plan', value); // Convertir la valeur en entier
}}
fieldName="registration_payment_plan"
className="mt-4"
/>
</div>
{/* Frais de scolarité */}
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200 mt-12">
{/* Titre */}
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">
Frais de scolarité
</h2>
{/* Section d'information */}
<div className="mb-6 bg-gray-50 p-4 rounded-lg border border-gray-100">
<p className="text-gray-700 text-sm mb-2">
<strong className="text-gray-900">Montant :</strong>{' '}
@ -113,6 +155,26 @@ export default function PaymentMethodSelector({
getError('tuition_payment') || getLocalError('tuition_payment')
}
/>
<RadioList
sectionLabel="Choisissez une option"
items={paymentPlansOptions
.filter((option) =>
tuitionPaymentPlans.some((plan) => plan.frequency === option.id)
)
.map((option) => ({
id: option.id,
label: option.name,
}))}
formData={formData}
handleChange={(e) => onChange('tuition_payment_plan', e.target.value)}
fieldName="tuition_payment_plan"
className="mt-4"
errorMsg={
getError('tuition_payment_plan') ||
getLocalError('tuition_payment_plan')
}
/>
</div>
</>
);

View File

@ -51,6 +51,8 @@ export default function StudentInfoForm({
level: data?.student?.level || '',
registration_payment: data?.registration_payment || '',
tuition_payment: data?.tuition_payment || '',
registration_payment_plan: data?.registration_payment_plan || '',
tuition_payment_plan: data?.tuition_payment_plan || '',
totalRegistrationFees: data?.totalRegistrationFees,
totalTuitionFees: data?.totalTuitionFees,
});
@ -96,7 +98,8 @@ export default function StudentInfoForm({
(!formData.attending_physician ||
formData.attending_physician.trim() === '')) ||
(field === 'level' &&
(!formData.level || String(formData.level).trim() === ''))
(!formData.level || String(formData.level).trim() === '')) ||
(field === 'photo' && !formData.photo)
) {
return 'Champs requis';
}
@ -230,6 +233,9 @@ export default function StudentInfoForm({
<FileUpload
selectionMessage="Sélectionnez une photo à uploader"
onFileSelect={(file) => handlePhotoUpload(file)}
existingFile={formData.photo}
required
errorMsg={getError('photo') || getLocalError('photo')}
/>
</div>
</>

View File

@ -7,9 +7,17 @@ const RadioList = ({
fieldName,
icon: Icon,
className,
sectionLabel,
required,
}) => {
return (
<div className={`mb-4 ${className}`}>
{sectionLabel && (
<h3 className="text-lg font-semibold text-gray-800 mb-2">
{sectionLabel}
{required && <span className="text-red-500 ml-1">*</span>}
</h3>
)}
<div className="grid grid-cols-1 gap-4">
{items.map((item) => (
<div key={item.id} className="flex items-center">