import { useEffect, useState } from "preact/hooks"; type Note = { numEtud: number; idModule: string; note: number; noteSession2: number | null; }; type UE = { id: number; nom: string }; type UEModule = { idModule: string; idUE: number; idPromo: string; coeff: number; }; type Module = { id: string; nom: string }; type Ajustement = { numEtud: number; idUE: number; valeur: number; malus: number; }; type Props = { numEtud: number | null; prenom: string; }; function scoreClass(score: number | null): string { if (score === null) return "score-none"; return score >= 10 ? "score-good" : "score-warn"; } function avgClass(avg: number | null): string { if (avg === null) return ""; return avg >= 10 ? "avg-good" : "avg-warn"; } /** Returns the effective note (session 2 if exists, otherwise session 1). */ function effectiveNote(n: Note): number { return n.noteSession2 ?? n.note; } export default function NotesView({ numEtud, prenom }: Props) { const [notes, setNotes] = useState([]); const [ues, setUes] = useState([]); const [ueModules, setUeModules] = useState([]); const [modules, setModules] = useState([]); const [ajustements, setAjustements] = useState([]); const [promos, setPromos] = useState([]); const [activePromo, setActivePromo] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { if (numEtud === null) { setLoading(false); return; } async function load() { try { const [notesRes, uesRes, ueModRes, modRes, ajRes] = await Promise.all([ fetch(`/notes/api/notes?numEtud=${numEtud}`), fetch("/notes/api/ues"), fetch("/notes/api/ue-modules"), fetch("/notes/api/modules"), fetch(`/notes/api/ajustements?numEtud=${numEtud}`), ]); if (!notesRes.ok || !uesRes.ok || !ueModRes.ok) { throw new Error("Erreur lors du chargement"); } const [notesData, uesData, ueModData, modData, ajData] = await Promise .all([ notesRes.json(), uesRes.json(), ueModRes.json(), modRes.ok ? modRes.json() : [], ajRes.ok ? ajRes.json() : [], ]); setNotes(notesData); setUes(uesData); setUeModules(ueModData); setModules(modData); setAjustements(ajData); const noteModuleIds = new Set(notesData.map((n: Note) => n.idModule)); const relevantPromos = [ ...new Set( ueModData .filter((um: UEModule) => noteModuleIds.has(um.idModule)) .map((um: UEModule) => um.idPromo), ), ] as string[]; setPromos(relevantPromos); if (relevantPromos.length > 0) setActivePromo(relevantPromos[0]); } catch (e) { setError(e instanceof Error ? e.message : "Erreur inconnue"); } finally { setLoading(false); } } load(); }, [numEtud]); if (numEtud === null) { return (

Bonjour {prenom}{" "} — aucun dossier etudiant n'est associe a votre compte.

); } if (loading) { return (

Chargement...

); } if (error) { return (

{error}

); } const filteredUeModules = activePromo ? ueModules.filter((um) => um.idPromo === activePromo) : ueModules; const ueIds = [...new Set(filteredUeModules.map((um) => um.idUE))]; const moduleMap = Object.fromEntries(modules.map((m) => [m.id, m])); const noteMap = Object.fromEntries( notes.map((n) => [n.idModule, n]), ); const ajMap = Object.fromEntries( ajustements.map((a) => [a.idUE, a]), ); return (
{promos.length > 1 && (
{promos.map((p) => ( ))}
)} {ueIds.length === 0 && (

Aucune note disponible pour cette periode.

)} {ueIds.map((ueId) => { const ue = ues.find((u) => u.id === ueId); if (!ue) return null; const ueModsForUE = filteredUeModules.filter((um) => um.idUE === ueId); let weightedSum = 0; let coveredCoeff = 0; ueModsForUE.forEach((um) => { const noteObj = noteMap[um.idModule]; if (noteObj) { const val = effectiveNote(noteObj); weightedSum += val * um.coeff; coveredCoeff += um.coeff; } }); const avg = coveredCoeff > 0 ? weightedSum / coveredCoeff : null; const ajust = ajMap[ueId] ?? null; // If ajust.valeur exists, it replaces the calculated average // Then malus is subtracted let finalAvg: number | null = avg; if (ajust) { finalAvg = ajust.valeur; if (ajust.malus > 0) { finalAvg = (finalAvg ?? 0) - ajust.malus; } } return (

UE : {ue.nom}

{finalAvg !== null ? (

Moyenne : {finalAvg.toFixed(2)}/20 {ajust && ajust.malus > 0 && ( (malus : -{ajust.malus}) )}

) :

Notes non disponibles

}
{ueModsForUE.map((um) => { const mod = moduleMap[um.idModule]; const noteObj = noteMap[um.idModule] ?? null; const effective = noteObj ? effectiveNote(noteObj) : null; const hasS2 = noteObj?.noteSession2 != null; return (
{mod ? mod.id : um.idModule} —{" "} {mod ? mod.nom : "ECUE inconnu"} (coef {um.coeff}) {effective !== null ? `${effective}/20` : "—"} {hasS2 && ( (S1: {noteObj!.note}) )}
); })}
); })}
); }