fix: Mise à jour des upcomming events

This commit is contained in:
Luc SORIGNET
2025-05-31 14:37:32 +02:00
parent e61cd51ce2
commit f93c428259
3 changed files with 193 additions and 32 deletions

View File

@ -1,9 +1,17 @@
'use client';
import React, { useState, useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { Users, Clock, CalendarCheck, School, AlertTriangle, CheckCircle2 } from 'lucide-react';
import {
Users,
Clock,
CalendarCheck,
School,
AlertTriangle,
CheckCircle2,
} from 'lucide-react';
import Loader from '@/components/Loader';
import StatCard from '@/components/StatCard';
import EventCard from '@/components/EventCard';
import logger from '@/utils/logger';
import {
fetchRegisterForms,
@ -15,20 +23,6 @@ import Attendance from '@/components/Grades/Attendance';
import LineChart from '@/components/Charts/LineChart';
import PieChart from '@/components/Charts/PieChart';
// Composant EventCard pour afficher les événements
const EventCard = ({ title, date, description, type }) => (
<div className="bg-stone-50 p-4 rounded-lg shadow-sm border border-gray-100 mb-4">
<div className="flex items-center gap-3">
<CalendarCheck className="text-blue-500" size={20} />
<div>
<h4 className="font-medium">{title}</h4>
<p className="text-sm text-gray-500">{date}</p>
<p className="text-sm mt-1">{description}</p>
</div>
</div>
</div>
);
const mockCompletionRate = 72; // en pourcentage
export default function DashboardPage() {
@ -39,8 +33,11 @@ export default function DashboardPage() {
const [upcomingEvents, setUpcomingEvents] = useState([]);
const [absencesToday, setAbsencesToday] = useState([]);
const { selectedEstablishmentId, selectedEstablishmentTotalCapacity, apiDocuseal } =
useEstablishment();
const {
selectedEstablishmentId,
selectedEstablishmentTotalCapacity,
apiDocuseal,
} = useEstablishment();
const [statusDistribution, setStatusDistribution] = useState([
{ label: 'Non envoyé', value: 0 },

View File

@ -2,7 +2,14 @@
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import Table from '@/components/Table';
import { Edit3, Users, Download, Eye, Upload, CalendarDays } from 'lucide-react';
import {
Edit3,
Users,
Download,
Eye,
Upload,
CalendarDays,
} from 'lucide-react';
import StatusLabel from '@/components/StatusLabel';
import FileUpload from '@/components/FileUpload';
import { FE_PARENTS_EDIT_SUBSCRIPTION_URL } from '@/utils/Url';
@ -10,17 +17,16 @@ import {
fetchChildren,
editRegisterForm,
} from '@/app/actions/subscriptionAction';
import { fetchUpcomingEvents } from '@/app/actions/planningAction';
import logger from '@/utils/logger';
import { BASE_URL } from '@/utils/Url';
import { useEstablishment } from '@/context/EstablishmentContext';
import { useCsrfToken } from '@/context/CsrfContext';
import { useClasses } from '@/context/ClassesContext';
import {
PlanningProvider,
PlanningModes
} from '@/context/PlanningContext';
import { PlanningProvider, PlanningModes } from '@/context/PlanningContext';
import SectionHeader from '@/components/SectionHeader';
import ParentPlanningSection from '@/components/ParentPlanningSection';
import EventCard from '@/components/EventCard';
export default function ParentHomePage() {
const [children, setChildren] = useState([]);
@ -30,6 +36,7 @@ export default function ParentHomePage() {
const [uploadState, setUploadState] = useState('off'); // État "on" ou "off" pour l'affichage du composant
const [showPlanning, setShowPlanning] = useState(false);
const [planningClassName, setPlanningClassName] = useState(null);
const [upcomingEvents, setUpcomingEvents] = useState([]);
const router = useRouter();
const csrfToken = useCsrfToken();
const [reloadFetch, setReloadFetch] = useState(false);
@ -43,7 +50,20 @@ export default function ParentHomePage() {
});
setReloadFetch(false);
}
}, [selectedEstablishmentId, reloadFetch]);
}, [selectedEstablishmentId, reloadFetch, user]);
useEffect(() => {
if (selectedEstablishmentId) {
// Fetch des événements à venir
fetchUpcomingEvents(selectedEstablishmentId)
.then((data) => {
setUpcomingEvents(data);
})
.catch((error) => {
logger.error('Error fetching upcoming events:', error);
});
}
}, [selectedEstablishmentId]);
function handleView(eleveId) {
logger.debug(`View dossier for student id: ${eleveId}`);
@ -106,7 +126,9 @@ export default function ParentHomePage() {
};
const showClassPlanning = (student) => {
setPlanningClassName(`${student.associated_class_name} - ${getNiveauLabel(student.level)}`);
setPlanningClassName(
`${student.associated_class_name} - ${getNiveauLabel(student.level)}`
);
setShowPlanning(true);
};
@ -143,7 +165,7 @@ export default function ParentHomePage() {
{
name: 'Classe',
transform: (row) => (
<div className="text-center">{(row.student.associated_class_name)}</div>
<div className="text-center">{row.student.associated_class_name}</div>
),
},
{
@ -276,25 +298,36 @@ export default function ParentHomePage() {
modeSet={PlanningModes.CLASS_SCHEDULE}
readOnly={true}
>
<ParentPlanningSection
planningClassName={planningClassName}
/>
<ParentPlanningSection planningClassName={planningClassName} />
</PlanningProvider>
</div>
</>
) : (
// Affichage classique avec le tableau des enfants
<div>
{/* Section des événements à venir */}
{upcomingEvents.length > 0 && (
<div className="mb-6">
<SectionHeader
icon={CalendarDays}
title="Événements à venir"
description="Prochains événements de l'établissement"
/>
<div className="bg-stone-50 p-4 rounded-lg shadow-sm border border-gray-100">
{upcomingEvents.slice(0, 3).map((event, index) => (
<EventCard key={index} {...event} />
))}
</div>
</div>
)}
<SectionHeader
icon={Users}
title="Vos enfants"
description="Suivez le parcours de vos enfants"
/>
<div className="overflow-x-auto">
<Table
data={children}
columns={childrenColumns}
/>
<Table data={children} columns={childrenColumns} />
</div>
{/* Composant FileUpload et bouton Valider en dessous du tableau */}
{uploadState === 'on' && uploadingStudentId && (

View File

@ -0,0 +1,131 @@
import React from 'react';
import { CalendarCheck } from 'lucide-react';
/**
* Formate une date en format français avec jour de la semaine
* @param {string} startDateString - Date de début au format ISO
* @param {string} endDateString - Date de fin au format ISO (optionnel)
* @returns {object} Objet contenant la date formatée et le jour
*/
const formatEventDate = (startDateString, endDateString) => {
if (!startDateString)
return { formattedDate: '', dayName: '', timeIndicator: '', timeRange: '' };
try {
const startDate = new Date(startDateString);
const endDate = endDateString ? new Date(endDateString) : null;
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const eventDate = new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate()
);
// Options pour le formatage de la date
const dateOptions = {
day: 'numeric',
month: 'long',
year: 'numeric',
};
const dayOptions = {
weekday: 'long',
};
const timeOptions = {
hour: '2-digit',
minute: '2-digit',
};
const formattedDate = startDate.toLocaleDateString('fr-FR', dateOptions);
const dayName = startDate.toLocaleDateString('fr-FR', dayOptions);
// Formatage de l'heure
let timeRange = '';
if (endDate) {
const startTime = startDate.toLocaleTimeString('fr-FR', timeOptions);
const endTime = endDate.toLocaleTimeString('fr-FR', timeOptions);
timeRange = `${startTime} - ${endTime}`;
} else {
timeRange = startDate.toLocaleTimeString('fr-FR', timeOptions);
}
// Calcul des jours restants
const diffTime = eventDate - today;
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
let timeIndicator = '';
if (diffDays === 0) {
timeIndicator = "Aujourd'hui";
} else if (diffDays === 1) {
timeIndicator = 'Demain';
} else if (diffDays > 0 && diffDays <= 7) {
timeIndicator = `Dans ${diffDays} jours`;
}
return {
formattedDate,
dayName: dayName.charAt(0).toUpperCase() + dayName.slice(1),
timeIndicator,
timeRange,
};
} catch (error) {
// logger.error('Erreur lors du formatage de la date:', error);
return {
formattedDate: startDateString,
dayName: '',
timeIndicator: '',
timeRange: '',
};
}
};
/**
* Composant EventCard pour afficher les événements à venir
* @param {string} title - Titre de l'événement
* @param {string} start - Date de début de l'événement (format ISO)
* @param {string} end - Date de fin de l'événement (format ISO)
* @param {string} date - Date de l'événement (pour compatibilité)
* @param {string} description - Description de l'événement
* @param {string} type - Type d'événement (optionnel)
* @returns {JSX.Element} Carte d'événement
*/
const EventCard = ({ title, start, end, date, description, type }) => {
// Utiliser start si disponible, sinon date pour compatibilité
const eventDate = start || date;
const { formattedDate, dayName, timeIndicator, timeRange } = formatEventDate(
eventDate,
end
);
return (
<div className="bg-white p-4 rounded-lg shadow-sm border border-gray-200 mb-3 hover:shadow-md transition-shadow">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 mt-1">
<CalendarCheck className="text-emerald-500" size={20} />
</div>
<div className="flex-1">
<h4 className="font-medium text-gray-900 mb-1">{title}</h4>
<div className="flex flex-col text-sm text-gray-500">
<span className="font-medium text-emerald-600">{dayName}</span>
<span>{formattedDate}</span>
{timeRange && (
<span className="text-xs text-gray-600 mt-1">{timeRange}</span>
)}
{timeIndicator && (
<span className="text-xs text-blue-600 mt-1 font-medium">
{timeIndicator}
</span>
)}
</div>
{description && (
<p className="text-sm mt-2 text-gray-700">{description}</p>
)}
</div>
</div>
</div>
);
};
export default EventCard;