diff --git a/client/src/pages/FormateurEmargement.tsx b/client/src/pages/FormateurEmargement.tsx index 51b2a0c..bd2afa1 100644 --- a/client/src/pages/FormateurEmargement.tsx +++ b/client/src/pages/FormateurEmargement.tsx @@ -93,6 +93,17 @@ export default function FormateurEmargement() { ); }; + // Récupérer les informations de présence pour une date et une période + const getPresenceInfo = (inscriptionId: number, dateFormationId: number, periode: "matin" | "apres_midi") => { + return presences?.find( + (p) => p.inscriptionId === inscriptionId && p.dateFormationId === dateFormationId && p.periode === periode + ); + }; + + // État pour la modale de signature + const [selectedSignature, setSelectedSignature] = useState(null); + const [showSignatureDialog, setShowSignatureDialog] = useState(false); + if (loadingSequences) { return (
@@ -247,13 +258,46 @@ export default function FormateurEmargement() { {/* Matin */}
-
+
{presentMatin ? ( ) : ( )} 🌅 Matin + {(() => { + const presenceInfo = getPresenceInfo(inscrit.inscription.id, date.id, "matin"); + if (presenceInfo) { + return ( +
+ • {inscrit.apprenant?.prenom} {inscrit.apprenant?.nom} + • {new Date(presenceInfo.heurePresence).toLocaleString("fr-FR", { + day: "2-digit", + month: "2-digit", + year: "numeric", + hour: "2-digit", + minute: "2-digit" + })} + {presenceInfo.signatureUrl && ( + + )} +
+ ); + } + return null; + })()}
{!presentMatin && ( + )} +
+ ); + } + return null; + })()}
{!presentApresMidi && (
); diff --git a/server/presenceDb.ts b/server/presenceDb.ts index b9be3df..f990238 100644 --- a/server/presenceDb.ts +++ b/server/presenceDb.ts @@ -100,6 +100,9 @@ export async function getPresencesByInscription(inscriptionId: number) { modeValidation: presences.modeValidation, validateurId: presences.validateurId, commentaire: presences.commentaire, + signatureUrl: presences.signatureUrl, + signatureS3Key: presences.signatureS3Key, + dateSigned: presences.dateSigned, dateDebut: datesFormation.dateDebut, dateFin: datesFormation.dateFin, }) diff --git a/server/routers.ts b/server/routers.ts index ca6d097..9fc311a 100644 --- a/server/routers.ts +++ b/server/routers.ts @@ -2485,10 +2485,11 @@ export const appRouter = router({ const { sequences, inscriptions } = await import("../drizzle/schema"); const { eq } = await import("drizzle-orm"); + const db = await getDb(); + if (!db) throw new Error("Database not available"); + // Si mode QR code, vérifier le token if (input.modeValidation === "qrcode" && input.token) { - const db = await getDb(); - if (!db) throw new Error("Database not available"); // Récupérer l'inscription pour vérifier la séquence const inscription = await db.select().from(inscriptions).where(eq(inscriptions.id, input.inscriptionId)).limit(1); diff --git a/todo.md b/todo.md index e2fc04d..f544765 100644 --- a/todo.md +++ b/todo.md @@ -1306,8 +1306,16 @@ ## Stockage local des signatures d'émargement -- [ ] Modifier routers.ts pour stocker les signatures localement au lieu de S3 -- [ ] Créer la structure de dossiers uploads/signatures/[formation-id]-[sequence-id]/ -- [ ] Générer des noms de fichiers uniques pour chaque signature +- [x] Modifier routers.ts pour stocker les signatures localement au lieu de S3 +- [x] Créer la structure de dossiers uploads/signatures/[formation-id]-[sequence-id]/ +- [x] Générer des noms de fichiers uniques pour chaque signature - [ ] Configurer Nginx pour servir les fichiers de signatures - [ ] Tester le flux complet sur le VPS + +## Affichage des signatures dans l'émargement numérique + +- [ ] Modifier l'API pour récupérer les présences avec signatures +- [ ] Afficher nom du signataire, date/heure et icône à côté de chaque puce matin/après-midi +- [ ] Ajouter une modale pour visualiser la signature en grand format +- [ ] Tester l'affichage sur smartphone +- [ ] Déployer sur le VPS