diff --git a/Front-End/next.config.mjs b/Front-End/next.config.mjs index 2a18797..7653014 100644 --- a/Front-End/next.config.mjs +++ b/Front-End/next.config.mjs @@ -5,12 +5,31 @@ const withNextIntl = createNextIntlPlugin(); /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'standalone', + webpack: (config, { isServer }) => { + // Configuration pour améliorer le hot reload dans Docker + config.watchOptions = { + poll: 1000, + aggregateTimeout: 300, + } + return config + }, experimental: { instrumentationHook: true, }, env: { NEXT_PUBLIC_APP_VERSION: pkg.version, }, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'i.pravatar.cc', + port: '', + pathname: '/**', + }, + ], + }, }; export default withNextIntl(nextConfig); \ No newline at end of file diff --git a/Front-End/src/app/[locale]/admin/layout.js b/Front-End/src/app/[locale]/admin/layout.js index 3f86c1c..5ab5dcf 100644 --- a/Front-End/src/app/[locale]/admin/layout.js +++ b/Front-End/src/app/[locale]/admin/layout.js @@ -26,6 +26,7 @@ import { import { disconnect } from '@/app/lib/authAction'; import { fetchEstablishment } from '@/app/lib/schoolAction'; +import Image from 'next/image'; export default function Layout({ children, @@ -75,13 +76,13 @@ export default function Layout({ <> {!isLoading && (
- +
{/* Header - h-16 = 64px */}
{headerTitle}
} + buttonContent={Profile} items={dropdownItems} buttonClassName="" menuClassName="absolute right-0 mt-2 w-48 bg-white border border-gray-200 rounded shadow-lg" diff --git a/Front-End/src/app/[locale]/admin/page.js b/Front-End/src/app/[locale]/admin/page.js index c48ed4b..767be6e 100644 --- a/Front-End/src/app/[locale]/admin/page.js +++ b/Front-End/src/app/[locale]/admin/page.js @@ -154,8 +154,8 @@ export default function DashboardPage() {
{classes.map((classe) => ( -
- +
+
))}
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index 9c09fca..a7e0094 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -239,7 +239,7 @@ const registerFormArchivedDataHandler = (data) => { fetchDataAndSetState(); - }, [reloadFetch, currentPage]); + }, [reloadFetch, itemsPerPage, currentPage,activeTab, searchTerm]); useEffect(() => { const fetchDataAndSetState = () => { @@ -271,7 +271,7 @@ const timeoutId = setTimeout(() => { fetchDataAndSetState(); }, 500); // Debounce la recherche return () => clearTimeout(timeoutId); -}, [searchTerm]); +}, [currentPage,itemsPerPage,searchTerm]); /** * UseEffect to update page count of tab @@ -284,7 +284,7 @@ useEffect(()=>{ } else if (activeTab === 'archived') { setTotalPages(Math.ceil(totalArchives / itemsPerPage)); } -},[currentPage]); +},[currentPage,activeTab,itemsPerPage,totalPending,totalSubscribed,totalArchives]); /** * Archives a registration form after user confirmation. * diff --git a/Front-End/src/app/[locale]/parents/layout.js b/Front-End/src/app/[locale]/parents/layout.js index b7f2f1a..ed69eac 100644 --- a/Front-End/src/app/[locale]/parents/layout.js +++ b/Front-End/src/app/[locale]/parents/layout.js @@ -35,7 +35,7 @@ export default function Layout({ .finally(() => { setIsLoading(false); }); - }, [userId]); + }, [setUserId, userId]); if (isLoading) { return
Loading...
; diff --git a/Front-End/src/app/[locale]/parents/messagerie/page.js b/Front-End/src/app/[locale]/parents/messagerie/page.js index 34bfc00..9f8196f 100644 --- a/Front-End/src/app/[locale]/parents/messagerie/page.js +++ b/Front-End/src/app/[locale]/parents/messagerie/page.js @@ -1,6 +1,7 @@ 'use client' import React, { useState, useRef, useEffect } from 'react'; import { SendHorizontal } from 'lucide-react'; +import Image from 'next/image'; const contacts = [ { id: 1, name: 'Facturation', profilePic: 'https://i.pravatar.cc/32' }, @@ -61,7 +62,7 @@ export default function MessageriePage() { className={`p-2 cursor-pointer ${selectedContact?.id === contact.id ? 'bg-gray-200' : ''}`} onClick={() => setSelectedContact(contact)} > - {`${contact.name}'s + {`${contact.name}'s {contact.name}
))} @@ -75,7 +76,7 @@ export default function MessageriePage() { style={{ borderRadius: message.isResponse ? '20px 20px 0 20px' : '20px 20px 20px 0', minWidth: '25%' }} >
- {`${selectedContact.name}'s + {`${selectedContact.name}'s {selectedContact.name} {new Date(message.date).toLocaleTimeString()}
diff --git a/Front-End/src/app/[locale]/users/password/reset/page.js b/Front-End/src/app/[locale]/users/password/reset/page.js index cae7a16..3e19b8c 100644 --- a/Front-End/src/app/[locale]/users/password/reset/page.js +++ b/Front-End/src/app/[locale]/users/password/reset/page.js @@ -52,7 +52,7 @@ export default function Page() { console.error('Error fetching data:', error); }); } - }, []); + }, [uuid]); function validate(formData) { if (useFakeData) { diff --git a/Front-End/src/app/_error/page.js b/Front-End/src/app/_error/page.js new file mode 100644 index 0000000..f5fb071 --- /dev/null +++ b/Front-End/src/app/_error/page.js @@ -0,0 +1,17 @@ +'use client'; + +import Link from 'next/link'; +import Logo from '@/components/Logo'; + +export default function Error() { + return ( +
+
+ +

Une erreur est survenue

+

Désolé, une erreur s'est produite.

+ Retour Accueil +
+
+ ); +} \ No newline at end of file diff --git a/Front-End/src/app/not-found.js b/Front-End/src/app/_not-found/page.js similarity index 71% rename from Front-End/src/app/not-found.js rename to Front-End/src/app/_not-found/page.js index fc0e9cd..c06ef63 100644 --- a/Front-End/src/app/not-found.js +++ b/Front-End/src/app/_not-found/page.js @@ -1,15 +1,17 @@ -import Link from 'next/link' -import Logo from '../components/Logo' +'use client'; + +import Link from 'next/link'; +import Logo from '@/components/Logo'; export default function NotFound() { return (
-
+

404 | Page non trouvée

-

La ressource que vous souhaitez consulter n'existe pas ou plus.

+

La ressource que vous souhaitez consulter n'existe pas ou plus.

Retour Accueil
- ) + ); } \ No newline at end of file diff --git a/Front-End/src/app/layout.js b/Front-End/src/app/layout.js index d290675..2ab20a5 100644 --- a/Front-End/src/app/layout.js +++ b/Front-End/src/app/layout.js @@ -1,6 +1,5 @@ import React from 'react'; import { NextIntlClientProvider } from 'next-intl'; - import {getMessages} from 'next-intl/server'; import "@/css/tailwind.css"; @@ -22,12 +21,13 @@ export const metadata = { }; export default async function RootLayout({ children, params: { locale } }) { - const messages = await getMessages(); + + const messages = await getMessages(locale); return ( - + {children} diff --git a/Front-End/src/components/Calendar.js b/Front-End/src/components/Calendar.js index 6c87781..0c9551a 100644 --- a/Front-End/src/components/Calendar.js +++ b/Front-End/src/components/Calendar.js @@ -75,7 +75,7 @@ const Calendar = ({ onDateClick, onEventClick }) => { onClick={() => setCurrentDate(new Date())} className="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-gray-900 bg-gray-100 hover:bg-gray-200 rounded-md transition-colors" > - Aujourd'hui + Aujourd'hui
@@ -408,7 +408,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r ) : (

Attention! - Aucun frais d'inscription n'a été créé. + Aucun frais d'inscription n'a été créé.

)}
@@ -440,7 +440,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r ) : (

Information - Aucune réduction n'a été créée sur les frais de scolarité. + Aucune réduction n'a été créée sur les frais de scolarité.

)}
@@ -463,7 +463,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r ) : (

Attention! - Aucun frais de scolarité n'a été créé. + Aucun frais de scolarité n'a été créé.

)} @@ -501,7 +501,7 @@ const InscriptionForm = ( { students, registrationDiscounts, tuitionDiscounts, r ) : (

Attention! - Aucun groupe de documents n'a été créé. + Aucun groupe de documents n'a été créé.

)} diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js index 221e9b6..1f458fd 100644 --- a/Front-End/src/components/Inscription/InscriptionFormShared.js +++ b/Front-End/src/components/Inscription/InscriptionFormShared.js @@ -249,7 +249,7 @@ export default function InscriptionFormShared({ {/* Section Élève */}
-

Informations de l'élève

+

Informations de l'élève

{ - updateDefaultDay(); - }, [dates, selectedFrequency]); - - const updateDefaultDay = () => { - const currentDates = dates[selectedFrequency]; - if (currentDates && currentDates.length > 0) { - const days = currentDates.map(date => new Date(date).getDate()); - const allSameDay = days.every(day => day === days[0]); - if (allSameDay) { - setDefaultDay(days[0]); + const updateDefaultDay = () => { + const currentDates = dates[selectedFrequency]; + if (currentDates && currentDates.length > 0) { + const days = currentDates.map(date => new Date(date).getDate()); + const allSameDay = days.every(day => day === days[0]); + if (allSameDay) { + setDefaultDay(days[0]); + } else { + setDefaultDay('-'); + setIsDefaultDayModified(false); + } } else { setDefaultDay('-'); - setIsDefaultDayModified(false); } - } else { - setDefaultDay('-'); - } - }; + }; + + updateDefaultDay(); + }, [dates, selectedFrequency]); const handleActivationChange = (value) => { const selectedPlan = paymentPlans.find(plan => plan.frequency === paymentPlansOptions.find(p => p.id === value)?.frequency); @@ -79,7 +79,7 @@ const PaymentPlanSelector = ({ paymentPlans, setPaymentPlans, handleEdit, type } handleEdit(selectedPlan.id, updatedData) .then(() => { - setPaymentPlans(prevPlans => prevPlans.map(plan => + setPaymentPlans(prevPlans => prevPlans.map(plan => plan.id === selectedPlan.id ? { ...plan, is_active: updatedData.is_active } : plan )); setActiveFrequencies(prevFrequencies => { @@ -172,7 +172,7 @@ const PaymentPlanSelector = ({ paymentPlans, setPaymentPlans, handleEdit, type } }; handleEdit(selectedPlan.id, updatedData) - .then(() => { + .then(() => { setPopupMessage(`Mise à jour des dates d'échéances effectuée avec succès`); setPopupVisible(true); setIsDefaultDayModified(false); diff --git a/Front-End/src/components/RegistrationFileGroupList.js b/Front-End/src/components/RegistrationFileGroupList.js index cda824d..c5e9e4f 100644 --- a/Front-End/src/components/RegistrationFileGroupList.js +++ b/Front-End/src/components/RegistrationFileGroupList.js @@ -10,7 +10,7 @@ export default function RegistrationFileGroupList() { return (
-

Groupes de fichiers d'inscription

+

Groupes de fichiers d'inscription

    {groups.map(group => (
  • {group.name}
  • diff --git a/Front-End/src/components/Structure/Configuration/ClassesSection.js b/Front-End/src/components/Structure/Configuration/ClassesSection.js index 7380b36..1b67be1 100644 --- a/Front-End/src/components/Structure/Configuration/ClassesSection.js +++ b/Front-End/src/components/Structure/Configuration/ClassesSection.js @@ -7,7 +7,8 @@ import SelectChoice from '@/components/SelectChoice'; import TeacherItem from '@/components/Structure/Configuration/TeacherItem'; import MultiSelect from '@/components/MultiSelect'; import LevelLabel from '@/components/CustomLabels/LevelLabel'; -import { DndProvider, HTML5Backend, useDrop } from 'react-dnd'; +import { DndProvider, useDrop } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { ESTABLISHMENT_ID } from '@/utils/Url'; const ItemTypes = { @@ -17,16 +18,13 @@ const ItemTypes = { const TeachersDropZone = ({ classe, handleTeachersChange, teachers, isEditing }) => { const [localTeachers, setLocalTeachers] = useState(classe.teachers_details || []); - useEffect(() => { - }, [teachers]); - useEffect(() => { setLocalTeachers(classe.teachers_details || []); }, [classe.teachers_details]); useEffect(() => { handleTeachersChange(localTeachers.map(teacher => teacher.id)); - }, [localTeachers]); + }, [handleTeachersChange, localTeachers]); const [{ isOver, canDrop }, drop] = useDrop({ accept: ItemTypes.TEACHER, @@ -105,20 +103,20 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi { id: 3, name: 'MS', age: 4 }, { id: 4, name: 'GS', age: 5 }, ]; - + const niveauxSecondCycle = [ { id: 5, name: 'CP', age: 6 }, { id: 6, name: 'CE1', age: 7 }, { id: 7, name: 'CE2', age: 8 }, ]; - + const niveauxTroisiemeCycle = [ { id: 8, name: 'CM1', age: 9 }, { id: 9, name: 'CM2', age: 10 }, ]; - + const allNiveaux = [...niveauxPremierCycle, ...niveauxSecondCycle, ...niveauxTroisiemeCycle]; - + const getNiveauxLabels = (levels) => { return levels.map(niveauId => { const niveau = allNiveaux.find(n => n.id === niveauId); @@ -131,10 +129,10 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi const currentDate = new Date(); const currentYear = currentDate.getFullYear(); const currentMonth = currentDate.getMonth() + 1; // Les mois sont indexés à partir de 0 - + // Si nous sommes avant septembre, l'année scolaire en cours a commencé l'année précédente const startYear = currentMonth >= 9 ? currentYear : currentYear - 1; - + const choices = []; for (let i = 0; i < 3; i++) { const year = startYear + i; @@ -232,7 +230,7 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi const handleMultiSelectChange = (selectedOptions) => { const levels = selectedOptions.map(option => option.id); - + if (editingClass) { setFormData((prevData) => ({ ...prevData, @@ -348,7 +346,7 @@ const ClassesSection = ({ classes, setClasses, teachers, handleCreate, handleEdi const levelLabels = Array.isArray(classe.levels) ? getNiveauxLabels(classe.levels) : []; return (
    - {levelLabels.length > 0 + {levelLabels.length > 0 ? levelLabels.map((label, index) => ( )) diff --git a/Front-End/src/components/Structure/Configuration/TeachersSection.js b/Front-End/src/components/Structure/Configuration/TeachersSection.js index 01d979c..f5ca14b 100644 --- a/Front-End/src/components/Structure/Configuration/TeachersSection.js +++ b/Front-End/src/components/Structure/Configuration/TeachersSection.js @@ -27,7 +27,7 @@ const SpecialitiesDropZone = ({ teacher, handleSpecialitiesChange, specialities, useEffect(() => { handleSpecialitiesChange(localSpecialities.map(speciality => speciality.id)); - }, [localSpecialities]); + }, [localSpecialities,handleSpecialitiesChange]); const [{ isOver, canDrop }, drop] = useDrop({ accept: ItemTypes.SPECIALITY, diff --git a/Front-End/src/components/Structure/Planning/PlanningClassView.js b/Front-End/src/components/Structure/Planning/PlanningClassView.js index 63ef1b0..176a1c4 100644 --- a/Front-End/src/components/Structure/Planning/PlanningClassView.js +++ b/Front-End/src/components/Structure/Planning/PlanningClassView.js @@ -21,7 +21,7 @@ const PlanningClassView = ({ schedule, onDrop, selectedLevel, handleUpdatePlanni if (schedule?.emploiDuTemps) { setCurrentPeriod(determineInitialPeriod(schedule.emploiDuTemps)); } - }, [schedule]); + }, [schedule, determineInitialPeriod]); if (!schedule || !schedule.emploiDuTemps) { return ( @@ -62,7 +62,7 @@ const PlanningClassView = ({ schedule, onDrop, selectedLevel, handleUpdatePlanni const [eventHour, eventMinute] = event.heure.split(':').map(Number); const eventStartTime = eventHour + eventMinute / 60; const eventEndTime = eventStartTime + parseFloat(event.duree); - + // Filtrer en fonction du selectedLevel return schedule.niveau === level && startTime >= eventStartTime && startTime < eventEndTime; }) || []; @@ -78,10 +78,10 @@ const PlanningClassView = ({ schedule, onDrop, selectedLevel, handleUpdatePlanni const renderTimeSlots = () => { const timeSlots = []; - + for (let hour = parseInt(formData.time_range[0], 10); hour <= parseInt(formData.time_range[1], 10); hour++) { const hourString = hour.toString().padStart(2, '0'); - + timeSlots.push(
    @@ -160,7 +160,7 @@ const PlanningClassView = ({ schedule, onDrop, selectedLevel, handleUpdatePlanni
    ))}
    - + {/* Contenu du planning */}
    diff --git a/Front-End/src/components/Structure/Planning/ScheduleManagement.js b/Front-End/src/components/Structure/Planning/ScheduleManagement.js index a3a3283..a9d363e 100644 --- a/Front-End/src/components/Structure/Planning/ScheduleManagement.js +++ b/Front-End/src/components/Structure/Planning/ScheduleManagement.js @@ -22,7 +22,7 @@ const ScheduleManagement = ({ handleUpdatePlanning, classes }) => { const [schedule, setSchedule] = useState(null); const { getNiveauxTabs } = useClasses(); - const niveauxLabels = Array.isArray(selectedClass?.levels) ? getNiveauxTabs(selectedClass.levels) : []; + const niveauxLabels = useMemo(() => Array.isArray(selectedClass?.levels) ? getNiveauxTabs(selectedClass.levels) : [], [selectedClass, getNiveauxTabs]); const [isModalOpen, setIsModalOpen] = useState(false); const handleOpenModal = () => setIsModalOpen(true); @@ -38,7 +38,7 @@ const ScheduleManagement = ({ handleUpdatePlanning, classes }) => { const currentPlanning = selectedClass.plannings_read?.find(planning => planning.niveau === niveau); setSchedule(currentPlanning ? currentPlanning.planning : {}); } - }, [selectedClass, niveauxLabels]); + }, [selectedClass, niveauxLabels, selectedLevel]); useEffect(() => { if (selectedClass && selectedLevel) { diff --git a/Front-End/src/components/Structure/Tarification/FeesManagement.js b/Front-End/src/components/Structure/Tarification/FeesManagement.js index f7f6f7a..685ec72 100644 --- a/Front-End/src/components/Structure/Tarification/FeesManagement.js +++ b/Front-End/src/components/Structure/Tarification/FeesManagement.js @@ -6,14 +6,14 @@ import PaymentModeSelector from '@/components/PaymentModeSelector'; import { BE_SCHOOL_FEES_URL, BE_SCHOOL_DISCOUNTS_URL, BE_SCHOOL_PAYMENT_PLANS_URL, BE_SCHOOL_PAYMENT_MODES_URL } from '@/utils/Url'; import { set } from 'lodash'; -const FeesManagement = ({ registrationDiscounts, - setRegistrationDiscounts, - tuitionDiscounts, - setTuitionDiscounts, - registrationFees, - setRegistrationFees, - tuitionFees, - setTuitionFees, +const FeesManagement = ({ registrationDiscounts, + setRegistrationDiscounts, + tuitionDiscounts, + setTuitionDiscounts, + registrationFees, + setRegistrationFees, + tuitionFees, + setTuitionFees, registrationPaymentPlans, setRegistrationPaymentPlans, tuitionPaymentPlans, @@ -22,20 +22,20 @@ const FeesManagement = ({ registrationDiscounts, setRegistrationPaymentModes, tuitionPaymentModes, setTuitionPaymentModes, - handleCreate, - handleEdit, + handleCreate, + handleEdit, handleDelete }) => { const handleDiscountDelete = (id, type) => { if (type === 0) { - setRegistrationFees(prevFees => + setRegistrationFees(prevFees => prevFees.map(fee => ({ ...fee, discounts: fee.discounts.filter(discountId => discountId !== id) })) ); } else { - setTuitionFees(prevFees => + setTuitionFees(prevFees => prevFees.map(fee => ({ ...fee, discounts: fee.discounts.filter(discountId => discountId !== id) @@ -47,7 +47,7 @@ const FeesManagement = ({ registrationDiscounts, return (
    -

    Frais d'inscription

    +

    Frais d'inscription

    { .catch(error => { console.error('Error fetching CSRF token:', error); }); - }, []); + }, [token]); return token; };