chore: Ajout des présences dans le dashboard

This commit is contained in:
N3WT DE COMPET
2025-05-25 19:33:20 +02:00
parent 98763dc90a
commit d877c956b7
3 changed files with 97 additions and 46 deletions

View File

@ -23,9 +23,18 @@ import pytz
import Subscriptions.util as util import Subscriptions.util as util
class AbsenceManagementSerializer(serializers.ModelSerializer): class AbsenceManagementSerializer(serializers.ModelSerializer):
student_name = serializers.SerializerMethodField()
class Meta: class Meta:
model = AbsenceManagement model = AbsenceManagement
fields = '__all__' fields = '__all__'
# Ajoute les nouveaux champs au retour JSON
extra_fields = ['student_name']
def get_student_name(self, obj):
if obj.student:
return f"{obj.student.first_name} {obj.student.last_name}"
return None
class RegistrationSchoolFileMasterSerializer(serializers.ModelSerializer): class RegistrationSchoolFileMasterSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)

View File

@ -7,10 +7,15 @@ import ClasseDetails from '@/components/ClasseDetails';
import { fetchClasses } from '@/app/actions/schoolAction'; import { fetchClasses } from '@/app/actions/schoolAction';
import StatCard from '@/components/StatCard'; import StatCard from '@/components/StatCard';
import logger from '@/utils/logger'; import logger from '@/utils/logger';
import { fetchRegisterForms } from '@/app/actions/subscriptionAction'; import {
fetchRegisterForms,
fetchAbsences,
} from '@/app/actions/subscriptionAction';
import { fetchUpcomingEvents } from '@/app/actions/planningAction'; import { fetchUpcomingEvents } from '@/app/actions/planningAction';
import { useEstablishment } from '@/context/EstablishmentContext'; import { useEstablishment } from '@/context/EstablishmentContext';
import { useNotification } from '@/context/NotificationContext'; import { useNotification } from '@/context/NotificationContext';
import Attendance from '@/components/Grades/Attendance';
// Composant EventCard pour afficher les événements // Composant EventCard pour afficher les événements
const EventCard = ({ title, date, description, type }) => ( 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="bg-stone-50 p-4 rounded-lg shadow-sm border border-gray-100 mb-4">
@ -38,6 +43,7 @@ export default function DashboardPage() {
}); });
const [classes, setClasses] = useState([]); const [classes, setClasses] = useState([]);
const [absencesToday, setAbsencesToday] = useState([]);
const { selectedEstablishmentId, selectedEstablishmentTotalCapacity } = const { selectedEstablishmentId, selectedEstablishmentTotalCapacity } =
useEstablishment(); useEstablishment();
const { showNotification } = useNotification(); const { showNotification } = useNotification();
@ -90,6 +96,27 @@ export default function DashboardPage() {
}) })
.catch((error) => { .catch((error) => {
logger.error('Error fetching upcoming events:', error); logger.error('Error fetching upcoming events:', error);
});
// Fetch absences/retards du jour
fetchAbsences(selectedEstablishmentId)
.then((data) => {
const today = new Date().toISOString().split('T')[0];
const absToday = (data || []).filter((a) => a.day === today);
// Mapping pour Attendance
setAbsencesToday(
absToday.map((a) => ({
id: a.id,
date: a.day,
type: [1, 2].includes(a.reason) ? 'Absence' : 'Retard',
justified: [1, 3].includes(a.reason),
commentaire: a.commentaire,
student_name: a.student_name,
}))
);
})
.catch((error) => {
logger.error('Erreur lors du fetch des absences du jour:', error);
}) })
.finally(() => { .finally(() => {
setIsLoading(false); // Fin du chargement setIsLoading(false); // Fin du chargement
@ -137,7 +164,7 @@ export default function DashboardPage() {
{t('inscriptionTrends')} {t('inscriptionTrends')}
</h2> </h2>
{/* Insérer ici un composant de graphique */} {/* Insérer ici un composant de graphique */}
<div className="h-64 bg-gray-50 rounded flex items-center justify-center"> <div className="h-64 bg-gray-50 rounded flex items-center justify-center mb-6">
<TrendingUp size={48} className="text-gray-300" /> <TrendingUp size={48} className="text-gray-300" />
</div> </div>
</div> </div>
@ -150,6 +177,9 @@ export default function DashboardPage() {
))} ))}
</div> </div>
</div> </div>
{/* Ajout du composant Attendance en dessous, en lecture seule */}
<Attendance absences={absencesToday} readOnly={true} />
</div> </div>
); );
} }

