diff --git a/Back-End/Auth/migrations/0001_initial.py b/Back-End/Auth/migrations/0001_initial.py index c7d826b..38f75fe 100644 --- a/Back-End/Auth/migrations/0001_initial.py +++ b/Back-End/Auth/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.contrib.auth.models import django.contrib.auth.validators diff --git a/Back-End/Common/migrations/0001_initial.py b/Back-End/Common/migrations/0001_initial.py index db9180b..6070e9f 100644 --- a/Back-End/Common/migrations/0001_initial.py +++ b/Back-End/Common/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.db.models.deletion from django.db import migrations, models diff --git a/Back-End/Establishment/migrations/0001_initial.py b/Back-End/Establishment/migrations/0001_initial.py index d658067..e171153 100644 --- a/Back-End/Establishment/migrations/0001_initial.py +++ b/Back-End/Establishment/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import Establishment.models import django.contrib.postgres.fields diff --git a/Back-End/GestionMessagerie/migrations/0001_initial.py b/Back-End/GestionMessagerie/migrations/0001_initial.py index 9f4045b..8d92423 100644 --- a/Back-End/GestionMessagerie/migrations/0001_initial.py +++ b/Back-End/GestionMessagerie/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.db.models.deletion import django.utils.timezone diff --git a/Back-End/GestionNotification/migrations/0001_initial.py b/Back-End/GestionNotification/migrations/0001_initial.py index e3f2d8f..8198a27 100644 --- a/Back-End/GestionNotification/migrations/0001_initial.py +++ b/Back-End/GestionNotification/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.db.models.deletion from django.conf import settings diff --git a/Back-End/Planning/migrations/0001_initial.py b/Back-End/Planning/migrations/0001_initial.py index 66d1bd8..7b95e54 100644 --- a/Back-End/Planning/migrations/0001_initial.py +++ b/Back-End/Planning/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.db.models.deletion from django.db import migrations, models diff --git a/Back-End/School/migrations/0001_initial.py b/Back-End/School/migrations/0001_initial.py index 346bd41..5b5aa28 100644 --- a/Back-End/School/migrations/0001_initial.py +++ b/Back-End/School/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.contrib.postgres.fields import django.db.models.deletion @@ -124,6 +124,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=100)), ('updated_date', models.DateTimeField(auto_now=True)), ('color_code', models.CharField(default='#FFFFFF', max_length=7)), + ('school_year', models.CharField(blank=True, max_length=9)), ('establishment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='specialities', to='Establishment.establishment')), ], ), @@ -153,6 +154,7 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('last_name', models.CharField(max_length=100)), ('first_name', models.CharField(max_length=100)), + ('school_year', models.CharField(blank=True, max_length=9)), ('updated_date', models.DateTimeField(auto_now=True)), ('profile_role', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teacher_profile', to='Auth.profilerole')), ('specialities', models.ManyToManyField(blank=True, to='School.speciality')), diff --git a/Back-End/Settings/migrations/0001_initial.py b/Back-End/Settings/migrations/0001_initial.py index a4dde10..dfb4083 100644 --- a/Back-End/Settings/migrations/0001_initial.py +++ b/Back-End/Settings/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import django.db.models.deletion from django.db import migrations, models diff --git a/Back-End/Subscriptions/migrations/0001_initial.py b/Back-End/Subscriptions/migrations/0001_initial.py index 51cb628..5e846df 100644 --- a/Back-End/Subscriptions/migrations/0001_initial.py +++ b/Back-End/Subscriptions/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2026-04-04 09:15 +# Generated by Django 5.1.3 on 2026-04-05 08:05 import Subscriptions.models import django.db.models.deletion @@ -53,7 +53,7 @@ class Migration(migrations.Migration): ('name', models.CharField(default='', max_length=255)), ('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_school_file_upload_to)), ('formTemplateData', models.JSONField(blank=True, default=list, null=True)), - ('isValidated', models.BooleanField(default=False)), + ('isValidated', models.BooleanField(blank=True, default=None, null=True)), ], ), migrations.CreateModel( @@ -200,7 +200,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('file', models.FileField(blank=True, null=True, upload_to=Subscriptions.models.registration_parent_file_upload_to)), - ('isValidated', models.BooleanField(default=False)), + ('isValidated', models.BooleanField(blank=True, default=None, null=True)), ('master', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_file_templates', to='Subscriptions.registrationparentfilemaster')), ('registration_form', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_file_templates', to='Subscriptions.registrationform')), ], diff --git a/Front-End/src/app/500.js b/Front-End/src/app/500.js index 17758f0..3323f74 100644 --- a/Front-End/src/app/500.js +++ b/Front-End/src/app/500.js @@ -3,16 +3,19 @@ import Logo from '../components/Logo'; export default function Custom500() { return ( -
-
+
+
-

+

500 | Erreur interne

-

+

Une erreur interne est survenue.

- + Retour Accueil
diff --git a/Front-End/src/app/[locale]/admin/directory/page.js b/Front-End/src/app/[locale]/admin/directory/page.js index e052b0e..9b59fa8 100644 --- a/Front-End/src/app/[locale]/admin/directory/page.js +++ b/Front-End/src/app/[locale]/admin/directory/page.js @@ -2,8 +2,17 @@ import React, { useState, useEffect } from 'react'; import { useEstablishment } from '@/context/EstablishmentContext'; import { PARENT_FILTER, SCHOOL_FILTER } from '@/utils/constants'; -import { Trash2, ToggleLeft, ToggleRight, Info, XCircle } from 'lucide-react'; +import { + Trash2, + ToggleLeft, + ToggleRight, + Info, + XCircle, + Users, + UserPlus, +} from 'lucide-react'; import Table from '@/components/Table'; +import EmptyState from '@/components/EmptyState'; import Popup from '@/components/Popup'; import StatusLabel from '@/components/StatusLabel'; import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem'; @@ -17,7 +26,6 @@ import { dissociateGuardian } from '@/app/actions/subscriptionAction'; import { useCsrfToken } from '@/context/CsrfContext'; import DjangoCSRFToken from '@/components/DjangoCSRFToken'; import logger from '@/utils/logger'; -import AlertMessage from '@/components/AlertMessage'; const roleTypeToLabel = (roleType) => { switch (roleType) { @@ -39,7 +47,7 @@ const roleTypeToBadgeClass = (roleType) => { case 1: return 'bg-red-100 text-red-600'; case 2: - return 'bg-green-100 text-green-600'; + return 'bg-tertiary/10 text-tertiary'; default: return 'bg-gray-100 text-gray-600'; } @@ -378,7 +386,7 @@ export default function Page() { type="button" className={ row.is_active - ? 'text-emerald-500 hover:text-emerald-700' + ? 'text-primary hover:text-secondary' : 'text-orange-500 hover:text-orange-700' } onClick={() => handleConfirmActivateProfile(row)} @@ -474,7 +482,7 @@ export default function Page() { type="button" className={ row.is_active - ? 'text-emerald-500 hover:text-emerald-700' + ? 'text-primary hover:text-secondary' : 'text-orange-500 hover:text-orange-700' } onClick={() => handleConfirmActivateProfile(row)} @@ -516,10 +524,10 @@ export default function Page() { totalPages={totalProfilesParentPages} onPageChange={handlePageChange} emptyMessage={ - } /> @@ -540,10 +548,10 @@ export default function Page() { totalPages={totalProfilesSchoolPages} onPageChange={handlePageChange} emptyMessage={ - } /> diff --git a/Front-End/src/app/[locale]/admin/feedback/page.js b/Front-End/src/app/[locale]/admin/feedback/page.js index 605f167..4709ee0 100644 --- a/Front-End/src/app/[locale]/admin/feedback/page.js +++ b/Front-End/src/app/[locale]/admin/feedback/page.js @@ -82,12 +82,12 @@ export default function FeedbackPage() { return (
-

