mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 23:43:22 +00:00
refactor: Revue de la modale permettant de créer un dossier
d'inscription
This commit is contained in:
@ -805,8 +805,7 @@ const handleFileUpload = ({file, name, is_required, order}) => {
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
setIsOpen={setIsOpen}
|
setIsOpen={setIsOpen}
|
||||||
title={"Création d'un nouveau dossier d'inscription"}
|
title={"Nouveau dossier d'inscription"}
|
||||||
size='sm:w-1/4'
|
|
||||||
ContentComponent={() => (
|
ContentComponent={() => (
|
||||||
<InscriptionForm students={students}
|
<InscriptionForm students={students}
|
||||||
registrationDiscounts={registrationDiscounts}
|
registrationDiscounts={registrationDiscounts}
|
||||||
|
|||||||
@ -5,9 +5,11 @@ import ToggleSwitch from '@/components/ToggleSwitch';
|
|||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import Table from '@/components/Table';
|
import Table from '@/components/Table';
|
||||||
import FeesSection from '@/components/Structure/Tarification/FeesSection';
|
import FeesSection from '@/components/Structure/Tarification/FeesSection';
|
||||||
import DiscountsSection from '../Structure/Tarification/DiscountsSection';
|
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
|
||||||
|
import Navigation from '@/components/Navigation';
|
||||||
|
import StepTitle from '@/components/StepTitle';
|
||||||
|
|
||||||
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit }) => {
|
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit, currentStep }) => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
studentLastName: '',
|
studentLastName: '',
|
||||||
studentFirstName: '',
|
studentFirstName: '',
|
||||||
@ -22,12 +24,47 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
selectedTuitionFees: []
|
selectedTuitionFees: []
|
||||||
});
|
});
|
||||||
|
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(currentStep || 1);
|
||||||
const [selectedStudent, setSelectedEleve] = useState('');
|
const [selectedStudent, setSelectedEleve] = useState('');
|
||||||
const [existingGuardians, setExistingGuardians] = useState([]);
|
const [existingGuardians, setExistingGuardians] = useState([]);
|
||||||
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
|
const [totalRegistrationAmount, setTotalRegistrationAmount] = useState(0);
|
||||||
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
|
const [totalTuitionAmount, setTotalTuitionAmount] = useState(0);
|
||||||
const maxStep = 6
|
|
||||||
|
const stepTitles = {
|
||||||
|
1: 'Nouvel élève',
|
||||||
|
2: 'Nouveau Responsable',
|
||||||
|
3: "Frais d'inscription",
|
||||||
|
4: 'Frais de scolarité',
|
||||||
|
5: 'Récapitulatif'
|
||||||
|
};
|
||||||
|
|
||||||
|
const steps = ['1', '2', '3', '4', 'Récap'];
|
||||||
|
|
||||||
|
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
|
||||||
|
const isStep2Valid = (
|
||||||
|
(formData.responsableType === "new" && formData.guardianEmail.length > 0) ||
|
||||||
|
(formData.responsableType === "existing" && formData.selectedGuardians.length > 0)
|
||||||
|
);
|
||||||
|
const isStep3Valid = formData.selectedRegistrationFees.length > 0;
|
||||||
|
const isStep4Valid = formData.selectedTuitionFees.length > 0;
|
||||||
|
const isStep5Valid = isStep1Valid && isStep2Valid && isStep3Valid && isStep4Valid;
|
||||||
|
|
||||||
|
const isStepValid = (stepNumber) => {
|
||||||
|
switch (stepNumber) {
|
||||||
|
case 1:
|
||||||
|
return isStep1Valid;
|
||||||
|
case 2:
|
||||||
|
return isStep2Valid;
|
||||||
|
case 3:
|
||||||
|
return isStep3Valid;
|
||||||
|
case 4:
|
||||||
|
return isStep4Valid;
|
||||||
|
case 5:
|
||||||
|
return isStep5Valid;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Calcul du montant total des frais d'inscription lors de l'initialisation
|
// Calcul du montant total des frais d'inscription lors de l'initialisation
|
||||||
@ -39,6 +76,10 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
|
|
||||||
}, [registrationDiscounts, registrationFees]);
|
}, [registrationDiscounts, registrationFees]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStep(currentStep || 1);
|
||||||
|
}, [currentStep]);
|
||||||
|
|
||||||
const handleToggleChange = () => {
|
const handleToggleChange = () => {
|
||||||
setFormData({ ...formData, autoMail: !formData.autoMail });
|
setFormData({ ...formData, autoMail: !formData.autoMail });
|
||||||
};
|
};
|
||||||
@ -52,7 +93,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nextStep = () => {
|
const nextStep = () => {
|
||||||
if (step < maxStep) {
|
if (step < steps.length) {
|
||||||
setStep(step + 1);
|
setStep(step + 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -181,362 +222,358 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
|
|||||||
return finalAmount.toFixed(2);
|
return finalAmount.toFixed(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLabelAttenuated = (item) => {
|
|
||||||
return !formData.selectedRegistrationDiscounts.includes(parseInt(item.id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const isLabelFunction = (item) => {
|
|
||||||
return item.name + ' : ' + item.amount
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 mt-8">
|
<div className="space-y-4 mt-6">
|
||||||
{step === 1 && (
|
<Navigation
|
||||||
<div>
|
steps={steps}
|
||||||
<h2 className="text-l font-bold mb-4">Nouvel élève</h2>
|
step={step}
|
||||||
<InputTextIcon
|
setStep={setStep}
|
||||||
name="studentLastName"
|
isStepValid={isStepValid}
|
||||||
type="text"
|
stepTitles={stepTitles}
|
||||||
IconItem={User}
|
/>
|
||||||
placeholder="Nom de l'élève"
|
|
||||||
value={formData.studentLastName}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
<InputTextIcon
|
|
||||||
name="studentFirstName"
|
|
||||||
type="text"
|
|
||||||
IconItem={User}
|
|
||||||
placeholder="Prénom de l'élève"
|
|
||||||
value={formData.studentFirstName}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 2 && (
|
{step === 1 && (
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<h2 className="text-l font-bold mb-4">Responsable(s)</h2>
|
<InputTextIcon
|
||||||
<div className="flex flex-col space-y-4">
|
name="studentLastName"
|
||||||
<label className="flex items-center space-x-3">
|
type="text"
|
||||||
<input
|
IconItem={User}
|
||||||
type="radio"
|
placeholder="Nom de l'élève"
|
||||||
name="responsableType"
|
value={formData.studentLastName}
|
||||||
value="new"
|
onChange={handleChange}
|
||||||
checked={formData.responsableType === 'new'}
|
className="w-full"
|
||||||
onChange={handleChange}
|
/>
|
||||||
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
<InputTextIcon
|
||||||
/>
|
name="studentFirstName"
|
||||||
<span className="text-gray-900">Nouveau Responsable</span>
|
type="text"
|
||||||
</label>
|
IconItem={User}
|
||||||
<label className="flex items-center space-x-3">
|
placeholder="Prénom de l'élève"
|
||||||
<input
|
value={formData.studentFirstName}
|
||||||
type="radio"
|
onChange={handleChange}
|
||||||
name="responsableType"
|
className="w-full"
|
||||||
value="existing"
|
/>
|
||||||
checked={formData.responsableType === 'existing'}
|
</div>
|
||||||
onChange={handleChange}
|
)}
|
||||||
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
|
||||||
/>
|
|
||||||
<span className="text-gray-900">Responsable Existant</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
{formData.responsableType === 'new' && (
|
|
||||||
<InputTextIcon
|
|
||||||
name="guardianEmail"
|
|
||||||
type="email"
|
|
||||||
IconItem={Mail}
|
|
||||||
placeholder="Email du responsable"
|
|
||||||
value={formData.guardianEmail}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full mt-4"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{formData.responsableType === 'existing' && (
|
{step === 2 && (
|
||||||
<div className="mt-4">
|
<div className="mt-6">
|
||||||
<div className="mt-4" style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
<InputTextIcon
|
||||||
<table className="min-w-full bg-white border">
|
name="guardianPhone"
|
||||||
<thead>
|
type="tel"
|
||||||
<tr>
|
IconItem={Phone}
|
||||||
<th className="px-4 py-2 border">Nom</th>
|
placeholder="Numéro de téléphone (optionnel)"
|
||||||
<th className="px-4 py-2 border">Prénom</th>
|
value={formData.guardianPhone}
|
||||||
</tr>
|
onChange={handleChange}
|
||||||
</thead>
|
className="w-full mt-4"
|
||||||
<tbody>
|
/>
|
||||||
{students.map((student, index) => (
|
<div className="flex flex-col space-y-4 mt-6">
|
||||||
<tr
|
<label className="flex items-center space-x-3">
|
||||||
key={student.id}
|
<input
|
||||||
className={`cursor-pointer ${selectedStudent && selectedStudent.id === student.id ? 'bg-emerald-600 text-white' : index % 2 === 0 ? 'bg-emerald-100' : ''}`}
|
type="radio"
|
||||||
onClick={() => handleEleveSelection(student)}
|
name="responsableType"
|
||||||
>
|
value="new"
|
||||||
<td className="px-4 py-2 border">{student.last_name}</td>
|
checked={formData.responsableType === 'new'}
|
||||||
<td className="px-4 py-2 border">{student.first_name}</td>
|
onChange={handleChange}
|
||||||
</tr>
|
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
||||||
))}
|
/>
|
||||||
</tbody>
|
<span className="text-gray-900">Nouveau Responsable</span>
|
||||||
</table>
|
</label>
|
||||||
</div>
|
<label className="flex items-center space-x-3">
|
||||||
{selectedStudent && (
|
<input
|
||||||
<div className="mt-4">
|
type="radio"
|
||||||
<h3 className="font-bold">Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :</h3>
|
name="responsableType"
|
||||||
{existingGuardians.map((guardian) => (
|
value="existing"
|
||||||
<div key={guardian.id}>
|
checked={formData.responsableType === 'existing'}
|
||||||
<label className="flex items-center space-x-3 mt-2">
|
onChange={handleChange}
|
||||||
<input
|
className="form-radio h-3 w-3 text-emerald-600 focus:ring-emerald-500 hover:ring-emerald-400 checked:bg-emerald-600 checked:h-3 checked:w-3"
|
||||||
type="checkbox"
|
/>
|
||||||
checked={formData.selectedGuardians.includes(guardian.id)}
|
<span className="text-gray-900">Responsable Existant</span>
|
||||||
className="form-checkbox h-5 w-5 text-emerald-600"
|
</label>
|
||||||
onChange={() => handleResponsableSelection(guardian.id)}
|
|
||||||
/>
|
|
||||||
<span className="text-gray-900">
|
|
||||||
{guardian.last_name && guardian.first_name ? `${guardian.last_name} ${guardian.first_name}` : `${guardian.email}`}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 3 && (
|
|
||||||
<div className="mt-6">
|
|
||||||
<h2 className="text-l font-bold mb-4">Téléphone (optionnel)</h2>
|
|
||||||
<InputTextIcon
|
|
||||||
name="guardianPhone"
|
|
||||||
type="tel"
|
|
||||||
IconItem={Phone}
|
|
||||||
placeholder="Numéro de téléphone"
|
|
||||||
value={formData.guardianPhone}
|
|
||||||
onChange={handleChange}
|
|
||||||
className="w-full mt-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 4 && (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-l font-bold mb-4">Frais d'inscription</h2>
|
|
||||||
{registrationFees.length > 0 ? (
|
|
||||||
<>
|
|
||||||
<div className="mb-4">
|
|
||||||
<FeesSection
|
|
||||||
fees={registrationFees}
|
|
||||||
type={0}
|
|
||||||
subscriptionMode={true}
|
|
||||||
selectedFees={formData.selectedRegistrationFees}
|
|
||||||
handleFeeSelection={handleRegistrationFeeSelection}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<h2 className="text-l font-bold mb-4">Réductions</h2>
|
|
||||||
<div className="mb-4">
|
|
||||||
{registrationDiscounts.length > 0 ? (
|
|
||||||
<DiscountsSection
|
|
||||||
discounts={registrationDiscounts}
|
|
||||||
type={0}
|
|
||||||
subscriptionMode={true}
|
|
||||||
selectedDiscounts={formData.selectedRegistrationDiscounts}
|
|
||||||
handleDiscountSelection={handleRegistrationDiscountSelection}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<p className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative" role="alert">
|
|
||||||
<strong className="font-bold">Information</strong>
|
|
||||||
<span className="block sm:inline"> Aucune réduction n'a été créée sur les frais d'inscription.</span>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
data={[ {id: 1}]}
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
name: 'LIBELLE',
|
|
||||||
transform: () => <span>MONTANT TOTAL</span>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TOTAL',
|
|
||||||
transform: () => <b>{totalRegistrationAmount} €</b>
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
defaultTheme='bg-cyan-100'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
|
||||||
<strong className="font-bold">Attention!</strong>
|
|
||||||
<span className="block sm:inline"> Aucun frais d'inscription n'a été créé.</span>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === 5 && (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-l font-bold mb-4">Frais de scolarité</h2>
|
|
||||||
{tuitionFees.length > 0 ? (
|
|
||||||
<>
|
|
||||||
<div className="mb-4">
|
|
||||||
<FeesSection
|
|
||||||
fees={tuitionFees}
|
|
||||||
type={1}
|
|
||||||
subscriptionMode={true}
|
|
||||||
selectedFees={formData.selectedTuitionFees}
|
|
||||||
handleFeeSelection={handleTuitionFeeSelection}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<h2 className="text-l font-bold mb-4">Réductions</h2>
|
|
||||||
<div className="mb-4">
|
|
||||||
{tuitionDiscounts.length > 0 ? (
|
|
||||||
<DiscountsSection
|
|
||||||
discounts={tuitionDiscounts}
|
|
||||||
type={1}
|
|
||||||
subscriptionMode={true}
|
|
||||||
selectedDiscounts={formData.selectedTuitionDiscounts}
|
|
||||||
handleDiscountSelection={handleTuitionDiscountSelection}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<p className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative" role="alert">
|
|
||||||
<strong className="font-bold">Information</strong>
|
|
||||||
<span className="block sm:inline"> Aucune réduction n'a été créée sur les frais de scolarité.</span>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
data={[ {id: 1}]}
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
name: 'LIBELLE',
|
|
||||||
transform: () => <span>MONTANT TOTAL</span>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'TOTAL',
|
|
||||||
transform: () => <b>{totalTuitionAmount} €</b>
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
defaultTheme='bg-cyan-100'
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
|
||||||
<strong className="font-bold">Attention!</strong>
|
|
||||||
<span className="block sm:inline"> Aucun frais de scolarité n'a été créé.</span>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
)}
|
|
||||||
|
|
||||||
{step === maxStep && (
|
|
||||||
<div>
|
|
||||||
<h2 className="text-l font-bold mb-4">Récapitulatif</h2>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<section>
|
|
||||||
<h3 className="font-bold">Élève</h3>
|
|
||||||
<table className="min-w-full bg-white border">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-2 border">Nom</th>
|
|
||||||
<th className="px-4 py-2 border">Prénom</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td className="px-4 py-2 border">{formData.studentLastName}</td>
|
|
||||||
<td className="px-4 py-2 border">{formData.studentFirstName}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<h3 className="font-bold">Responsable(s)</h3>
|
|
||||||
{formData.responsableType === 'new' && (
|
|
||||||
<table className="min-w-full bg-white border">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-2 border">Email</th>
|
|
||||||
<th className="px-4 py-2 border">Téléphone</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td className="px-4 py-2 border">{formData.guardianEmail}</td>
|
|
||||||
<td className="px-4 py-2 border">{formData.guardianPhone}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)}
|
|
||||||
{formData.responsableType === 'existing' && selectedStudent && (
|
|
||||||
<div>
|
|
||||||
<p>Associé(s) à : {selectedStudent.nom} {selectedStudent.prenom}</p>
|
|
||||||
<table className="min-w-full bg-white border">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th className="px-4 py-2 border">Nom</th>
|
|
||||||
<th className="px-4 py-2 border">Prénom</th>
|
|
||||||
<th className="px-4 py-2 border">Email</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{existingGuardians.filter(guardian => formData.selectedGuardians.includes(guardian.id)).map((guardian) => (
|
|
||||||
<tr key={guardian.id}>
|
|
||||||
<td className="px-4 py-2 border">{guardian.last_name}</td>
|
|
||||||
<td className="px-4 py-2 border">{guardian.first_name}</td>
|
|
||||||
<td className="px-4 py-2 border">{guardian.email}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div className='mt-4'>
|
|
||||||
<ToggleSwitch
|
|
||||||
label="Envoi automatique"
|
|
||||||
checked={formData.autoMail}
|
|
||||||
onChange={handleToggleChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex justify-end mt-4 space-x-4">
|
|
||||||
{step > 1 && (
|
|
||||||
<Button text="Précédent"
|
|
||||||
onClick={prevStep}
|
|
||||||
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md shadow-sm hover:bg-gray-400 focus:outline-none"
|
|
||||||
secondary
|
|
||||||
name="Previous" />
|
|
||||||
)}
|
|
||||||
{step < maxStep ? (
|
|
||||||
<Button text="Suivant"
|
|
||||||
onClick={nextStep}
|
|
||||||
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
|
||||||
(step === 1 && (!formData.studentLastName || !formData.studentFirstName)) ||
|
|
||||||
(step === 2 && formData.responsableType === "new" && !formData.guardianEmail) ||
|
|
||||||
(step === 2 && formData.responsableType === "existing" && formData.selectedGuardians.length === 0)
|
|
||||||
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
|
||||||
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
|
||||||
}`}
|
|
||||||
disabled={(step === 1 && (!formData.studentLastName || !formData.studentFirstName)) ||
|
|
||||||
(step === 2 && formData.responsableType === "new" && !formData.guardianEmail) ||
|
|
||||||
(step === 2 && formData.responsableType === "existing" && formData.selectedGuardians.length === 0)
|
|
||||||
}
|
|
||||||
primary
|
|
||||||
name="Next" />
|
|
||||||
) : (
|
|
||||||
<Button text="Créer"
|
|
||||||
onClick={submit}
|
|
||||||
className="px-4 py-2 bg-emerald-500 text-white rounded-md shadow-sm hover:bg-emerald-600 focus:outline-none"
|
|
||||||
primary
|
|
||||||
name="Create" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
{formData.responsableType === 'new' && (
|
||||||
|
<InputTextIcon
|
||||||
|
name="guardianEmail"
|
||||||
|
type="email"
|
||||||
|
IconItem={Mail}
|
||||||
|
placeholder="Email du responsable"
|
||||||
|
value={formData.guardianEmail}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="w-full mt-4"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{formData.responsableType === 'existing' && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<div className="mt-4" style={{ maxHeight: '300px', overflowY: 'auto' }}>
|
||||||
|
<table className="min-w-full bg-white border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border">Nom</th>
|
||||||
|
<th className="px-4 py-2 border">Prénom</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{students.map((student, index) => (
|
||||||
|
<tr
|
||||||
|
key={student.id}
|
||||||
|
className={`cursor-pointer ${selectedStudent && selectedStudent.id === student.id ? 'bg-emerald-600 text-white' : index % 2 === 0 ? 'bg-emerald-100' : ''}`}
|
||||||
|
onClick={() => handleEleveSelection(student)}
|
||||||
|
>
|
||||||
|
<td className="px-4 py-2 border">{student.last_name}</td>
|
||||||
|
<td className="px-4 py-2 border">{student.first_name}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{selectedStudent && (
|
||||||
|
<div className="mt-4">
|
||||||
|
<h3 className="font-bold">Responsables associés à {selectedStudent.last_name} {selectedStudent.first_name} :</h3>
|
||||||
|
{existingGuardians.map((guardian) => (
|
||||||
|
<div key={guardian.id}>
|
||||||
|
<label className="flex items-center space-x-3 mt-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.selectedGuardians.includes(guardian.id)}
|
||||||
|
className="form-checkbox h-5 w-5 text-emerald-600"
|
||||||
|
onChange={() => handleResponsableSelection(guardian.id)}
|
||||||
|
/>
|
||||||
|
<span className="text-gray-900">
|
||||||
|
{guardian.last_name && guardian.first_name ? `${guardian.last_name} ${guardian.first_name}` : `${guardian.email}`}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === 3 && (
|
||||||
|
<div>
|
||||||
|
{registrationFees.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FeesSection
|
||||||
|
fees={registrationFees}
|
||||||
|
type={0}
|
||||||
|
subscriptionMode={true}
|
||||||
|
selectedFees={formData.selectedRegistrationFees}
|
||||||
|
handleFeeSelection={handleRegistrationFeeSelection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<StepTitle title='Réductions' />
|
||||||
|
<div className="mb-4">
|
||||||
|
{registrationDiscounts.length > 0 ? (
|
||||||
|
<DiscountsSection
|
||||||
|
discounts={registrationDiscounts}
|
||||||
|
type={0}
|
||||||
|
subscriptionMode={true}
|
||||||
|
selectedDiscounts={formData.selectedRegistrationDiscounts}
|
||||||
|
handleDiscountSelection={handleRegistrationDiscountSelection}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative" role="alert">
|
||||||
|
<strong className="font-bold">Information</strong>
|
||||||
|
<span className="block sm:inline"> Aucune réduction n'a été créée sur les frais d'inscription.</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<StepTitle title='Montant total' />
|
||||||
|
<Table
|
||||||
|
data={[ {id: 1}]}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
name: 'LIBELLE',
|
||||||
|
transform: () => <span>MONTANT TOTAL</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TOTAL',
|
||||||
|
transform: () => <b>{totalRegistrationAmount} €</b>
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
defaultTheme='bg-cyan-100'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||||
|
<strong className="font-bold">Attention!</strong>
|
||||||
|
<span className="block sm:inline"> Aucun frais d'inscription n'a été créé.</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === 4 && (
|
||||||
|
<div>
|
||||||
|
{tuitionFees.length > 0 ? (
|
||||||
|
<>
|
||||||
|
<div className="mb-4">
|
||||||
|
<FeesSection
|
||||||
|
fees={tuitionFees}
|
||||||
|
type={1}
|
||||||
|
subscriptionMode={true}
|
||||||
|
selectedFees={formData.selectedTuitionFees}
|
||||||
|
handleFeeSelection={handleTuitionFeeSelection}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<StepTitle title='Réductions' />
|
||||||
|
<div className="mb-4">
|
||||||
|
{tuitionDiscounts.length > 0 ? (
|
||||||
|
<DiscountsSection
|
||||||
|
discounts={tuitionDiscounts}
|
||||||
|
type={1}
|
||||||
|
subscriptionMode={true}
|
||||||
|
selectedDiscounts={formData.selectedTuitionDiscounts}
|
||||||
|
handleDiscountSelection={handleTuitionDiscountSelection}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p className="bg-orange-100 border border-orange-400 text-orange-700 px-4 py-3 rounded relative" role="alert">
|
||||||
|
<strong className="font-bold">Information</strong>
|
||||||
|
<span className="block sm:inline"> Aucune réduction n'a été créée sur les frais de scolarité.</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<StepTitle title='Montant total' />
|
||||||
|
<Table
|
||||||
|
data={[ {id: 1}]}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
name: 'LIBELLE',
|
||||||
|
transform: () => <span>MONTANT TOTAL</span>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TOTAL',
|
||||||
|
transform: () => <b>{totalTuitionAmount} €</b>
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
defaultTheme='bg-cyan-100'
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
|
||||||
|
<strong className="font-bold">Attention!</strong>
|
||||||
|
<span className="block sm:inline"> Aucun frais de scolarité n'a été créé.</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === steps.length && (
|
||||||
|
<div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<section>
|
||||||
|
<h3 className="font-bold">Élève</h3>
|
||||||
|
<table className="min-w-full bg-white border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border">Nom</th>
|
||||||
|
<th className="px-4 py-2 border">Prénom</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td className="px-4 py-2 border">{formData.studentLastName}</td>
|
||||||
|
<td className="px-4 py-2 border">{formData.studentFirstName}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3 className="font-bold">Responsable(s)</h3>
|
||||||
|
{formData.responsableType === 'new' && (
|
||||||
|
<table className="min-w-full bg-white border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border">Email</th>
|
||||||
|
<th className="px-4 py-2 border">Téléphone</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td className="px-4 py-2 border">{formData.guardianEmail}</td>
|
||||||
|
<td className="px-4 py-2 border">{formData.guardianPhone}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
{formData.responsableType === 'existing' && selectedStudent && (
|
||||||
|
<div>
|
||||||
|
<p>Associé(s) à : {selectedStudent.nom} {selectedStudent.prenom}</p>
|
||||||
|
<table className="min-w-full bg-white border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-2 border">Nom</th>
|
||||||
|
<th className="px-4 py-2 border">Prénom</th>
|
||||||
|
<th className="px-4 py-2 border">Email</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{existingGuardians.filter(guardian => formData.selectedGuardians.includes(guardian.id)).map((guardian) => (
|
||||||
|
<tr key={guardian.id}>
|
||||||
|
<td className="px-4 py-2 border">{guardian.last_name}</td>
|
||||||
|
<td className="px-4 py-2 border">{guardian.first_name}</td>
|
||||||
|
<td className="px-4 py-2 border">{guardian.email}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div className='mt-4'>
|
||||||
|
<ToggleSwitch
|
||||||
|
label="Envoi automatique"
|
||||||
|
checked={formData.autoMail}
|
||||||
|
onChange={handleToggleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex justify-end mt-4 space-x-4">
|
||||||
|
{step > 1 && (
|
||||||
|
<Button text="Précédent"
|
||||||
|
onClick={prevStep}
|
||||||
|
className="px-4 py-2 bg-gray-300 text-gray-700 rounded-md shadow-sm hover:bg-gray-400 focus:outline-none"
|
||||||
|
secondary
|
||||||
|
name="Previous" />
|
||||||
|
)}
|
||||||
|
{step < steps.length ? (
|
||||||
|
<Button text="Suivant"
|
||||||
|
onClick={nextStep}
|
||||||
|
className={`px-4 py-2 rounded-md shadow-sm focus:outline-none ${
|
||||||
|
(
|
||||||
|
(step === 1 && !isStep1Valid) ||
|
||||||
|
(step === 2 && !isStep2Valid) ||
|
||||||
|
(step === 3 && !isStep3Valid) ||
|
||||||
|
(step === 4 && !isStep4Valid)
|
||||||
|
)
|
||||||
|
? "bg-gray-300 text-gray-700 cursor-not-allowed"
|
||||||
|
: "bg-emerald-500 text-white hover:bg-emerald-600"
|
||||||
|
}`}
|
||||||
|
disabled={
|
||||||
|
(
|
||||||
|
(step === 1 && !isStep1Valid) ||
|
||||||
|
(step === 2 && !isStep2Valid) ||
|
||||||
|
(step === 3 && !isStep3Valid) ||
|
||||||
|
(step === 4 && !isStep4Valid)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
primary
|
||||||
|
name="Next" />
|
||||||
|
) : (
|
||||||
|
<Button text="Valider"
|
||||||
|
onClick={submit}
|
||||||
|
className="px-4 py-2 bg-emerald-500 text-white rounded-md shadow-sm hover:bg-emerald-600 focus:outline-none"
|
||||||
|
primary
|
||||||
|
name="Create" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import * as Dialog from '@radix-ui/react-dialog';
|
import * as Dialog from '@radix-ui/react-dialog';
|
||||||
import Button from '@/components/Button';
|
|
||||||
|
|
||||||
const Modal = ({ isOpen, setIsOpen, title, ContentComponent, size }) => {
|
const Modal = ({ isOpen, setIsOpen, title, ContentComponent }) => {
|
||||||
return (
|
return (
|
||||||
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<Dialog.Portal>
|
<Dialog.Portal>
|
||||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||||
<Dialog.Content className="fixed inset-0 flex items-center justify-center">
|
<Dialog.Content className="fixed inset-0 flex items-center justify-center">
|
||||||
<div className={`inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:p-6 ${size ? size : 'sm:w-full' }`} style={{ minWidth: '300px', width: 'auto' }}>
|
<div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle" style={{ width: '600px', maxWidth: '90%' }}>
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex justify-between items-start">
|
||||||
<Dialog.Title className="text-xl font-medium text-gray-900">
|
<Dialog.Title className="text-xl font-medium text-gray-900">
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
30
Front-End/src/components/Navigation.js
Normal file
30
Front-End/src/components/Navigation.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import StepTitle from '@/components/StepTitle';
|
||||||
|
|
||||||
|
const Navigation = ({ steps, step, setStep, isStepValid, stepTitles }) => {
|
||||||
|
return (
|
||||||
|
<div className="relative mb-4">
|
||||||
|
<div className="relative flex justify-between">
|
||||||
|
{steps.map((stepLabel, index) => {
|
||||||
|
const isCurrentStep = step === index + 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={index} className="flex flex-col items-center">
|
||||||
|
<div className={`mb-4 ${isCurrentStep ? 'text-gray-500 font-extrabold' : 'text-gray-500'}`}>
|
||||||
|
{stepLabel}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`w-4 h-4 rounded-full mb-4 ${isStepValid(index + 1) ? 'bg-emerald-500' : 'bg-yellow-300'} cursor-pointer`}
|
||||||
|
onClick={() => setStep(index + 1)}
|
||||||
|
style={{ transform: 'translateY(-50%)' }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<StepTitle title={stepTitles[step]} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Navigation;
|
||||||
16
Front-End/src/components/StepTitle.js
Normal file
16
Front-End/src/components/StepTitle.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const StepTitle = ({ title }) => {
|
||||||
|
return (
|
||||||
|
<div className="relative mb-4">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<div className="w-full border-t border-gray-300"></div>
|
||||||
|
</div>
|
||||||
|
<div className="relative flex justify-center">
|
||||||
|
<div className="px-4 bg-white text-gray-500">{title}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default StepTitle;
|
||||||
Reference in New Issue
Block a user