View File

@ -5,7 +5,12 @@ import Button from '@/components/Button';
import Popup from '@/components/Popup'; import Popup from '@/components/Popup';
import { useNotification } from '@/context/NotificationContext'; import { useNotification } from '@/context/NotificationContext';
export default function Attendance({ absences, onToggleJustify, onDelete }) { export default function Attendance({
absences,
onToggleJustify,
onDelete,
readOnly,
}) {
const [popupVisible, setPopupVisible] = useState(false); const [popupVisible, setPopupVisible] = useState(false);
const [popupMessage, setPopupMessage] = useState(''); const [popupMessage, setPopupMessage] = useState('');
const [removePopupVisible, setRemovePopupVisible] = useState(false); const [removePopupVisible, setRemovePopupVisible] = useState(false);
@ -32,6 +37,11 @@ export default function Attendance({ absences, onToggleJustify, onDelete }) {
<time className="mb-1 text-xs font-normal leading-none text-gray-400"> <time className="mb-1 text-xs font-normal leading-none text-gray-400">
{absence.date} {absence.date}
</time> </time>
{readOnly && absence.student_name && (
<div className="mb-1 text-sm font-semibold text-gray-700">
{absence.student_name}
</div>
)}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="font-medium">{absence.type}</span> <span className="font-medium">{absence.type}</span>
<span <span
@ -44,49 +54,51 @@ export default function Attendance({ absences, onToggleJustify, onDelete }) {
{absence.commentaire} {absence.commentaire}
</div> </div>
</div> </div>
{/* Actions à droite */} {/* Actions masquées en lecture seule */}
<div className="flex flex-col items-end gap-2 min-w-[110px]"> {!readOnly && (
<ToggleSwitch <div className="flex flex-col items-end gap-2 min-w-[110px]">
name={`justified-${idx}`} <ToggleSwitch
checked={absence.justified} name={`justified-${idx}`}
onChange={() => onToggleJustify(absence)} checked={absence.justified}
label="Justifiée" onChange={() => onToggleJustify(absence)}
/> label="Justifiée"
<Button />
primary <Button
onClick={() => { primary
setRemovePopupVisible(true); onClick={() => {
setRemovePopupMessage( setRemovePopupVisible(true);
"Attention ! \nVous êtes sur le point de supprimer l'absence enregistrée.\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?" setRemovePopupMessage(
); "Attention ! \nVous êtes sur le point de supprimer l'absence enregistrée.\nÊtes-vous sûr(e) de vouloir poursuivre l'opération ?"
setRemovePopupOnConfirm(() => () => { );
onDelete(absence) setRemovePopupOnConfirm(() => () => {
.then((data) => { onDelete(absence)
showNotification( .then((data) => {
'Opération effectuée avec succès.', showNotification(
'success', 'Opération effectuée avec succès.',
'Succès' 'success',
); 'Succès'
setPopupVisible(true); );
setRemovePopupVisible(false); setPopupVisible(true);
}) setRemovePopupVisible(false);
.catch((error) => { })
showNotification( .catch((error) => {
'Erreur lors de la suppression de l\absence', showNotification(
'error', 'Erreur lors de la suppression de l\absence',
'Erreur' 'error',
); 'Erreur'
setPopupVisible(true); );
setRemovePopupVisible(false); setPopupVisible(true);
}); setRemovePopupVisible(false);
}); });
}} });
className="mb-1 px-4 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600 w-full" }}
icon={<Trash2 className="w-6 h-6" />} className="mb-1 px-4 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600 w-full"
text="Supprimer" icon={<Trash2 className="w-6 h-6" />}
title="Evaluez l'élève" text="Supprimer"
/> title="Evaluez l'élève"
</div> />
</div>
)}
</div> </div>
</li> </li>
))} ))}