feat: Génération du bilan de compétence en PDF [#16]

This commit is contained in:
N3WT DE COMPET
2025-05-21 20:44:37 +02:00
parent eb7805e54e
commit 0fe6c76189
14 changed files with 357 additions and 67 deletions

View File

@ -11,7 +11,10 @@ import Orientation from '@/components/Grades/Orientation';
import GradesStatsCircle from '@/components/Grades/GradesStatsCircle';
import Button from '@/components/Button';
import logger from '@/utils/logger';
import { FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL } from '@/utils/Url';
import {
FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL,
BASE_URL,
} from '@/utils/Url';
import { useRouter } from 'next/navigation';
import {
fetchStudents,
@ -21,6 +24,9 @@ import {
import { useEstablishment } from '@/context/EstablishmentContext';
import { useClasses } from '@/context/ClassesContext';
import StudentInput from '@/components/Grades/StudentInput';
import { Award, BookOpen } from 'lucide-react';
import SectionHeader from '@/components/SectionHeader';
import GradesDomainBarChart from '@/components/Grades/GradesDomainBarChart';
export default function Page() {
const router = useRouter();
@ -145,9 +151,14 @@ export default function Page() {
return (
<div className="p-8 space-y-8">
{/* Sélection de l'élève */}
<div className="bg-white p-6 rounded-lg shadow-sm border border-gray-200">
<h2 className="text-xl font-semibold mb-4">Sélectionner un élève</h2>
<div className="flex flex-col sm:flex-row sm:items-end gap-4">
<SectionHeader
icon={BookOpen}
title="Suivi pédagogique"
description="Suivez le parcours d'un élève"
/>
<div className="flex flex-col md:flex-row md:items-start gap-4">
{/* Recherche élève + bouton + fiche élève */}
<div className="flex-1 flex flex-row gap-4 items-start">
<div className="flex-1">
<StudentInput
label="Recherche élève"
@ -161,49 +172,78 @@ export default function Page() {
establishmentId={selectedEstablishmentId}
required
/>
{/* <SelectChoice
name="selectedStudent"
label="Élève"
placeHolder="Sélectionnez un élève"
selected={formData.selectedStudent || ''}
callback={(e) => handleChange('selectedStudent', e.target.value)}
choices={students.map((student) => ({
value: student.id,
label: `${student.last_name} ${student.first_name} - ${getNiveauLabel(
student.level
)} (${student.associated_class_name})`,
}))}
required
/> */}
</div>
<Button
text="Réaliser le bilan de compétences"
primary
disabled={!formData.selectedStudent}
onClick={() => {
const url = `${FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL}?studentId=${formData.selectedStudent}`;
router.push(`${url}`);
}}
className={`px-6 py-2 rounded-md shadow ${
!formData.selectedStudent
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
: 'bg-emerald-500 text-white hover:bg-emerald-600'
}`}
/>
{formData.selectedStudent && (
<div className="ml-4 flex flex-col items-center min-w-[220px] max-w-xs p-4 rounded-lg border border-emerald-100 shadow">
{(() => {
const student = students.find(
(s) => s.id === formData.selectedStudent
);
if (!student) return null;
return (
<>
{student.photo ? (
<img
src={`${BASE_URL}${student.photo}`}
alt={`${student.first_name} ${student.last_name}`}
className="w-20 h-20 object-cover rounded-full border-4 border-emerald-200 mb-2 shadow"
/>
) : (
<div className="w-20 h-20 flex items-center justify-center bg-gray-200 rounded-full text-gray-500 font-bold text-3xl mb-2 border-4 border-emerald-100">
{student.first_name?.[0]}
{student.last_name?.[0]}
</div>
)}
<div className="text-center">
<div className="text-base font-semibold text-emerald-800">
{student.last_name} {student.first_name}
</div>
<div className="text-xs text-gray-600 mt-1">
Niveau :{' '}
<span className="font-medium">
{getNiveauLabel(student.level)}
</span>
</div>
<div className="text-xs text-gray-600">
Classe :{' '}
<span className="font-medium">
{student.associated_class_name}
</span>
</div>
</div>
{/* Bouton bilan de compétences en dessous */}
<Button
primary
onClick={() => {
const url = `${FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL}?studentId=${formData.selectedStudent}`;
router.push(`${url}`);
}}
className="mt-4 px-6 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600"
icon={<Award className="w-6 h-6" />}
title="Réaliser le bilan de compétences"
/>
</>
);
})()}
</div>
)}
</div>
</div>
{/* Partie basse : stats à gauche, présence à droite, sans bg */}
{formData.selectedStudent && (
<>
{/* <AcademicResults results={academicResults} /> */}
<Attendance absences={absences} />
<GradesStatsCircle grades={grades} />
{/* <Remarks remarks={remarks} />
<WorkPlan workPlan={workPlan} />
<Homeworks homeworks={homeworks} />
<SpecificEvaluations specificEvaluations={specificEvaluations} />
<Orientation orientation={orientation} /> */}
</>
<div className="flex flex-col gap-8 w-full justify-center items-stretch mt-8">
<div className="w-3/4 flex flex-row items-stretch gap-4 mx-auto">
<div className="flex-1 flex items-center justify-center">
<Attendance absences={absences} />
</div>
<div className="flex-1 flex items-center justify-center">
<GradesStatsCircle grades={grades} />
</div>
</div>
<div className="flex items-center justify-center">
<GradesDomainBarChart studentCompetencies={studentCompetencies} />
</div>
</div>
)}
</div>
);