import { useEffect, useState } from "preact/hooks"; type Student = { numEtud: number; nom: string; prenom: string; idPromo: string; }; type Promotion = { id: string; annee: string }; export default function ConsultStudents() { const [students, setStudents] = useState([]); const [promos, setPromos] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [filterPromo, setFilterPromo] = useState(""); const [filterNom, setFilterNom] = useState(""); const [selected, setSelected] = useState>(new Set()); const [bulkPromo, setBulkPromo] = useState(""); const [bulkBusy, setBulkBusy] = useState(false); async function load() { try { const [sRes, pRes] = await Promise.all([ fetch("/students/api/students"), fetch("/students/api/promotions"), ]); if (!sRes.ok) throw new Error("Impossible de charger les élèves"); setStudents(await sRes.json()); if (pRes.ok) setPromos(await pRes.json()); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } finally { setLoading(false); } } useEffect(() => { load(); }, []); async function deleteStudent(numEtud: number) { if (!confirm(`Supprimer l'élève #${numEtud} ?`)) return; try { const res = await fetch(`/students/api/students/${numEtud}`, { method: "DELETE", }); if (!res.ok) throw new Error("Suppression échouée"); await load(); setSelected((prev) => { const next = new Set(prev); next.delete(numEtud); return next; }); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } } const filtered = students.filter((s) => { const matchPromo = !filterPromo || s.idPromo === filterPromo; const matchNom = !filterNom || `${s.nom} ${s.prenom}`.toLowerCase().includes(filterNom.toLowerCase()); return matchPromo && matchNom; }); const filteredIds = new Set(filtered.map((s) => s.numEtud)); const selectedInView = [...selected].filter((id) => filteredIds.has(id)); const allFilteredSelected = filtered.length > 0 && filtered.every((s) => selected.has(s.numEtud)); function toggleOne(numEtud: number) { setSelected((prev) => { const next = new Set(prev); if (next.has(numEtud)) next.delete(numEtud); else next.add(numEtud); return next; }); } function toggleAll() { if (allFilteredSelected) { setSelected((prev) => { const next = new Set(prev); for (const s of filtered) next.delete(s.numEtud); return next; }); } else { setSelected((prev) => { const next = new Set(prev); for (const s of filtered) next.add(s.numEtud); return next; }); } } async function bulkDelete() { const count = selectedInView.length; if (count === 0) return; if ( !confirm(`Supprimer définitivement ${count} élève(s) sélectionné(s) ?`) ) return; setBulkBusy(true); try { const results = await Promise.all( selectedInView.map((id) => fetch(`/students/api/students/${id}`, { method: "DELETE" }) ), ); const failed = results.filter((r) => !r.ok).length; if (failed > 0) setError(`${failed} suppression(s) échouée(s)`); setSelected(new Set()); await load(); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } finally { setBulkBusy(false); } } async function bulkChangePromo() { if (!bulkPromo || selectedInView.length === 0) return; setBulkBusy(true); try { const results = await Promise.all( selectedInView.map((id) => fetch(`/students/api/students/${id}`, { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify({ idPromo: bulkPromo }), }) ), ); const failed = results.filter((r) => !r.ok).length; if (failed > 0) setError(`${failed} modification(s) échouée(s)`); setSelected(new Set()); setBulkPromo(""); await load(); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } finally { setBulkBusy(false); } } return (

Gestion des Élèves

{error &&

{error}

}
setFilterNom((e.target as HTMLInputElement).value)} />
{/* Bulk actions bar */} {selectedInView.length > 0 && (
{selectedInView.length} sélectionné(s)
)} {loading ?

Chargement…

: (
{filtered.length === 0 ? ( ) : filtered.map((s) => ( ))}
0} onChange={toggleAll} /> N° étud. Nom Prénom Promo Actions
Aucun élève trouvé
toggleOne(s.numEtud)} /> {s.numEtud} {s.nom} {s.prenom} {s.idPromo}
)}
); }