diff --git a/client/src/pages/FormateurEmargement.tsx b/client/src/pages/FormateurEmargement.tsx index b117782..0ec223b 100644 --- a/client/src/pages/FormateurEmargement.tsx +++ b/client/src/pages/FormateurEmargement.tsx @@ -69,6 +69,12 @@ export default function FormateurEmargement() { { enabled: !!selectedSequenceId } ); + // Récupérer les signatures formateurs de la séquence + const { data: signaturesFormateurs, refetch: refetchSignaturesFormateurs } = trpc.presences.listSignaturesFormateurs.useQuery( + { sequenceId: selectedSequenceId || 0 }, + { enabled: !!selectedSequenceId } + ); + // Rafraîchissement automatique toutes les 30 secondes useEffect(() => { if (!selectedSequenceId) return; @@ -76,12 +82,13 @@ export default function FormateurEmargement() { const interval = setInterval(() => { refetchInscrits(); refetchPresences(); + refetchSignaturesFormateurs(); setLastRefreshTime(new Date()); setSecondsSinceRefresh(0); }, 30000); // 30 secondes return () => clearInterval(interval); - }, [selectedSequenceId, refetchInscrits, refetchPresences]); + }, [selectedSequenceId, refetchInscrits, refetchPresences, refetchSignaturesFormateurs]); // Compteur pour "Mis à jour il y a X secondes" useEffect(() => { @@ -262,6 +269,7 @@ export default function FormateurEmargement() { onClick={() => { refetchInscrits(); refetchPresences(); + refetchSignaturesFormateurs(); setLastRefreshTime(new Date()); setSecondsSinceRefresh(0); }} @@ -485,6 +493,67 @@ export default function FormateurEmargement() { )} + {/* Encart des signatures formateurs */} + {selectedSequenceId && signaturesFormateurs && signaturesFormateurs.length > 0 && ( + + + Signatures Formateurs + + Signatures tactiles des formateurs pour cette séquence + + + +
+ {signaturesFormateurs.map((signature: any) => ( +
+
+
+

+ {new Date(signature.dateDebut).toLocaleDateString("fr-FR", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + })} +

+

+ {signature.periode === "matin" ? "🌅 Matin" : "🌆 Après-midi"} +

+
+ + {new Date(signature.heurePresence).toLocaleTimeString("fr-FR", { + hour: "2-digit", + minute: "2-digit", + })} + +
+ {signature.signatureUrl && ( +
+

Signature tactile :

+
{ + setSelectedSignature(signature.signatureUrl!); + setShowSignatureDialog(true); + }} + > + Signature formateur +
+
+ )} +
+ ))} +
+
+
+ )} + {/* Dialog QR Code Apprenants */} diff --git a/server/presenceDb.ts b/server/presenceDb.ts index e2cfbde..c44fe22 100644 --- a/server/presenceDb.ts +++ b/server/presenceDb.ts @@ -108,6 +108,38 @@ export async function getPresencesBySequence(sequenceId: number) { return result; } +/** + * Récupérer les signatures formateurs pour une séquence + */ +export async function getSignaturesFormateursBySequence(sequenceId: number) { + const db = await getDb(); + if (!db) throw new Error("Database not available"); + + const result = await db + .select({ + presenceId: presences.id, + formateurId: presences.formateurId, + dateFormationId: presences.dateFormationId, + periode: presences.periode, + heurePresence: presences.heurePresence, + modeValidation: presences.modeValidation, + signatureUrl: sql`${presences}.signatureUrl`, + signatureS3Key: sql`${presences}.signatureS3Key`, + dateDebut: datesFormation.dateDebut, + dateFin: datesFormation.dateFin, + }) + .from(presences) + .innerJoin(datesFormation, eq(presences.dateFormationId, datesFormation.id)) + .where( + and( + eq(datesFormation.sequenceId, sequenceId), + eq(presences.typeUtilisateur, "formateur") + ) + ); + + return result; +} + /** * Récupérer les présences pour une inscription spécifique */ diff --git a/server/routers.ts b/server/routers.ts index 88920f3..ff198e4 100644 --- a/server/routers.ts +++ b/server/routers.ts @@ -2796,6 +2796,16 @@ export const appRouter = router({ return await getPresencesBySequence(input.sequenceId); }), + // Lister les signatures formateurs pour une séquence + listSignaturesFormateurs: protectedProcedure + .input(z.object({ + sequenceId: z.number(), + })) + .query(async ({ input }) => { + const { getSignaturesFormateursBySequence } = await import("./presenceDb"); + return await getSignaturesFormateursBySequence(input.sequenceId); + }), + // Lister les présences pour une inscription listByInscription: publicProcedure .input(z.object({ diff --git a/todo.md b/todo.md index 6d4a5a6..a6072a2 100644 --- a/todo.md +++ b/todo.md @@ -1527,3 +1527,11 @@ - [ ] Déployer sur le VPS - [ ] Tester en production - [ ] Valider avec l'utilisateur + + +## Affichage des signatures formateurs sur la page d'émargement numérique + +- [x] Créer une procédure backend pour récupérer les signatures formateurs d'une séquence +- [x] Ajouter un encart dédié sur la page d'émargement numérique pour afficher les signatures formateurs +- [x] Afficher les signatures formateurs avec date/heure et période (matin/après-midi) +- [x] Déployer sur recette et production