mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-29 07:53:23 +00:00
chore: câblage des absences/retard dans le suivi pédagogique
This commit is contained in:
@ -20,6 +20,9 @@ import {
|
||||
fetchStudents,
|
||||
fetchStudentCompetencies,
|
||||
searchStudents,
|
||||
fetchAbsences,
|
||||
editAbsences,
|
||||
deleteAbsences,
|
||||
} from '@/app/actions/subscriptionAction';
|
||||
import { useEstablishment } from '@/context/EstablishmentContext';
|
||||
import { useClasses } from '@/context/ClassesContext';
|
||||
@ -28,9 +31,11 @@ import SectionHeader from '@/components/SectionHeader';
|
||||
import GradesDomainBarChart from '@/components/Grades/GradesDomainBarChart';
|
||||
import InputText from '@/components/InputText';
|
||||
import dayjs from 'dayjs';
|
||||
import { useCsrfToken } from '@/context/CsrfContext';
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
const csrfToken = useCsrfToken();
|
||||
const { selectedEstablishmentId, selectedEstablishmentEvaluationFrequency } =
|
||||
useEstablishment();
|
||||
const { getNiveauLabel } = useClasses();
|
||||
@ -43,6 +48,7 @@ export default function Page() {
|
||||
const [grades, setGrades] = useState({});
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedPeriod, setSelectedPeriod] = useState(null);
|
||||
const [allAbsences, setAllAbsences] = useState([]);
|
||||
|
||||
// Définir les périodes selon la fréquence
|
||||
const getPeriods = () => {
|
||||
@ -99,11 +105,6 @@ export default function Page() {
|
||||
},
|
||||
];
|
||||
|
||||
const absences = [
|
||||
{ date: '2023-09-01', type: 'Absence', reason: 'Maladie', justified: true },
|
||||
{ date: '2023-09-15', type: 'Retard', reason: 'Trafic', justified: false },
|
||||
];
|
||||
|
||||
const remarks = [
|
||||
{
|
||||
date: '2023-09-10',
|
||||
@ -196,6 +197,32 @@ export default function Page() {
|
||||
}
|
||||
}, [formData.selectedStudent, selectedPeriod]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedEstablishmentId) {
|
||||
fetchAbsences(selectedEstablishmentId)
|
||||
.then((data) => setAllAbsences(data))
|
||||
.catch((error) =>
|
||||
logger.error('Erreur lors du fetch des absences:', error)
|
||||
);
|
||||
}
|
||||
}, [selectedEstablishmentId]);
|
||||
|
||||
// Transforme les absences backend pour l'élève sélectionné
|
||||
const absences = React.useMemo(() => {
|
||||
if (!formData.selectedStudent) return [];
|
||||
return allAbsences
|
||||
.filter((a) => a.student === formData.selectedStudent)
|
||||
.map((a) => ({
|
||||
id: a.id,
|
||||
date: a.day,
|
||||
type: [2, 1].includes(a.reason) ? 'Absence' : 'Retard',
|
||||
reason: a.reason, // tu peux mapper le code vers un label si besoin
|
||||
justified: [1, 3].includes(a.reason), // 1 et 3 = justifié
|
||||
moment: a.moment,
|
||||
commentaire: a.commentaire,
|
||||
}));
|
||||
}, [allAbsences, formData.selectedStudent]);
|
||||
|
||||
// Fonction utilitaire pour convertir la période sélectionnée en string backend
|
||||
function getPeriodString(selectedPeriod, frequency) {
|
||||
const year = dayjs().month() >= 8 ? dayjs().year() : dayjs().year() - 1; // année scolaire commence en septembre
|
||||
@ -207,6 +234,42 @@ export default function Page() {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Callback pour justifier/non justifier une absence
|
||||
const handleToggleJustify = (absence) => {
|
||||
// Inverser l'état justifié (1/3 = justifié, 2/4 = non justifié)
|
||||
const newReason =
|
||||
absence.type === 'Absence'
|
||||
? absence.justified
|
||||
? 2 // Absence non justifiée
|
||||
: 1 // Absence justifiée
|
||||
: absence.justified
|
||||
? 4 // Retard non justifié
|
||||
: 3; // Retard justifié
|
||||
|
||||
editAbsences(absence.id, { ...absence, reason: newReason }, csrfToken)
|
||||
.then(() => {
|
||||
setAllAbsences((prev) =>
|
||||
prev.map((a) =>
|
||||
a.id === absence.id ? { ...a, reason: newReason } : a
|
||||
)
|
||||
);
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.error('Erreur lors du changement de justification', e);
|
||||
});
|
||||
};
|
||||
|
||||
// Callback pour supprimer une absence
|
||||
const handleDeleteAbsence = (absence) => {
|
||||
return deleteAbsences(absence.id, csrfToken)
|
||||
.then(() => {
|
||||
setAllAbsences((prev) => prev.filter((a) => a.id !== absence.id));
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.error("Erreur lors de la suppression de l'absence", e);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8">
|
||||
<SectionHeader
|
||||
@ -392,7 +455,11 @@ export default function Page() {
|
||||
<div className="flex flex-col gap-8 w-full justify-center items-stretch">
|
||||
<div className="w-full flex flex-row items-stretch gap-4">
|
||||
<div className="flex-1 flex items-stretch justify-center h-full">
|
||||
<Attendance absences={absences} />
|
||||
<Attendance
|
||||
absences={absences}
|
||||
onToggleJustify={handleToggleJustify}
|
||||
onDelete={handleDeleteAbsence}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 flex items-stretch justify-center h-full">
|
||||
<GradesStatsCircle grades={grades} />
|
||||
|
||||
@ -345,6 +345,7 @@ export default function Page() {
|
||||
reason: reason,
|
||||
moment: absenceData.moment,
|
||||
establishment: selectedEstablishmentId,
|
||||
commentaire: absenceData.commentaire,
|
||||
};
|
||||
|
||||
if (absenceData.id) {
|
||||
@ -574,7 +575,7 @@ export default function Page() {
|
||||
Motif d'absence
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 items-center">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-2 items-center">
|
||||
{/* Select Absence/Retard */}
|
||||
<SelectChoice
|
||||
name={`type-${row.id}`}
|
||||
@ -619,6 +620,23 @@ export default function Page() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Nouveau champ commentaire */}
|
||||
<input
|
||||
type="text"
|
||||
className="border rounded px-2 py-1 text-sm w-full"
|
||||
placeholder="Commentaire"
|
||||
value={formAbsences[row.id]?.commentaire || ''}
|
||||
onChange={(e) =>
|
||||
setFormAbsences((prev) => ({
|
||||
...prev,
|
||||
[row.id]: {
|
||||
...prev[row.id],
|
||||
commentaire: e.target.value,
|
||||
},
|
||||
}))
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Checkbox Justifié */}
|
||||
<div className="flex items-center gap-2 justify-center">
|
||||
<CheckBox
|
||||
|
||||
Reference in New Issue
Block a user