diff --git a/Front-End/src/components/Inscription/InscriptionForm.js b/Front-End/src/components/Inscription/InscriptionForm.js
index a33d607..0f7bab6 100644
--- a/Front-End/src/components/Inscription/InscriptionForm.js
+++ b/Front-End/src/components/Inscription/InscriptionForm.js
@@ -6,8 +6,8 @@ import Button from '@/components/Button';
import Table from '@/components/Table';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
-import Navigation from '@/components/Navigation';
-import StepTitle from '@/components/StepTitle';
+import SectionTitle from '@/components/SectionTitle';
+import ProgressStep from '@/components/ProgressStep';
const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, registrationFees, tuitionFees, onSubmit, currentStep }) => {
const [formData, setFormData] = useState({
@@ -37,8 +37,8 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
4: 'Frais de scolarité',
5: 'Récapitulatif'
};
-
- const steps = ['1', '2', '3', '4', 'Récap'];
+
+ const steps = ['Élève', 'Responsable', 'Inscription', 'Scolarité', 'Récap'];
const isStep1Valid = formData.studentLastName && formData.studentFirstName;
const isStep2Valid = (
@@ -136,7 +136,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
return { ...prevData, selectedRegistrationFees };
});
};
-
+
const handleTuitionFeeSelection = (feeId) => {
setFormData((prevData) => {
const selectedTuitionFees = prevData.selectedTuitionFees.includes(feeId)
@@ -147,7 +147,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
return { ...prevData, selectedTuitionFees };
});
};
-
+
const handleRegistrationDiscountSelection = (discountId) => {
setFormData((prevData) => {
const selectedRegistrationDiscounts = prevData.selectedRegistrationDiscounts.includes(discountId)
@@ -169,7 +169,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
return { ...prevData, selectedTuitionDiscounts };
});
};
-
+
const calculateFinalRegistrationAmount = (selectedRegistrationFees, selectedRegistrationDiscounts) => {
const totalFees = selectedRegistrationFees.reduce((sum, feeId) => {
const fee = registrationFees.find(f => f.id === feeId);
@@ -178,7 +178,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}
return sum;
}, 0);
-
+
const totalDiscounts = selectedRegistrationDiscounts.reduce((sum, discountId) => {
const discount = registrationDiscounts.find(d => d.id === discountId);
if (discount) {
@@ -190,9 +190,9 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}
return sum;
}, 0);
-
+
const finalAmount = totalFees - totalDiscounts;
-
+
return finalAmount.toFixed(2);
};
@@ -204,7 +204,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}
return sum;
}, 0);
-
+
const totalDiscounts = selectedTuitionDiscounts.reduce((sum, discountId) => {
const discount = tuitionDiscounts.find(d => d.id === discountId);
if (discount) {
@@ -216,20 +216,20 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
}
return sum;
}, 0);
-
+
const finalAmount = totalFees - totalDiscounts;
-
+
return finalAmount.toFixed(2);
};
return (
-
{step === 1 && (
@@ -257,15 +257,6 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
{step === 2 && (
-
{formData.responsableType === 'new' && (
+ <>
+
+ >
)}
{formData.responsableType === 'existing' && (
@@ -364,7 +366,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
handleFeeSelection={handleRegistrationFeeSelection}
/>
-
+
{registrationDiscounts.length > 0 ? (
)}
-
+
MONTANT TOTAL
},
{
- name: 'TOTAL',
+ name: 'TOTAL',
transform: () => {totalRegistrationAmount} €
}
]}
@@ -403,7 +405,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
Aucun frais d'inscription n'a été créé.
)}
-
+
)}
{step === 4 && (
@@ -419,7 +421,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r
handleFeeSelection={handleTuitionFeeSelection}
/>
-
+
{tuitionDiscounts.length > 0 ? (
)}
-
+
MONTANT TOTAL
},
{
- name: 'TOTAL',
+ name: 'TOTAL',
transform: () => {totalTuitionAmount} €
}
]}
diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js
index 34aa4c3..fad9a90 100644
--- a/Front-End/src/components/Inscription/InscriptionFormShared.js
+++ b/Front-End/src/components/Inscription/InscriptionFormShared.js
@@ -39,7 +39,18 @@ export default function InscriptionFormShared({
}) {
// États pour gérer les données du formulaire
const [isLoading, setIsLoading] = useState(true);
- const [formData, setFormData] = useState({});
+ const [formData, setFormData] = useState({
+ id: '',
+ last_name: '',
+ first_name: '',
+ address: '',
+ birth_date: '',
+ birth_place: '',
+ birth_postal_code: '',
+ nationality: '',
+ attending_physician: '',
+ level: ''
+ });
const [guardians, setGuardians] = useState([]);
diff --git a/Front-End/src/components/Modal.js b/Front-End/src/components/Modal.js
index d84aaaf..6ae3cfa 100644
--- a/Front-End/src/components/Modal.js
+++ b/Front-End/src/components/Modal.js
@@ -5,9 +5,9 @@ const Modal = ({ isOpen, setIsOpen, title, ContentComponent }) => {
-
-
-
+
+
+
{title}
@@ -23,7 +23,7 @@ const Modal = ({ isOpen, setIsOpen, title, ContentComponent }) => {
-
diff --git a/Front-End/src/components/Navigation.js b/Front-End/src/components/Navigation.js
deleted file mode 100644
index 9ad5ba2..0000000
--- a/Front-End/src/components/Navigation.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import StepTitle from '@/components/StepTitle';
-
-const Navigation = ({ steps, step, setStep, isStepValid, stepTitles }) => {
- return (
-
-
- {steps.map((stepLabel, index) => {
- const isCurrentStep = step === index + 1;
-
- return (
-
-
- {stepLabel}
-
-
setStep(index + 1)}
- style={{ transform: 'translateY(-50%)' }}
- >
-
- );
- })}
-
-
-
- );
-};
-
-export default Navigation;
\ No newline at end of file
diff --git a/Front-End/src/components/ProgressStep.js b/Front-End/src/components/ProgressStep.js
new file mode 100644
index 0000000..312d133
--- /dev/null
+++ b/Front-End/src/components/ProgressStep.js
@@ -0,0 +1,157 @@
+import React, { useState, useEffect } from 'react';
+
+const Step = ({ number, title, isActive, isValid, isCompleted, onClick }) => {
+ return (
+
+
+ {isCompleted ? (
+
+ ) : (
+ number
+ )}
+
+
+
+ {title}
+
+
+
+ );
+};
+
+const SpacerStep = ({ isCompleted }) => {
+ return (
+
+ );
+};
+
+const Dots = () => {
+ return (
+
+ );
+};
+
+const ProgressStep = ({ steps, stepTitles, currentStep, setStep, isStepValid }) => {
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
+ const [visibleSteps, setVisibleSteps] = useState(steps);
+
+ useEffect(() => {
+ const handleResize = () => setWindowWidth(window.innerWidth);
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ useEffect(() => {
+ const calculateVisibleSteps = () => {
+ const minWidth = 150; // Largeur minimale estimée par étape
+ const maxVisibleSteps = Math.floor(windowWidth / minWidth);
+
+ if (maxVisibleSteps >= steps.length) {
+ setVisibleSteps(steps);
+ return;
+ }
+
+ if (maxVisibleSteps < 4) {
+ // Garder seulement première, dernière et courante
+ let filtered = [steps[0]];
+ if (currentStep > 1 && currentStep < steps.length) {
+ filtered.push('...');
+ filtered.push(steps[currentStep - 1]);
+ }
+ if (currentStep < steps.length) {
+ filtered.push('...');
+ }
+ filtered.push(steps[steps.length - 1]);
+ setVisibleSteps(filtered);
+ } else {
+ // Garder première, dernière, courante et quelques étapes adjacentes
+ let filtered = [steps[0]];
+ if (currentStep > 2) filtered.push('...');
+ if (currentStep > 1 && currentStep < steps.length) {
+ filtered.push(steps[currentStep - 1]);
+ }
+ if (currentStep < steps.length - 1) filtered.push('...');
+ filtered.push(steps[steps.length - 1]);
+ setVisibleSteps(filtered);
+ }
+ };
+
+ calculateVisibleSteps();
+ }, [windowWidth, currentStep, steps]);
+
+ const handleStepClick = (stepIndex) => {
+ // Vérifie si on peut naviguer vers l'étape (toutes les étapes précédentes doivent être valides)
+ const canNavigate = Array.from({ length: stepIndex }, (_, i) => i + 1)
+ .every(step => isStepValid(step));
+
+ if (canNavigate) {
+ setStep(stepIndex + 1);
+ }
+ };
+
+ return (
+
+
+ {visibleSteps.map((step, index) => {
+ if (step === '...') {
+ return (
+
+
+ {index !== visibleSteps.length - 1 && }
+
+ );
+ }
+
+ const originalIndex = steps.indexOf(step);
+ return (
+
i + 1).every(s => isStepValid(s)) ? 'cursor-pointer' : 'cursor-not-allowed'}
+ `}
+ onClick={() => handleStepClick(originalIndex)}
+ >
+
+
+ originalIndex + 1}
+ isValid={isStepValid(originalIndex + 1)}
+ />
+ {index !== visibleSteps.length - 1 && (
+ originalIndex + 1} />
+ )}
+
+
+
+ );
+ })}
+
+
+ );
+};
+
+export default ProgressStep;
diff --git a/Front-End/src/components/ProtectedRoute.js b/Front-End/src/components/ProtectedRoute.js
index 61c7671..43f02c5 100644
--- a/Front-End/src/components/ProtectedRoute.js
+++ b/Front-End/src/components/ProtectedRoute.js
@@ -14,6 +14,9 @@ const ProtectedRoute = ({ children }) => {
}
}, [userId, router]);
+ if (!userId) {
+ return
Loading...
;
+ }
// Afficher les enfants seulement si l'utilisateur est connecté
return userId ? children : null;
};
diff --git a/Front-End/src/components/StepTitle.js b/Front-End/src/components/SectionTitle.js
similarity index 84%
rename from Front-End/src/components/StepTitle.js
rename to Front-End/src/components/SectionTitle.js
index e27410d..6f58e86 100644
--- a/Front-End/src/components/StepTitle.js
+++ b/Front-End/src/components/SectionTitle.js
@@ -1,6 +1,6 @@
import React from 'react';
-const StepTitle = ({ title }) => {
+const SectionTitle = ({ title }) => {
return (
@@ -13,4 +13,4 @@ const StepTitle = ({ title }) => {
);
};
-export default StepTitle;
\ No newline at end of file
+export default SectionTitle;
\ No newline at end of file
diff --git a/Front-End/src/components/Structure/Configuration/TeachersSection.js b/Front-End/src/components/Structure/Configuration/TeachersSection.js
index 2fadfa7..0eb1e57 100644
--- a/Front-End/src/components/Structure/Configuration/TeachersSection.js
+++ b/Front-End/src/components/Structure/Configuration/TeachersSection.js
@@ -8,6 +8,7 @@ import { createProfile, updateProfile } from '@/app/lib/authAction';
import useCsrfToken from '@/hooks/useCsrfToken';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
+import InputText from '@/components/InputText';
const ItemTypes = {
SPECIALITY: 'speciality',
@@ -158,7 +159,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
const handleChange = (e) => {
const { name, value } = e.target;
let parsedValue = value;
-
+
if (editingTeacher) {
setFormData((prevData) => ({
...prevData,
@@ -203,7 +204,7 @@ const TeachersSection = ({ teachers, setTeachers, specialities, handleCreate, ha
switch (column) {
case 'NOM':
return (
-
{
setSelectedLevel(niveau);
- const currentPlanning = selectedClass.plannings_read.find(planning => planning.niveau === niveau);
+ const currentPlanning = selectedClass.plannings_read?.find(planning => planning.niveau === niveau);
setSchedule(currentPlanning ? currentPlanning.planning : {});
}
}, [selectedClass, niveauxLabels]);
useEffect(() => {
if (selectedClass && selectedLevel) {
- const currentPlanning = selectedClass.plannings_read.find(planning => planning.niveau === selectedLevel);
+ const currentPlanning = selectedClass.plannings_read?.find(planning => planning.niveau === selectedLevel);
setSchedule(currentPlanning ? currentPlanning.planning : {});
}
}, [selectedClass, selectedLevel]);
diff --git a/Front-End/src/components/Structure/Tarification/DiscountsSection.js b/Front-End/src/components/Structure/Tarification/DiscountsSection.js
index 47a0a7c..9d8f04d 100644
--- a/Front-End/src/components/Structure/Tarification/DiscountsSection.js
+++ b/Front-End/src/components/Structure/Tarification/DiscountsSection.js
@@ -1,9 +1,9 @@
import React, { useState } from 'react';
import { Plus, Trash2, Edit3, Check, X, Percent, EuroIcon, Tag } from 'lucide-react';
import Table from '@/components/Table';
-import InputTextIcon from '@/components/InputTextIcon';
import Popup from '@/components/Popup';
import CheckBox from '@/components/CheckBox';
+import InputText from '@/components/InputText';
const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, handleDelete, type, subscriptionMode = false, selectedDiscounts, handleDiscountSelection }) => {
const [editingDiscount, setEditingDiscount] = useState(null);
@@ -103,7 +103,7 @@ const DiscountsSection = ({ discounts, setDiscounts, handleCreate, handleEdit, h
const renderInputField = (field, value, onChange, placeholder) => (
-
{
const [editingFee, setEditingFee] = useState(null);
@@ -112,7 +112,7 @@ const FeesSection = ({ fees, setFees, discounts, handleCreate, handleEdit, handl
const renderInputField = (field, value, onChange, placeholder) => (
- {
{ id: 2, label: 'Semestriel' },
{ id: 3, label: 'Trimestriel' },
];
-
+
const selectedDays = {
1: 'lundi',
2: 'mardi',
@@ -60,14 +60,14 @@ export const ClassesProvider = ({ children }) => {
const getNiveauxTabs = (levels) => {
// Trier les levels par id
const sortedNiveaux = levels.sort((a, b) => a - b);
-
+
// Mapper les labels correspondants
return sortedNiveaux.map(niveauId => {
const niveau = allNiveaux.find(n => n.id === niveauId);
return niveau ? { id: niveau.id, title: niveau.name, icon: School } : { id: 'unknown', title: 'Niveau inconnu', icon: null };
});
};
-
+
const generateAgeToNiveaux = (minAge, maxAge) => {
if (minAge === null || isNaN(minAge)) {
@@ -112,7 +112,7 @@ export const ClassesProvider = ({ children }) => {
const updatePlannings = (formData, existingPlannings) => {
return formData.levels.map(niveau => {
let existingPlanning = existingPlannings.find(planning => planning.niveau === niveau);
-
+
const emploiDuTemps = formData.opening_days.reduce((acc, dayId) => {
const dayName = selectedDays[dayId];
if (dayName) {
@@ -167,7 +167,7 @@ export const ClassesProvider = ({ children }) => {
}
// Fusionner les plannings existants avec les nouvelles données
- return existingPlanning
+ return existingPlanning
? { ...existingPlanning, ...updatedPlanning }
: updatedPlanning;
});
@@ -176,17 +176,21 @@ export const ClassesProvider = ({ children }) => {
const groupSpecialitiesBySubject = (teachers) => {
const groupedSpecialities = {};
+ if (!teachers) return [];
+
teachers.forEach(teacher => {
- teacher.specialites.forEach(specialite => {
- if (!groupedSpecialities[specialite.id]) {
- groupedSpecialities[specialite.id] = {
- ...specialite,
- teachers: [`${teacher.nom} ${teacher.prenom}`],
- };
- } else {
- groupedSpecialities[specialite.id].teachers.push(`${teacher.nom} ${teacher.prenom}`);
- }
- });
+ if (teacher && teacher.specialites) {
+ teacher.specialites.forEach(specialite => {
+ if (!groupedSpecialities[specialite.id]) {
+ groupedSpecialities[specialite.id] = {
+ ...specialite,
+ teachers: [`${teacher.nom} ${teacher.prenom}`],
+ };
+ } else {
+ groupedSpecialities[specialite.id].teachers.push(`${teacher.nom} ${teacher.prenom}`);
+ }
+ });
+ }
});
return Object.values(groupedSpecialities);
@@ -202,15 +206,15 @@ export const ClassesProvider = ({ children }) => {
};
return (
-