+

{t('title')}

{t('description')}

-
+
{/* Catégorie */} -

Suivi pédagogique

+

Suivi pédagogique

{/* Student profile */} {student && ( -
+
{student.photo ? ( {`${student.first_name} ) : ( -
+
{student.first_name?.[0]} {student.last_name?.[0]}
)}
-
+
{student.last_name} {student.first_name}
@@ -322,7 +322,7 @@ export default function StudentGradesPage() { `${FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL}?studentId=${studentId}&period=${periodString}` ); }} - className="px-4 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600 w-full sm:w-auto" + className="px-4 py-2 rounded shadow bg-primary text-white font-label font-medium hover:bg-secondary w-full sm:w-auto min-h-[44px] transition-colors" icon={} text="Évaluer" title="Évaluer l'élève" @@ -351,10 +351,10 @@ export default function StudentGradesPage() {
{/* Évaluations par matière */} -
+
- -

+ +

Évaluations par matière

diff --git a/Front-End/src/app/[locale]/admin/grades/page.js b/Front-End/src/app/[locale]/admin/grades/page.js index ad6eb1e..a81ca5f 100644 --- a/Front-End/src/app/[locale]/admin/grades/page.js +++ b/Front-End/src/app/[locale]/admin/grades/page.js @@ -12,13 +12,17 @@ import { Save, Download, FileText, + UserPlus, + Users, } from 'lucide-react'; import SectionHeader from '@/components/SectionHeader'; import Table from '@/components/Table'; +import EmptyState from '@/components/EmptyState'; import logger from '@/utils/logger'; import { FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL, FE_ADMIN_STRUCTURE_SCHOOLCLASS_MANAGEMENT_URL, + FE_ADMIN_SUBSCRIPTIONS_CREATE_URL, } from '@/utils/Url'; import { getSecureFileUrl } from '@/utils/fileUrl'; import { @@ -36,15 +40,25 @@ import { useClasses } from '@/context/ClassesContext'; import { useCsrfToken } from '@/context/CsrfContext'; import { exportToCSV } from '@/utils/exportCSV'; import SchoolYearFilter from '@/components/SchoolYearFilter'; -import { getCurrentSchoolYear, getNextSchoolYear, getHistoricalYears } from '@/utils/Date'; -import { CURRENT_YEAR_FILTER, NEXT_YEAR_FILTER, HISTORICAL_FILTER } from '@/utils/constants'; +import { + getCurrentSchoolYear, + getNextSchoolYear, + getHistoricalYears, +} from '@/utils/Date'; +import { + CURRENT_YEAR_FILTER, + NEXT_YEAR_FILTER, + HISTORICAL_FILTER, +} from '@/utils/constants'; import dayjs from 'dayjs'; function getPeriodString(periodValue, frequency, schoolYear = null) { - const year = schoolYear || (() => { - const y = dayjs().month() >= 8 ? dayjs().year() : dayjs().year() - 1; - return `${y}-${y + 1}`; - })(); + const year = + schoolYear || + (() => { + const y = dayjs().month() >= 8 ? dayjs().year() : dayjs().year() - 1; + return `${y}-${y + 1}`; + })(); if (frequency === 1) return `T${periodValue}_${year}`; if (frequency === 2) return `S${periodValue}_${year}`; if (frequency === 3) return `A_${year}`; @@ -97,7 +111,7 @@ const COMPETENCY_COLUMNS = [ { key: 'acquired', label: 'Acquises', - color: 'bg-emerald-100 text-emerald-700', + color: 'bg-primary/10 text-secondary', }, { key: 'inProgress', @@ -145,7 +159,7 @@ function PercentBadge({ value, loading, color }) { const badgeColor = color || (value >= 75 - ? 'bg-emerald-100 text-emerald-700' + ? 'bg-primary/10 text-secondary' : value >= 50 ? 'bg-yellow-100 text-yellow-700' : 'bg-red-100 text-red-600'); @@ -177,13 +191,13 @@ export default function Page() { const [editingEvalId, setEditingEvalId] = useState(null); const [editScore, setEditScore] = useState(''); const [editAbsent, setEditAbsent] = useState(false); - + // Filtrage par année scolaire const [activeYearFilter, setActiveYearFilter] = useState(CURRENT_YEAR_FILTER); const currentSchoolYear = useMemo(() => getCurrentSchoolYear(), []); const nextSchoolYear = useMemo(() => getNextSchoolYear(), []); const historicalYears = useMemo(() => getHistoricalYears(5), []); - + // Déterminer l'année scolaire sélectionnée const selectedSchoolYear = useMemo(() => { if (activeYearFilter === CURRENT_YEAR_FILTER) return currentSchoolYear; @@ -193,10 +207,11 @@ export default function Page() { return historicalYears[0]; }, [activeYearFilter, currentSchoolYear, nextSchoolYear, historicalYears]); - const periodColumns = useMemo(() => getPeriodColumns( - selectedEstablishmentEvaluationFrequency - ), [selectedEstablishmentEvaluationFrequency]); - + const periodColumns = useMemo( + () => getPeriodColumns(selectedEstablishmentEvaluationFrequency), + [selectedEstablishmentEvaluationFrequency] + ); + const currentPeriodValue = getCurrentPeriodValue( selectedEstablishmentEvaluationFrequency ); @@ -229,7 +244,11 @@ export default function Page() { const tasks = students.flatMap((student) => periodColumns.map(({ value: periodValue }) => { - const periodStr = getPeriodString(periodValue, frequency, selectedSchoolYear); + const periodStr = getPeriodString( + periodValue, + frequency, + selectedSchoolYear + ); return fetchStudentCompetencies(student.id, periodStr) .then((data) => ({ studentId: student.id, periodValue, data })) .catch(() => ({ studentId: student.id, periodValue, data: null })); @@ -281,7 +300,12 @@ export default function Page() { setStatsMap(map); setStatsLoading(false); }); - }, [students, selectedEstablishmentEvaluationFrequency, selectedSchoolYear, periodColumns]); + }, [ + students, + selectedEstablishmentEvaluationFrequency, + selectedSchoolYear, + periodColumns, + ]); const filteredStudents = students.filter( (student) => @@ -330,12 +354,16 @@ export default function Page() { { key: 'last_name', label: 'Nom' }, { key: 'first_name', label: 'Prénom' }, { key: 'birth_date', label: 'Date de naissance' }, - { key: 'level', label: 'Niveau', transform: (value) => getNiveauLabel(value) }, + { + key: 'level', + label: 'Niveau', + transform: (value) => getNiveauLabel(value), + }, { key: 'associated_class_name', label: 'Classe' }, - { - key: 'id', + { + key: 'id', label: 'Absences', - transform: (value) => absencesMap[value] || 0 + transform: (value) => absencesMap[value] || 0, }, ]; @@ -347,7 +375,7 @@ export default function Page() { transform: (value) => { const stats = statsMap[value]; return stats?.[key] !== undefined ? `${stats[key]}%` : ''; - } + }, }); }); @@ -526,7 +554,7 @@ export default function Page() { `${FE_ADMIN_STRUCTURE_SCHOOLCLASS_MANAGEMENT_URL}?schoolClassId=${student.associated_class_id}` ); }} - className="text-emerald-700 hover:underline font-medium" + className="text-secondary hover:underline font-medium" > {student.associated_class_name} @@ -581,7 +609,7 @@ export default function Page() {
-

Bilan de compétence

+

Bilan de compétence

+
{children}
diff --git a/Front-End/src/app/[locale]/admin/page.js b/Front-End/src/app/[locale]/admin/page.js index 12a12b9..5a7767f 100644 --- a/Front-End/src/app/[locale]/admin/page.js +++ b/Front-End/src/app/[locale]/admin/page.js @@ -174,14 +174,14 @@ export default function DashboardPage() { } - color="green" + icon={} + color="tertiary" /> } - color="emerald" + icon={} + color="primary" /> {/* Graphique des inscriptions */} -
-

+
+

{t('inscriptionTrends')}

@@ -214,14 +214,14 @@ export default function DashboardPage() {
{/* Présence et assiduité */} -
+
{/* Colonne de droite : Événements à venir */} -
-

{t('upcomingEvents')}

+
+

{t('upcomingEvents')}

{upcomingEvents.map((event, index) => ( ))} diff --git a/Front-End/src/app/[locale]/admin/settings/page.js b/Front-End/src/app/[locale]/admin/settings/page.js index ac50dfa..d721666 100644 --- a/Front-End/src/app/[locale]/admin/settings/page.js +++ b/Front-End/src/app/[locale]/admin/settings/page.js @@ -1,7 +1,5 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import Tab from '@/components/Tab'; -import TabContent from '@/components/TabContent'; import Button from '@/components/Form/Button'; import InputText from '@/components/Form/InputText'; import CheckBox from '@/components/Form/CheckBox'; // Import du composant CheckBox @@ -13,13 +11,8 @@ import { import { useEstablishment } from '@/context/EstablishmentContext'; import { useCsrfToken } from '@/context/CsrfContext'; // Import du hook pour récupérer le csrfToken import { useNotification } from '@/context/NotificationContext'; -import { useSearchParams } from 'next/navigation'; // Ajoute cet import export default function SettingsPage() { - const [activeTab, setActiveTab] = useState('smtp'); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [confirmPassword, setConfirmPassword] = useState(''); const [smtpServer, setSmtpServer] = useState(''); const [smtpPort, setSmtpPort] = useState(''); const [smtpUser, setSmtpUser] = useState(''); @@ -29,23 +22,10 @@ export default function SettingsPage() { const { selectedEstablishmentId } = useEstablishment(); const csrfToken = useCsrfToken(); // Récupération du csrfToken const { showNotification } = useNotification(); - const searchParams = useSearchParams(); - - const handleTabClick = (tab) => { - setActiveTab(tab); - }; - - // Ajout : sélection automatique de l'onglet via l'ancre ou le paramètre de recherche - useEffect(() => { - const tabParam = searchParams.get('tab'); - if (tabParam === 'smtp') { - setActiveTab('smtp'); - } - }, [searchParams]); // Charger les paramètres SMTP existants useEffect(() => { - if (activeTab === 'smtp') { + if (csrfToken && selectedEstablishmentId) { fetchSmtpSettings(csrfToken, selectedEstablishmentId) // Passer le csrfToken ici .then((data) => { setSmtpServer(data.smtp_server || ''); @@ -75,7 +55,7 @@ export default function SettingsPage() { } }); } - }, [activeTab, csrfToken]); // Ajouter csrfToken comme dépendance + }, [csrfToken, selectedEstablishmentId]); const handleSmtpServerChange = (e) => { setSmtpServer(e.target.value); @@ -128,66 +108,63 @@ export default function SettingsPage() { }; return ( -
-
- handleTabClick('smtp')} - /> -
-
- - -
- +

+ Paramètres +

+
+

+ Paramètres SMTP +

+ +
+ + + + +
+
+
+ setUseTls((prev) => !prev)} // Inverser la valeur booléenne + fieldName="useTls" + itemLabelFunc={() => 'Utiliser TLS'} /> - - - setUseSsl((prev) => !prev)} // Inverser la valeur booléenne + fieldName="useSsl" + itemLabelFunc={() => 'Utiliser SSL'} />
-
-
- setUseTls((prev) => !prev)} // Inverser la valeur booléenne - fieldName="useTls" - itemLabelFunc={() => 'Utiliser TLS'} - /> - setUseSsl((prev) => !prev)} // Inverser la valeur booléenne - fieldName="useSsl" - itemLabelFunc={() => 'Utiliser SSL'} - /> -
-
- - - +
+ +
); diff --git a/Front-End/src/app/[locale]/admin/structure/FormBuilder/page.js b/Front-End/src/app/[locale]/admin/structure/FormBuilder/page.js index 77ab593..0ca67f9 100644 --- a/Front-End/src/app/[locale]/admin/structure/FormBuilder/page.js +++ b/Front-End/src/app/[locale]/admin/structure/FormBuilder/page.js @@ -204,7 +204,7 @@ export default function FormBuilderPage() { Retour -

+

{isEditing ? 'Modifier le formulaire' : 'Créer un formulaire personnalisé'}

diff --git a/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js b/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js index 43ef6fa..0ff8875 100644 --- a/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js +++ b/Front-End/src/app/[locale]/admin/structure/SchoolClassManagement/page.js @@ -1,10 +1,28 @@ 'use client'; import React, { useState, useEffect } from 'react'; -import { Users, Layers, CheckCircle, Clock, XCircle, ClipboardList, Plus } from 'lucide-react'; +import { + Users, + Layers, + CheckCircle, + Clock, + XCircle, + ClipboardList, + Plus, +} from 'lucide-react'; import Table from '@/components/Table'; import Popup from '@/components/Popup'; -import { fetchClasse, fetchSpecialities, fetchEvaluations, createEvaluation, updateEvaluation, deleteEvaluation, fetchStudentEvaluations, saveStudentEvaluations, deleteStudentEvaluation } from '@/app/actions/schoolAction'; +import { + fetchClasse, + fetchSpecialities, + fetchEvaluations, + createEvaluation, + updateEvaluation, + deleteEvaluation, + fetchStudentEvaluations, + saveStudentEvaluations, + deleteStudentEvaluation, +} from '@/app/actions/schoolAction'; import { useSearchParams } from 'next/navigation'; import logger from '@/utils/logger'; import { useClasses } from '@/context/ClassesContext'; @@ -17,7 +35,11 @@ import { editAbsences, deleteAbsences, } from '@/app/actions/subscriptionAction'; -import { EvaluationForm, EvaluationList, EvaluationGradeTable } from '@/components/Evaluation'; +import { + EvaluationForm, + EvaluationList, + EvaluationGradeTable, +} from '@/components/Evaluation'; import { useCsrfToken } from '@/context/CsrfContext'; import { useEstablishment } from '@/context/EstablishmentContext'; @@ -53,14 +75,15 @@ export default function Page() { const [editingEvaluation, setEditingEvaluation] = useState(null); const csrfToken = useCsrfToken(); - const { selectedEstablishmentId, selectedEstablishmentEvaluationFrequency } = useEstablishment(); + const { selectedEstablishmentId, selectedEstablishmentEvaluationFrequency } = + useEstablishment(); // Périodes selon la fréquence d'évaluation const getPeriods = () => { const year = dayjs().month() >= 8 ? dayjs().year() : dayjs().year() - 1; const nextYear = (year + 1).toString(); const schoolYear = `${year}-${nextYear}`; - + if (selectedEstablishmentEvaluationFrequency === 1) { return [ { label: 'Trimestre 1', value: `T1_${schoolYear}` }, @@ -212,16 +235,25 @@ export default function Page() { const currentSchoolYear = `${year}-${year + 1}`; fetchSpecialities(selectedEstablishmentId, currentSchoolYear) .then((data) => setSpecialities(data)) - .catch((error) => logger.error('Erreur lors du chargement des matières:', error)); + .catch((error) => + logger.error('Erreur lors du chargement des matières:', error) + ); } }, [selectedEstablishmentId]); // Load evaluations when tab is active and period is selected useEffect(() => { - if (activeTab === 'evaluations' && selectedEstablishmentId && schoolClassId && selectedPeriod) { + if ( + activeTab === 'evaluations' && + selectedEstablishmentId && + schoolClassId && + selectedPeriod + ) { fetchEvaluations(selectedEstablishmentId, schoolClassId, selectedPeriod) .then((data) => setEvaluations(data)) - .catch((error) => logger.error('Erreur lors du chargement des évaluations:', error)); + .catch((error) => + logger.error('Erreur lors du chargement des évaluations:', error) + ); } }, [activeTab, selectedEstablishmentId, schoolClassId, selectedPeriod]); @@ -230,7 +262,9 @@ export default function Page() { if (selectedEvaluation && schoolClassId) { fetchStudentEvaluations(null, selectedEvaluation.id, null, schoolClassId) .then((data) => setStudentEvaluations(data)) - .catch((error) => logger.error('Erreur lors du chargement des notes:', error)); + .catch((error) => + logger.error('Erreur lors du chargement des notes:', error) + ); } }, [selectedEvaluation, schoolClassId]); @@ -241,7 +275,11 @@ export default function Page() { showNotification('Évaluation créée avec succès', 'success', 'Succès'); setShowEvaluationForm(false); // Reload evaluations - const updatedEvaluations = await fetchEvaluations(selectedEstablishmentId, schoolClassId, selectedPeriod); + const updatedEvaluations = await fetchEvaluations( + selectedEstablishmentId, + schoolClassId, + selectedPeriod + ); setEvaluations(updatedEvaluations); } catch (error) { logger.error('Erreur lors de la création:', error); @@ -261,7 +299,11 @@ export default function Page() { setShowEvaluationForm(false); setEditingEvaluation(null); // Reload evaluations - const updatedEvaluations = await fetchEvaluations(selectedEstablishmentId, schoolClassId, selectedPeriod); + const updatedEvaluations = await fetchEvaluations( + selectedEstablishmentId, + schoolClassId, + selectedPeriod + ); setEvaluations(updatedEvaluations); } catch (error) { logger.error('Erreur lors de la modification:', error); @@ -272,20 +314,31 @@ export default function Page() { const handleDeleteEvaluation = async (evaluationId) => { await deleteEvaluation(evaluationId, csrfToken); // Reload evaluations - const updatedEvaluations = await fetchEvaluations(selectedEstablishmentId, schoolClassId, selectedPeriod); + const updatedEvaluations = await fetchEvaluations( + selectedEstablishmentId, + schoolClassId, + selectedPeriod + ); setEvaluations(updatedEvaluations); }; const handleSaveGrades = async (gradesData) => { await saveStudentEvaluations(gradesData, csrfToken); // Reload student evaluations - const updatedStudentEvaluations = await fetchStudentEvaluations(null, selectedEvaluation.id, null, schoolClassId); + const updatedStudentEvaluations = await fetchStudentEvaluations( + null, + selectedEvaluation.id, + null, + schoolClassId + ); setStudentEvaluations(updatedStudentEvaluations); }; const handleDeleteGrade = async (studentEvalId) => { await deleteStudentEvaluation(studentEvalId, csrfToken); - setStudentEvaluations((prev) => prev.filter((se) => se.id !== studentEvalId)); + setStudentEvaluations((prev) => + prev.filter((se) => se.id !== studentEvalId) + ); }; const handleLevelClick = (label) => { @@ -543,14 +596,16 @@ export default function Page() { return (
-

{classe?.atmosphere_name}

+

+ {classe?.atmosphere_name} +

{/* Section Niveaux et Enseignants */}
{/* Section Niveaux */} -
-

- +
+

+ Niveaux

@@ -559,24 +614,24 @@ export default function Page() {

{classe?.levels?.length > 0 ? ( getNiveauxLabels(classe.levels).map((label, index) => ( - handleLevelClick(label)} // Gérer le clic sur un niveau - className={`px-4 py-2 rounded-full cursor-pointer border transition-all duration-200 ${ + onClick={() => handleLevelClick(label)} + className={`px-4 py-2 rounded font-label font-medium cursor-pointer border transition-colors min-h-[44px] ${ selectedLevels.includes(label) - ? 'bg-emerald-200 text-emerald-800 border-emerald-300 shadow-md' + ? 'bg-primary/20 text-secondary border-primary/30 shadow-sm' : 'bg-gray-200 text-gray-800 border-gray-300 hover:bg-gray-300' }`} > {selectedLevels.includes(label) ? ( - + {label} ) : ( label )} - + )) ) : ( Aucun niveau associé @@ -585,9 +640,9 @@ export default function Page() {
{/* Section Enseignants */} -
-

- +
+

+ Enseignants

Liste des enseignants

@@ -595,7 +650,7 @@ export default function Page() { {classe?.teachers_details?.map((teacher) => ( {teacher.last_name} {teacher.first_name} @@ -605,14 +660,14 @@ export default function Page() {
{/* Tabs Navigation */} -
+
@@ -681,218 +736,224 @@ export default function Page() { name: 'Prénom', transform: (row) => (
{row.first_name}
- ), - }, - { - name: 'Niveau', - transform: (row) => ( -
{getNiveauLabel(row.level)}
- ), - }, - ...(isEditingAttendance - ? [ - { - name: "Gestion de l'appel", - transform: (row) => ( -
- {/* Présence */} -
- {attendance[row.id] ? ( - <> - - handleAttendanceChange(row.id) - } - fieldName="attendance" - /> - - Présent - - - ) : ( - <> - {/* Icône croix pour remettre l'élève en présent */} - - - Effacer l'absence - - - )} -
- - {/* Détails absence/retard */} - {!attendance[row.id] && ( -
-
- - - Motif d'absence - + ), + }, + { + name: 'Niveau', + transform: (row) => ( +
{getNiveauLabel(row.level)}
+ ), + }, + ...(isEditingAttendance + ? [ + { + name: "Gestion de l'appel", + transform: (row) => ( +
+ {/* Présence */} +
+ {attendance[row.id] ? ( + <> + + handleAttendanceChange(row.id) + } + fieldName="attendance" + /> + + Présent + + + ) : ( + <> + {/* Icône croix pour remettre l'élève en présent */} + + + Effacer l'absence + + + )}
-
- {/* Select Absence/Retard */} - - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - type: e.target.value, - }, - })) - } - choices={[ - { value: 'absence', label: 'Absence' }, - { value: 'retard', label: 'Retard' }, - ]} - /> - {/* Select Moment */} - - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - moment: parseInt(e.target.value, 10), - }, - })) - } - choices={Object.values(AbsenceMoment).map( - (moment) => ({ - value: moment.value, - label: moment.label, - }) - )} - /> + {/* Détails absence/retard */} + {!attendance[row.id] && ( +
+
+ + + Motif d'absence + +
+
+ {/* Select Absence/Retard */} + + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + type: e.target.value, + }, + })) + } + choices={[ + { value: 'absence', label: 'Absence' }, + { value: 'retard', label: 'Retard' }, + ]} + /> - {/* Nouveau champ commentaire */} - - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - commentaire: e.target.value, - }, - })) - } - /> + {/* Select Moment */} + + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + moment: parseInt(e.target.value, 10), + }, + })) + } + choices={Object.values(AbsenceMoment).map( + (moment) => ({ + value: moment.value, + label: moment.label, + }) + )} + /> - {/* Checkbox Justifié */} -
- - setFormAbsences((prev) => ({ - ...prev, - [row.id]: { - ...prev[row.id], - justified: !prev[row.id]?.justified, - }, - })) - } - fieldName="justified" - itemLabelFunc={() => 'Justifié'} - /> + {/* Nouveau champ commentaire */} + + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + commentaire: e.target.value, + }, + })) + } + /> + + {/* Checkbox Justifié */} +
+ + setFormAbsences((prev) => ({ + ...prev, + [row.id]: { + ...prev[row.id], + justified: !prev[row.id]?.justified, + }, + })) + } + fieldName="justified" + itemLabelFunc={() => 'Justifié'} + /> +
+
-
+ )}
- )} -
- ), - }, - ] - : [ - { - name: 'Statut', - transform: (row) => { - const today = new Date().toISOString().split('T')[0]; - const absence = - formAbsences[row.id] || - Object.values(fetchedAbsences).find( - (absence) => - absence.student === row.id && absence.day === today - ); + ), + }, + ] + : [ + { + name: 'Statut', + transform: (row) => { + const today = new Date().toISOString().split('T')[0]; + const absence = + formAbsences[row.id] || + Object.values(fetchedAbsences).find( + (absence) => + absence.student === row.id && + absence.day === today + ); - if (!absence) { - return ( -
- - Présent -
- ); - } + if (!absence) { + return ( +
+ + Présent +
+ ); + } - switch (absence.reason) { - case AbsenceReason.JUSTIFIED_LATE.value: - return ( -
- - Retard justifié -
- ); - case AbsenceReason.UNJUSTIFIED_LATE.value: - return ( -
- - Retard non justifié -
- ); - case AbsenceReason.JUSTIFIED_ABSENCE.value: - return ( -
- - Absence justifiée -
- ); - case AbsenceReason.UNJUSTIFIED_ABSENCE.value: - return ( -
- - Absence non justifiée -
- ); - default: - return ( -
- - Statut inconnu -
- ); - } - }, - }, - ]), - ]} - data={filteredStudents} // Utiliser les élèves filtrés - /> + switch (absence.reason) { + case AbsenceReason.JUSTIFIED_LATE.value: + return ( +
+ + Retard justifié +
+ ); + case AbsenceReason.UNJUSTIFIED_LATE.value: + return ( +
+ + Retard non justifié +
+ ); + case AbsenceReason.JUSTIFIED_ABSENCE.value: + return ( +
+ + Absence justifiée +
+ ); + case AbsenceReason.UNJUSTIFIED_ABSENCE.value: + return ( +
+ + Absence non justifiée +
+ ); + default: + return ( +
+ + Statut inconnu +
+ ); + } + }, + }, + ]), + ]} + data={filteredStudents} // Utiliser les élèves filtrés + /> )} @@ -900,11 +961,11 @@ export default function Page() { {activeTab === 'evaluations' && (
{/* Header avec sélecteur de période et bouton d'ajout */} -
+
- -

