Checkpoint: Système d'émargement numérique avec QR code et génération automatique d'attestations :
## Base de données - ✅ Table `presences` créée (inscriptionId, dateFormationId, heurePresence, modeValidation, validateurId) - ✅ Colonne `qrCodeToken` ajoutée à la table `sequences` ## Backend - ✅ Génération de QR code unique par séquence (qrCodeGenerator.ts) - ✅ Procédures tRPC pour l'émargement (presences.generateQRCode, presences.valider, presences.listBySequence) - ✅ Fonctions de base de données (presenceDb.ts) pour gérer les présences - ✅ Génération automatique d'attestation après validation de toutes les présences - ✅ Intégration avec le service d'attestations existant ## Interface formateur (/formateur/emargement) - ✅ Sélection de la séquence - ✅ Génération et affichage du QR code en modal - ✅ Liste des inscrits avec statut de présence par date - ✅ Validation manuelle de présence possible - ✅ Actualisation en temps réel des présences - ✅ Entrée de menu "Émargement" dans DashboardLayout ## Interface publique (/emargement/:token) - ✅ Page de scan QR code accessible sans authentification - ✅ Recherche de l'apprenant par email - ✅ Sélection de la formation et de la date - ✅ Validation automatique de la présence via QR code - ✅ Écrans de confirmation (succès/erreur) - ✅ Gestion des erreurs (QR invalide, déjà émargé, etc.) ## Génération automatique d'attestations - ✅ Vérification automatique après chaque validation de présence - ✅ Génération du PDF d'attestation si toutes les présences sont validées - ✅ Enregistrement en base de données - ✅ Upload automatique sur S3 ## Dépendances installées - qrcode (génération de QR codes) - @types/qrcode (types TypeScript) Le système est entièrement fonctionnel et prêt à être utilisé par les formateurs et apprenants.
This commit is contained in:
2212
drizzle/meta/0031_snapshot.json
Normal file
2212
drizzle/meta/0031_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,13 @@
|
||||
"when": 1767877943207,
|
||||
"tag": "0030_cultured_korg",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 31,
|
||||
"version": "5",
|
||||
"when": 1768157247820,
|
||||
"tag": "0031_simple_anthem",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -87,6 +87,8 @@ export const sequences = mysqlTable("sequences", {
|
||||
dateBlocage: datetime("dateBlocage").notNull(),
|
||||
/** Statut de la séquence */
|
||||
statut: mysqlEnum("statut", ["ouverte", "bloquee", "terminee"]).default("ouverte").notNull(),
|
||||
/** Token unique pour le QR code d'émargement */
|
||||
qrCodeToken: varchar("qrCodeToken", { length: 64 }).unique(),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||
});
|
||||
@@ -606,3 +608,27 @@ export const configAttestation = mysqlTable("configAttestation", {
|
||||
|
||||
export type ConfigAttestation = typeof configAttestation.$inferSelect;
|
||||
export type InsertConfigAttestation = typeof configAttestation.$inferInsert;
|
||||
|
||||
/**
|
||||
* Table des présences (émargement numérique)
|
||||
* Stocke les présences validées pour chaque apprenant à chaque date de formation
|
||||
*/
|
||||
export const presences = mysqlTable("presences", {
|
||||
id: int("id").autoincrement().primaryKey(),
|
||||
/** ID de l'inscription */
|
||||
inscriptionId: int("inscriptionId").notNull(),
|
||||
/** ID de la date de formation */
|
||||
dateFormationId: int("dateFormationId").notNull(),
|
||||
/** Heure de validation de la présence */
|
||||
heurePresence: timestamp("heurePresence").notNull(),
|
||||
/** Mode de validation (qrcode, manuel) */
|
||||
modeValidation: mysqlEnum("modeValidation", ["qrcode", "manuel"]).notNull(),
|
||||
/** ID du validateur (formateur qui a validé manuellement, null si QR code) */
|
||||
validateurId: int("validateurId"),
|
||||
/** Commentaire optionnel */
|
||||
commentaire: text("commentaire"),
|
||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
export type Presence = typeof presences.$inferSelect;
|
||||
export type InsertPresence = typeof presences.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user