diff --git a/client/src/pages/AdminGestionAttestations.tsx b/client/src/pages/AdminGestionAttestations.tsx index c3b5bd6..3dc39b4 100644 --- a/client/src/pages/AdminGestionAttestations.tsx +++ b/client/src/pages/AdminGestionAttestations.tsx @@ -171,6 +171,7 @@ export default function AdminGestionAttestations() { const [selectedInscriptionId, setSelectedInscriptionId] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [previewDialogOpen, setPreviewDialogOpen] = useState(false); + const [filtreStatut, setFiltreStatut] = useState<"toutes" | "terminees" | "a_venir">("toutes"); // Récupérer la liste des formations const { data: formations, isLoading: loadingFormations } = trpc.formations.list.useQuery(); @@ -353,6 +354,40 @@ export default function AdminGestionAttestations() { + {/* Filtre par statut */} + {selectedFormationId && ( + + + Filtrer les séquences + + Afficher uniquement les séquences terminées ou à venir + + + + setFiltreStatut(value)}> +
+ + +
+
+ + +
+
+ + +
+
+
+
+ )} + {/* Configuration */} {selectedFormationId && ( @@ -471,20 +506,64 @@ export default function AdminGestionAttestations() { const idB = parseInt(b[0]); return idA - idB; }) + .filter(([sequenceId, sequenceApprenants]) => { + // Filtrer selon le statut sélectionné + if (filtreStatut === "toutes") return true; + const premiereDate = sequenceApprenants[0]?.sequence?.premiereDate; + if (!premiereDate) return true; // Afficher les séquences sans date + const now = new Date(); + const sequenceDate = new Date(premiereDate); + if (filtreStatut === "terminees") { + return sequenceDate < now; + } else if (filtreStatut === "a_venir") { + return sequenceDate >= now; + } + return true; + }) .map(([sequenceId, sequenceApprenants]) => { const sequenceName = sequenceApprenants[0]?.sequence?.nom || 'Séquence inconnue'; + const premiereDate = sequenceApprenants[0]?.sequence?.premiereDate; + const dateFormatted = premiereDate ? new Date(premiereDate).toLocaleDateString('fr-FR') : null; return (
{/* En-tête de séquence */}
-

{sequenceName}

+

+ {sequenceName} + {dateFormatted && - {dateFormatted}} +

{sequenceApprenants.length} apprenant(s)

- - {sequenceApprenants.filter(a => a.attestation?.emailEnvoye).length} / {sequenceApprenants.length} envoyé(es) - +
+ + {sequenceApprenants.filter(a => a.attestation?.emailEnvoye).length} / {sequenceApprenants.length} envoyé(es) + + +
diff --git a/server/gestionAttestationsDb.ts b/server/gestionAttestationsDb.ts index 4f4a471..4b565a8 100644 --- a/server/gestionAttestationsDb.ts +++ b/server/gestionAttestationsDb.ts @@ -159,6 +159,18 @@ export async function getApprenantsWithAttestationStatus(formationId: number) { const db = await getDb(); if (!db) return []; + // Récupérer les premières dates de chaque séquence + const premieresDates = await db + .select({ + sequenceId: datesFormation.sequenceId, + premiereDate: sql`MIN(${datesFormation.date})`, + }) + .from(datesFormation) + .groupBy(datesFormation.sequenceId); + + // Créer un map pour accès rapide + const datesMap = new Map(premieresDates.map(d => [d.sequenceId, d.premiereDate])); + const results = await db .select({ // Apprenant @@ -205,6 +217,7 @@ export async function getApprenantsWithAttestationStatus(formationId: number) { sequence: { id: r.sequenceId, nom: r.sequenceNom, + premiereDate: datesMap.get(r.sequenceId) || null, }, attestation: r.attestationId ? { id: r.attestationId, diff --git a/todo.md b/todo.md index 5f2803e..f17293e 100644 --- a/todo.md +++ b/todo.md @@ -1285,4 +1285,12 @@ - [x] Identifier la cause du problème (structure imbriquée Drizzle ORM) - [x] Corriger le code backend (aplatir la structure de la requête) - [x] Corriger le code frontend (utiliser l'ID de séquence pour le tri) +- [x] Déployer sur le VPS + +## Améliorations de la gestion des attestations + +- [x] Modifier le backend pour inclure la première date de chaque séquence +- [x] Afficher la date de début dans l'en-tête de chaque séquence (ex: "Groupe A - 15/01/2026") +- [x] Ajouter un bouton "Envoyer toutes les attestations de cette séquence" dans l'en-tête +- [x] Créer un filtre "Séquences terminées / à venir" - [ ] Déployer sur le VPS