+ +

Évaluations de la classe

@@ -936,7 +997,11 @@ export default function Page() { schoolClassId={parseInt(schoolClassId)} establishmentId={selectedEstablishmentId} initialValues={editingEvaluation} - onSubmit={editingEvaluation ? handleUpdateEvaluation : handleCreateEvaluation} + onSubmit={ + editingEvaluation + ? handleUpdateEvaluation + : handleCreateEvaluation + } onCancel={() => { setShowEvaluationForm(false); setEditingEvaluation(null); @@ -945,12 +1010,14 @@ export default function Page() { )} {/* Liste des évaluations */} -
+
setSelectedEvaluation(evaluation)} + onGradeStudents={(evaluation) => + setSelectedEvaluation(evaluation) + } />
diff --git a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js index 5afee0a..8973189 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/createSubscription/page.js @@ -746,11 +746,11 @@ export default function CreateSubscriptionPage() { return (
{registerFormID ? ( -

+

Modifier un dossier d'inscription

) : ( -

+

Créer un dossier d'inscription

)} @@ -953,7 +953,7 @@ export default function CreateSubscriptionPage() { }} rowClassName={(row) => selectedStudent && selectedStudent.id === row.id - ? 'bg-emerald-600 text-white' + ? 'bg-primary text-white' : '' } selectedRows={selectedStudent ? [selectedStudent.id] : []} // Assurez-vous que selectedRows est un tableau @@ -965,7 +965,7 @@ export default function CreateSubscriptionPage() { {selectedStudent && (
-

+

Responsables associés à {selectedStudent.last_name}{' '} {selectedStudent.first_name} :

@@ -1026,7 +1026,7 @@ export default function CreateSubscriptionPage() {
{/* Montant total */} -
+
Montant total des frais d'inscription : @@ -1073,7 +1073,7 @@ export default function CreateSubscriptionPage() {
{/* Montant total */} -
+
Montant total des frais de scolarité : @@ -1136,7 +1136,7 @@ export default function CreateSubscriptionPage() { className={`px-6 py-2 rounded-md shadow ${ isSubmitDisabled() ? 'bg-gray-300 text-gray-500 cursor-not-allowed' - : 'bg-emerald-500 text-white hover:bg-emerald-600' + : 'bg-primary text-white hover:bg-primary' }`} primary disabled={isSubmitDisabled()} diff --git a/Front-End/src/app/[locale]/admin/subscriptions/page.js b/Front-End/src/app/[locale]/admin/subscriptions/page.js index d69931a..64d26d1 100644 --- a/Front-End/src/app/[locale]/admin/subscriptions/page.js +++ b/Front-End/src/app/[locale]/admin/subscriptions/page.js @@ -1,7 +1,7 @@ 'use client'; import React, { useState, useEffect } from 'react'; import Table from '@/components/Table'; -import Tab from '@/components/Tab'; +import SidebarTabs from '@/components/SidebarTabs'; import Textarea from '@/components/Textarea'; import { useTranslations } from 'next-intl'; import StatusLabel from '@/components/StatusLabel'; @@ -55,6 +55,7 @@ import { HISTORICAL_FILTER, } from '@/utils/constants'; import AlertMessage from '@/components/AlertMessage'; +import EmptyState from '@/components/EmptyState'; import { useNotification } from '@/context/NotificationContext'; import { exportToCSV } from '@/utils/exportCSV'; @@ -167,43 +168,86 @@ export default function Page({ params: { locale } }) { // Export CSV const handleExportCSV = () => { - const dataToExport = activeTab === CURRENT_YEAR_FILTER - ? registrationFormsDataCurrentYear - : activeTab === NEXT_YEAR_FILTER - ? registrationFormsDataNextYear - : registrationFormsDataHistorical; + const dataToExport = + activeTab === CURRENT_YEAR_FILTER + ? registrationFormsDataCurrentYear + : activeTab === NEXT_YEAR_FILTER + ? registrationFormsDataNextYear + : registrationFormsDataHistorical; const exportColumns = [ - { key: 'student', label: 'Nom', transform: (value) => value?.last_name || '' }, - { key: 'student', label: 'Prénom', transform: (value) => value?.first_name || '' }, - { key: 'student', label: 'Date de naissance', transform: (value) => value?.birth_date || '' }, - { key: 'student', label: 'Email contact', transform: (value) => value?.guardians?.[0]?.associated_profile_email || '' }, - { key: 'student', label: 'Téléphone contact', transform: (value) => value?.guardians?.[0]?.phone || '' }, - { key: 'student', label: 'Nom responsable 1', transform: (value) => value?.guardians?.[0]?.last_name || '' }, - { key: 'student', label: 'Prénom responsable 1', transform: (value) => value?.guardians?.[0]?.first_name || '' }, - { key: 'student', label: 'Nom responsable 2', transform: (value) => value?.guardians?.[1]?.last_name || '' }, - { key: 'student', label: 'Prénom responsable 2', transform: (value) => value?.guardians?.[1]?.first_name || '' }, + { + key: 'student', + label: 'Nom', + transform: (value) => value?.last_name || '', + }, + { + key: 'student', + label: 'Prénom', + transform: (value) => value?.first_name || '', + }, + { + key: 'student', + label: 'Date de naissance', + transform: (value) => value?.birth_date || '', + }, + { + key: 'student', + label: 'Email contact', + transform: (value) => + value?.guardians?.[0]?.associated_profile_email || '', + }, + { + key: 'student', + label: 'Téléphone contact', + transform: (value) => value?.guardians?.[0]?.phone || '', + }, + { + key: 'student', + label: 'Nom responsable 1', + transform: (value) => value?.guardians?.[0]?.last_name || '', + }, + { + key: 'student', + label: 'Prénom responsable 1', + transform: (value) => value?.guardians?.[0]?.first_name || '', + }, + { + key: 'student', + label: 'Nom responsable 2', + transform: (value) => value?.guardians?.[1]?.last_name || '', + }, + { + key: 'student', + label: 'Prénom responsable 2', + transform: (value) => value?.guardians?.[1]?.first_name || '', + }, { key: 'school_year', label: 'Année scolaire' }, - { key: 'status', label: 'Statut', transform: (value) => { - const statusMap = { - 0: 'En attente', - 1: 'En cours', - 2: 'Envoyé', - 3: 'À relancer', - 4: 'À valider', - 5: 'Validé', - 6: 'Archivé', - }; - return statusMap[value] || value; - }}, + { + key: 'status', + label: 'Statut', + transform: (value) => { + const statusMap = { + 0: 'En attente', + 1: 'En cours', + 2: 'Envoyé', + 3: 'À relancer', + 4: 'À valider', + 5: 'Validé', + 6: 'Archivé', + }; + return statusMap[value] || value; + }, + }, { key: 'formatted_last_update', label: 'Dernière mise à jour' }, ]; - const yearLabel = activeTab === CURRENT_YEAR_FILTER - ? currentSchoolYear - : activeTab === NEXT_YEAR_FILTER - ? nextSchoolYear - : 'historique'; + const yearLabel = + activeTab === CURRENT_YEAR_FILTER + ? currentSchoolYear + : activeTab === NEXT_YEAR_FILTER + ? nextSchoolYear + : 'historique'; const filename = `inscriptions_${yearLabel}_${new Date().toISOString().split('T')[0]}`; exportToCSV(dataToExport, exportColumns, filename); }; @@ -506,7 +550,7 @@ export default function Page({ params: { locale } }) { { icon: ( - + ), onClick: () => @@ -536,7 +580,7 @@ export default function Page({ params: { locale } }) { { icon: ( - + ), onClick: () => @@ -575,7 +619,7 @@ export default function Page({ params: { locale } }) { { icon: ( - + ), onClick: () => { @@ -690,7 +734,7 @@ export default function Page({ params: { locale } }) { { icon: ( - + ), onClick: () => openSepaUploadModal(row), @@ -800,161 +844,139 @@ export default function Page({ params: { locale } }) { }, ]; - let emptyMessage; - if (activeTab === CURRENT_YEAR_FILTER && searchTerm === '') { - emptyMessage = ( - { + if (searchTerm !== '') { + return ( + + ); + } + if (tabFilter === CURRENT_YEAR_FILTER) { + return ( + router.push(FE_ADMIN_SUBSCRIPTIONS_CREATE_URL)} + /> + ); + } + if (tabFilter === NEXT_YEAR_FILTER) { + return ( + router.push(FE_ADMIN_SUBSCRIPTIONS_CREATE_URL)} + /> + ); + } + return ( + ); - } else if (activeTab === NEXT_YEAR_FILTER && searchTerm === '') { - emptyMessage = ( - ( +
+
+
+ + +
+
+ + {profileRole !== 0 && ( + + )} +
+
+ + - ); - } else if (activeTab === HISTORICAL_FILTER && searchTerm === '') { - emptyMessage = ( - - ); - } + + ); if (isLoading) { return ; } return ( -
-
-
- {/* Tab pour l'année scolaire en cours */} - - {currentSchoolYear} - - ({totalCurrentYear}) - - - } - active={activeTab === CURRENT_YEAR_FILTER} - onClick={() => setActiveTab(CURRENT_YEAR_FILTER)} - /> - - {/* Tab pour l'année scolaire prochaine */} - - {nextSchoolYear} - - ({totalNextYear}) - - - } - active={activeTab === NEXT_YEAR_FILTER} - onClick={() => setActiveTab(NEXT_YEAR_FILTER)} - /> - - {/* Tab pour l'historique */} - - {t('historical')} - - ({totalHistorical}) - - - } - active={activeTab === HISTORICAL_FILTER} - onClick={() => setActiveTab(HISTORICAL_FILTER)} - /> -
-
- -
- {activeTab === CURRENT_YEAR_FILTER || - activeTab === NEXT_YEAR_FILTER || - activeTab === HISTORICAL_FILTER ? ( - -
-
- - -
-
- - {profileRole !== 0 && ( - - )} -
-
- -
- -
- - - ) : null} - +
+ 0 ? ` (${totalCurrentYear})` : ''}`, + content: renderTabContent( + registrationFormsDataCurrentYear, + currentSchoolYearPage, + totalCurrentSchoolYearPages, + CURRENT_YEAR_FILTER + ), + }, + { + id: NEXT_YEAR_FILTER, + label: `${nextSchoolYear}${totalNextYear > 0 ? ` (${totalNextYear})` : ''}`, + content: renderTabContent( + registrationFormsDataNextYear, + currentSchoolNextYearPage, + totalNextSchoolYearPages, + NEXT_YEAR_FILTER + ), + }, + { + id: HISTORICAL_FILTER, + label: `${t('historical')}${totalHistorical > 0 ? ` (${totalHistorical})` : ''}`, + content: renderTabContent( + registrationFormsDataHistorical, + currentSchoolHistoricalYearPage, + totalHistoricalPages, + HISTORICAL_FILTER + ), + }, + ]} + onTabChange={(newTab) => setActiveTab(newTab)} + /> - {/* Ajout du logo */} -

{t('welcomeParents')}

-

{t('pleaseLogin')}

+
+ +

{t('welcomeParents')}

+

{t('pleaseLogin')}

); diff --git a/Front-End/src/app/[locale]/parents/layout.js b/Front-End/src/app/[locale]/parents/layout.js index 22319ae..92e7e05 100644 --- a/Front-End/src/app/[locale]/parents/layout.js +++ b/Front-End/src/app/[locale]/parents/layout.js @@ -100,7 +100,7 @@ export default function Layout({ children }) { {/* Main container */}
{children}
diff --git a/Front-End/src/app/[locale]/parents/page.js b/Front-End/src/app/[locale]/parents/page.js index 42d497f..576225a 100644 --- a/Front-End/src/app/[locale]/parents/page.js +++ b/Front-End/src/app/[locale]/parents/page.js @@ -282,10 +282,10 @@ export default function ParentHomePage() { <>
@@ -309,7 +309,7 @@ export default function ParentHomePage() { title="Événements à venir" description="Prochains événements de l'établissement" /> -
+
{upcomingEvents.slice(0, 3).map((event, index) => ( ))} @@ -343,7 +343,7 @@ export default function ParentHomePage() { return (
{/* En-tête de la carte (toujours visible) */}
-

+

{student.last_name} {student.first_name}

@@ -399,7 +399,7 @@ export default function ParentHomePage() {
{child.status === 2 && (
- +
+

+ Paramètres du compte +

+
+
+ + + +
+
+ +
); } diff --git a/Front-End/src/app/[locale]/users/login/page.js b/Front-End/src/app/[locale]/users/login/page.js index 2fa1359..80fe348 100644 --- a/Front-End/src/app/[locale]/users/login/page.js +++ b/Front-End/src/app/[locale]/users/login/page.js @@ -80,16 +80,20 @@ export default function Page() { return ; // Affichez le composant Loader } else { return ( - <> -
-
+
+
+
-

+

Authentification

{ e.preventDefault(); handleFormLogin(new FormData(e.target)); @@ -112,27 +116,24 @@ export default function Page() { placeholder="Mot de passe" className="w-full mb-5" /> -
-
); } } diff --git a/Front-End/src/app/[locale]/users/password/new/page.js b/Front-End/src/app/[locale]/users/password/new/page.js index 3a44909..bac82ef 100644 --- a/Front-End/src/app/[locale]/users/password/new/page.js +++ b/Front-End/src/app/[locale]/users/password/new/page.js @@ -48,16 +48,15 @@ export default function Page() { return ; } else { return ( - <> -
-
+
+
+
-

+

Nouveau Mot de passe

{ e.preventDefault(); validate(new FormData(e.target)); @@ -70,28 +69,23 @@ export default function Page() { IconItem={User} label="Identifiant" placeholder="Identifiant" - className="w-full" + className="w-full mb-6" + /> +
- -
-
+
- +
); } } 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 14c1b7d..02fb5b4 100644 --- a/Front-End/src/app/[locale]/users/password/reset/page.js +++ b/Front-End/src/app/[locale]/users/password/reset/page.js @@ -61,16 +61,15 @@ export default function Page() { return ; } else { return ( - <> -
-
+
+
+
-

+

Réinitialisation du mot de passe

{ e.preventDefault(); validate(new FormData(e.target)); @@ -91,28 +90,23 @@ export default function Page() { IconItem={KeySquare} label="Confirmation mot de passe" placeholder="Confirmation mot de passe" - className="w-full" + className="w-full mb-6" + /> +
- -
-
+
- +
); } } diff --git a/Front-End/src/app/[locale]/users/subscribe/page.js b/Front-End/src/app/[locale]/users/subscribe/page.js index e475cbe..587144d 100644 --- a/Front-End/src/app/[locale]/users/subscribe/page.js +++ b/Front-End/src/app/[locale]/users/subscribe/page.js @@ -65,16 +65,15 @@ export default function Page() { return ; } else { return ( - <> -
-
+
+
+
-

+

Nouveau profil

{ e.preventDefault(); subscribeFormSubmit(new FormData(e.target)); @@ -103,30 +102,23 @@ export default function Page() { IconItem={KeySquare} label="Confirmation mot de passe" placeholder="Confirmation mot de passe" - className="w-full" + className="w-full mb-6" + /> +
- -
-
+
- +
); } } diff --git a/Front-End/src/app/layout.js b/Front-End/src/app/layout.js index 1fae34c..a8e9756 100644 --- a/Front-End/src/app/layout.js +++ b/Front-End/src/app/layout.js @@ -1,19 +1,19 @@ import React from 'react'; import { getMessages } from 'next-intl/server'; -import { Inter, Manrope } from 'next/font/google'; +import localFont from 'next/font/local'; import Providers from '@/components/Providers'; import ServiceWorkerRegister from '@/components/ServiceWorkerRegister'; import '@/css/tailwind.css'; import { headers } from 'next/headers'; -const inter = Inter({ - subsets: ['latin'], +const inter = localFont({ + src: '../fonts/Inter-Variable.woff2', variable: '--font-inter', display: 'swap', }); -const manrope = Manrope({ - subsets: ['latin'], +const manrope = localFont({ + src: '../fonts/Manrope-Variable.woff2', variable: '--font-manrope', display: 'swap', }); diff --git a/Front-End/src/app/not-found.js b/Front-End/src/app/not-found.js index d1a1c58..a13c04a 100644 --- a/Front-End/src/app/not-found.js +++ b/Front-End/src/app/not-found.js @@ -3,16 +3,19 @@ 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.

- + Retour Accueil
diff --git a/Front-End/src/components/Admin/AnnouncementScheduler.js b/Front-End/src/components/Admin/AnnouncementScheduler.js index 1d32cd0..fc38d79 100644 --- a/Front-End/src/components/Admin/AnnouncementScheduler.js +++ b/Front-End/src/components/Admin/AnnouncementScheduler.js @@ -14,7 +14,7 @@ export default function AnnouncementScheduler({ csrfToken }) { return (
-

Planifier une Annonce

+

Planifier une Annonce

{ value={classe.id} checked={formData.classeAssocie_id === classe.id} 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" + className="form-radio h-3 w-3 text-primary focus:ring-primary hover:ring-tertiary checked:bg-primary checked:h-3 checked:w-3" />
{dates[activeTab]?.map((date, index) => (
- + Échéance {index + 1} {modifiedDates[`${activeTab}-${index}`] && ( diff --git a/Front-End/src/components/EmptyState.js b/Front-End/src/components/EmptyState.js new file mode 100644 index 0000000..76be7be --- /dev/null +++ b/Front-End/src/components/EmptyState.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { Inbox } from 'lucide-react'; + +const EmptyState = ({ + icon: Icon = Inbox, + title, + description, + actionLabel, + onAction, + actionIcon: ActionIcon, +}) => { + return ( +
+
+ +
+

+ {title} +

+ {description && ( +

{description}

+ )} + {actionLabel && onAction && ( + + )} +
+ ); +}; + +export default EmptyState; diff --git a/Front-End/src/components/Evaluation/EvaluationForm.js b/Front-End/src/components/Evaluation/EvaluationForm.js index 52e62f0..4511772 100644 --- a/Front-End/src/components/Evaluation/EvaluationForm.js +++ b/Front-End/src/components/Evaluation/EvaluationForm.js @@ -74,7 +74,7 @@ export default function EvaluationForm({ className="space-y-4 p-4 bg-white rounded-lg border border-gray-200 shadow-sm" >
-

+

{isEditing ? 'Modifier l\'évaluation' : 'Nouvelle évaluation'}

diff --git a/Front-End/src/components/Grades/AcademicResults.js b/Front-End/src/components/Grades/AcademicResults.js index 2a81dc8..76c05b1 100644 --- a/Front-End/src/components/Grades/AcademicResults.js +++ b/Front-End/src/components/Grades/AcademicResults.js @@ -3,16 +3,16 @@ import React from 'react'; export default function AcademicResults({ results }) { return (
-

Résultats académiques

+

Résultats académiques

{results.map((result, idx) => (
{result.subject} - + {result.grade}/20
diff --git a/Front-End/src/components/Grades/Attendance.js b/Front-End/src/components/Grades/Attendance.js index 288b7e5..d48cc5e 100644 --- a/Front-End/src/components/Grades/Attendance.js +++ b/Front-End/src/components/Grades/Attendance.js @@ -21,16 +21,16 @@ export default function Attendance({ return (
-

Présence et assiduité

+

Présence et assiduité

{absences.length === 0 ? ( -
- Aucune absence enregistrée 🎉 +
+ Aucune absence enregistrée
) : ( -
    +
      {absences.map((absence, idx) => (
    1. -
      +
      {/* Infos principales à gauche */}
      @@ -45,7 +45,7 @@ export default function Attendance({
      {absence.type} {absence.justified ? 'Justifiée' : 'Non justifiée'} @@ -92,7 +92,7 @@ export default function Attendance({ }); }); }} - className="mb-1 px-4 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600 w-full" + className="mb-1 px-4 py-2 rounded-md shadow bg-primary text-white hover:bg-primary w-full" icon={} text="Supprimer" title="Evaluez l'élève" diff --git a/Front-End/src/components/Grades/GradeView.js b/Front-End/src/components/Grades/GradeView.js index d296ad3..8bae0f9 100644 --- a/Front-End/src/components/Grades/GradeView.js +++ b/Front-End/src/components/Grades/GradeView.js @@ -16,7 +16,7 @@ const getGradeStyle = (grade) => { case 2: return 'bg-yellow-50 border-yellow-200'; case 3: - return 'bg-emerald-50 border-emerald-200'; + return 'bg-primary/5 border-primary/20'; default: return 'bg-gray-50 border-gray-200'; } @@ -68,7 +68,7 @@ export default function GradeView({ data, grades, onGradeChange }) { return (
      -
      +
      {totalCompetencies} compétence{totalCompetencies > 1 ? 's' : ''} au total
      @@ -76,19 +76,19 @@ export default function GradeView({ data, grades, onGradeChange }) {
      toggleDomain(domaine.domaine_id)} >
      - - + + {domaine.domaine_nom}
      {openDomains[domaine.domaine_id] - ? - : + ? + : }
      {openDomains[domaine.domaine_id] && ( @@ -97,7 +97,7 @@ export default function GradeView({ data, grades, onGradeChange }) {
      diff --git a/Front-End/src/components/Grades/GradesDomainBarChart.js b/Front-End/src/components/Grades/GradesDomainBarChart.js index c1ac39f..4850f2d 100644 --- a/Front-End/src/components/Grades/GradesDomainBarChart.js +++ b/Front-End/src/components/Grades/GradesDomainBarChart.js @@ -27,13 +27,13 @@ export default function GradesDomainBarChart({ studentCompetencies }) { if (avg > 0 && avg <= 1) return 'bg-gradient-to-r from-red-200 to-red-400'; if (avg > 1 && avg <= 2) return 'bg-gradient-to-r from-yellow-200 to-yellow-400'; - if (avg > 2) return 'bg-gradient-to-r from-emerald-200 to-emerald-500'; + if (avg > 2) return 'bg-gradient-to-r from-primary/20 to-primary'; return 'bg-gray-200'; }; return (
      -

      Moyenne par domaine

      +

      Moyenne par domaine

      {domainStats.map((d) => (
      @@ -41,7 +41,7 @@ export default function GradesDomainBarChart({ studentCompetencies }) { {d.name}
      -
      +
      {d.avg} diff --git a/Front-End/src/components/Grades/GradesStatsCircle.js b/Front-End/src/components/Grades/GradesStatsCircle.js index e03d0fb..5f22c43 100644 --- a/Front-End/src/components/Grades/GradesStatsCircle.js +++ b/Front-End/src/components/Grades/GradesStatsCircle.js @@ -15,7 +15,7 @@ export default function GradesStatsCircle({ grades }) { return (
      -

      Statistiques globales

      +

      Statistiques globales

      - + {acquired} acquis {inProgress} en cours diff --git a/Front-End/src/components/Grades/Homeworks.js b/Front-End/src/components/Grades/Homeworks.js index 1622f9d..be4fb77 100644 --- a/Front-End/src/components/Grades/Homeworks.js +++ b/Front-End/src/components/Grades/Homeworks.js @@ -3,7 +3,7 @@ import React from 'react'; export default function Homeworks({ homeworks }) { return (
      -

      Suivi des devoirs

      +

      Suivi des devoirs

        {homeworks.map((hw, idx) => (
      • {hw.dueDate}
      {hw.status} diff --git a/Front-End/src/components/Grades/Orientation.js b/Front-End/src/components/Grades/Orientation.js index f8941cb..31dff00 100644 --- a/Front-End/src/components/Grades/Orientation.js +++ b/Front-End/src/components/Grades/Orientation.js @@ -3,7 +3,7 @@ import React from 'react'; export default function Orientation({ orientation }) { return (
      -

      Orientation & conseils

      +

      Orientation & conseils

{ if (actionType === 'upload' && selectedFile?.id === row.id) { @@ -212,11 +212,11 @@ export default function FilesToUpload({
-
- +
+
-

+

Pièces à fournir

diff --git a/Front-End/src/components/Inscription/InscriptionFormShared.js b/Front-End/src/components/Inscription/InscriptionFormShared.js index 6c2143f..26e73de 100644 --- a/Front-End/src/components/Inscription/InscriptionFormShared.js +++ b/Front-End/src/components/Inscription/InscriptionFormShared.js @@ -820,8 +820,7 @@ export default function InscriptionFormShared({ ? `${navButtonBaseClass} bg-gray-200 text-gray-500 cursor-not-allowed` : `${navButtonBaseClass} bg-primary hover:bg-secondary text-white`; - const navSecondaryClass = - `${navButtonBaseClass} bg-neutral text-secondary border border-gray-300 hover:bg-white`; + const navSecondaryClass = `${navButtonBaseClass} bg-neutral text-secondary border border-gray-300 hover:bg-white`; // Rendu du composant return ( @@ -985,7 +984,6 @@ export default function InscriptionFormShared({ /> )}

-
); } diff --git a/Front-End/src/components/Inscription/PaymentMethodSelector.js b/Front-End/src/components/Inscription/PaymentMethodSelector.js index 4ff29f2..c65c59c 100644 --- a/Front-End/src/components/Inscription/PaymentMethodSelector.js +++ b/Front-End/src/components/Inscription/PaymentMethodSelector.js @@ -76,7 +76,7 @@ export default function PaymentMethodSelector({ <> {/* Frais d'inscription */}
-

+

Frais d'inscription

@@ -155,7 +155,7 @@ export default function PaymentMethodSelector({ {/* Frais de scolarité */}
-

+

Frais de scolarité

diff --git a/Front-End/src/components/Inscription/ResponsableInputFields.js b/Front-End/src/components/Inscription/ResponsableInputFields.js index 7a33b22..6901b4c 100644 --- a/Front-End/src/components/Inscription/ResponsableInputFields.js +++ b/Front-End/src/components/Inscription/ResponsableInputFields.js @@ -132,7 +132,7 @@ export default function ResponsableInputFields({ {guardians.map((item, index) => (
-

+

{t('responsable')} {index + 1}

{guardians.length > 1 && ( @@ -259,11 +259,11 @@ export default function ResponsableInputFields({ className={`w-8 h-8 ${ guardians.length >= MAX_GUARDIANS ? 'text-gray-400 cursor-not-allowed' - : 'text-green-500 cursor-pointer hover:text-green-700' + : 'text-primary cursor-pointer hover:text-secondary' } transition-colors border-2 ${ guardians.length >= MAX_GUARDIANS ? 'border-gray-400' - : 'border-green-500 hover:border-green-700' + : 'border-primary hover:border-secondary' } rounded-full p-1`} onClick={(e) => { if (guardians.length < MAX_GUARDIANS) { diff --git a/Front-End/src/components/Inscription/SiblingInputFields.js b/Front-End/src/components/Inscription/SiblingInputFields.js index b9ead46..67d7f0e 100644 --- a/Front-End/src/components/Inscription/SiblingInputFields.js +++ b/Front-End/src/components/Inscription/SiblingInputFields.js @@ -103,7 +103,7 @@ export default function SiblingInputFields({ {siblings.map((item, index) => (
-

Frère/Sœur {index + 1}

+

Frère/Sœur {index + 1}

deleteSibling(index)} @@ -160,7 +160,7 @@ export default function SiblingInputFields({ {enable && (
diff --git a/Front-End/src/components/Inscription/ValidateSubscription.js b/Front-End/src/components/Inscription/ValidateSubscription.js index 45d0c79..b32d43e 100644 --- a/Front-End/src/components/Inscription/ValidateSubscription.js +++ b/Front-End/src/components/Inscription/ValidateSubscription.js @@ -213,7 +213,7 @@ export default function ValidateSubscription({
{currentTemplateIndex < allTemplates.length && (
-

+

{allTemplates[currentTemplateIndex].name || 'Document sans nom'}