import { useEffect, useState } from "preact/hooks"; type Promotion = { id: string; annee: string | null }; type Student = { numEtud: number; idPromo: string }; function parsePromo(id: string) { const m = id.match(/^(\d+A)(FISE|FISA)(.+)$/); if (!m) return { annee: id, filiere: "?", anneeSco: "?" }; return { annee: m[1], filiere: m[2], anneeSco: m[3] }; } const ANNEES = ["3A", "4A", "5A"]; const FILIERES = ["FISE", "FISA"]; export default function AdminPromotions() { const [promos, setPromos] = useState([]); const [students, setStudents] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [creating, setCreating] = useState(false); // PromoBuilder state const [selectedAnnee, setSelectedAnnee] = useState("4A"); const [selectedFiliere, setSelectedFiliere] = useState("FISE"); const [anneeSco, setAnneeSco] = useState(""); const generatedId = anneeSco.trim() ? `${selectedAnnee}${selectedFiliere}${anneeSco.trim()}` : ""; async function load() { try { const [pRes, sRes] = await Promise.all([ fetch("/students/api/promotions"), fetch("/students/api/students"), ]); if (!pRes.ok) throw new Error("Impossible de charger les promotions"); setPromos(await pRes.json()); if (sRes.ok) setStudents(await sRes.json()); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } finally { setLoading(false); } } useEffect(() => { load(); }, []); async function createPromo() { if (!generatedId) return; setCreating(true); try { const res = await fetch("/students/api/promotions", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ idPromo: generatedId, annee: selectedAnnee, }), }); if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error(body.error ?? "Création échouée"); } setAnneeSco(""); await load(); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } finally { setCreating(false); } } async function deletePromo(id: string) { if (!confirm(`Supprimer la promotion ${id} ?`)) return; try { const res = await fetch( `/students/api/promotions/${encodeURIComponent(id)}`, { method: "DELETE" }, ); if (!res.ok) throw new Error("Suppression échouée"); await load(); } catch (e) { setError(e instanceof Error ? e.message : "Erreur"); } } function studentCount(idPromo: string) { return students.filter((s) => s.idPromo === idPromo).length; } return (

Gestion des Promotions

{error &&

{error}

} {/* PromoBuilder */}

Créer une promotion

POST /promotions – idPromo est généré automatiquement

{ANNEES.map((a) => ( ))}
{FILIERES.map((f) => ( ))}
setAnneeSco((e.target as HTMLInputElement).value)} style="min-width: 9rem" />
idPromo généré : {generatedId || "—"}
{/* Existing promotions table */}

Promotions existantes

{loading ?

Chargement…

: (
{promos.length === 0 ? ( ) : promos.map((p) => { const parsed = parsePromo(p.id); const count = studentCount(p.id); return ( ); })}
idPromo Année Filière Année sco. Nb étudiants Actions
Aucune promotion enregistrée
{p.id} {parsed.annee} {parsed.filiere} {parsed.anneeSco} {count} étudiant{count !== 1 ? "s" : ""}
)}
); }