mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
chore: Ajout des présences dans le dashboard
This commit is contained in:
@ -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)
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,7 +54,8 @@ export default function Attendance({ absences, onToggleJustify, onDelete }) {
|
|||||||
{absence.commentaire}
|
{absence.commentaire}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Actions à droite */}
|
{/* Actions masquées en lecture seule */}
|
||||||
|
{!readOnly && (
|
||||||
<div className="flex flex-col items-end gap-2 min-w-[110px]">
|
<div className="flex flex-col items-end gap-2 min-w-[110px]">
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
name={`justified-${idx}`}
|
name={`justified-${idx}`}
|
||||||
@ -87,6 +98,7 @@ export default function Attendance({ absences, onToggleJustify, onDelete }) {
|
|||||||
title="Evaluez l'élève"
|
title="Evaluez l'élève"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user