import { boolean, int, mysqlEnum, mysqlTable, text, timestamp, varchar, datetime, decimal } from "drizzle-orm/mysql-core"; /** * Core user table backing auth flow. * Extend this file with additional tables as your product grows. * Columns use camelCase to match both database fields and generated types. */ export const users = mysqlTable("users", { /** * Surrogate primary key. Auto-incremented numeric value managed by the database. * Use this for relations between tables. */ id: int("id").autoincrement().primaryKey(), /** Manus OAuth identifier (openId) returned from the OAuth callback. Unique per user. */ openId: varchar("openId", { length: 64 }).notNull().unique(), name: text("name"), email: varchar("email", { length: 320 }), /** Identifiant de connexion unique pour l'utilisateur */ username: varchar("username", { length: 100 }).unique(), /** Mot de passe hashé (bcrypt) */ password: varchar("password", { length: 255 }), loginMethod: varchar("loginMethod", { length: 64 }), role: mysqlEnum("role", ["user", "admin", "formateur"]).default("user").notNull(), /** Statut du compte (actif/inactif) */ isActive: boolean("isActive").default(true).notNull(), /** ID du formateur associé (si role = formateur) */ formateurId: int("formateurId"), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(), }); export type User = typeof users.$inferSelect; export type InsertUser = typeof users.$inferInsert; /** * Table des tokens de réinitialisation de mot de passe */ export const passwordResetTokens = mysqlTable("passwordResetTokens", { id: int("id").autoincrement().primaryKey(), /** Email de l'utilisateur qui demande la réinitialisation */ email: varchar("email", { length: 320 }).notNull(), /** Token unique généré pour la réinitialisation */ token: varchar("token", { length: 255 }).notNull().unique(), /** Date d'expiration du token (24h après création) */ expiresAt: timestamp("expiresAt").notNull(), /** Indique si le token a déjà été utilisé */ used: boolean("used").default(false).notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type PasswordResetToken = typeof passwordResetTokens.$inferSelect; export type InsertPasswordResetToken = typeof passwordResetTokens.$inferInsert; /** * Table des formations (ex: Manager Itinova) */ export const formations = mysqlTable("formations", { id: int("id").autoincrement().primaryKey(), nom: varchar("nom", { length: 255 }).notNull(), description: text("description"), /** Lien unique pour l'inscription à cette formation */ lienUnique: varchar("lienUnique", { length: 100 }).notNull().unique(), actif: boolean("actif").default(true).notNull(), /** Mode de génération des attestations (auto = génération automatique, manuel = import de documents) */ modeAttestation: mysqlEnum("modeAttestation", ["auto", "manuel"]).default("auto").notNull(), /** Mode d'envoi des attestations (auto = envoi automatique, manuel = envoi manuel) */ modeEnvoi: mysqlEnum("modeEnvoi", ["auto", "manuel"]).default("manuel").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Formation = typeof formations.$inferSelect; export type InsertFormation = typeof formations.$inferInsert; /** * Table des séquences de formation (ex: Groupe A, Groupe B) * Chaque séquence peut avoir jusqu'à 4 dates de formation */ export const sequences = mysqlTable("sequences", { id: int("id").autoincrement().primaryKey(), formationId: int("formationId").notNull(), nom: varchar("nom", { length: 255 }).notNull(), lieu: varchar("lieu", { length: 255 }).notNull(), capaciteMax: int("capaciteMax").notNull(), statut: mysqlEnum("statut", ["ouverte", "bloquee", "terminee", "fermee", "annulee"]).default("ouverte").notNull(), /** Public cible de la séquence (Directeur, Chef de service, Autre, Tous) */ publicCible: mysqlEnum("publicCible", ["directeur", "chef_service", "autre", "tous"]).default("tous").notNull(), /** ID du formateur assigné à cette séquence */ formateurId: int("formateurId"), /** Date limite d'inscription (blocage) */ dateBlocage: datetime("dateBlocage"), /** Token unique pour le QR code d'émargement */ qrCodeToken: varchar("qrCodeToken", { length: 100 }).unique(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Sequence = typeof sequences.$inferSelect; export type InsertSequence = typeof sequences.$inferInsert; /** * Table des dates de formation pour chaque séquence * Une séquence peut avoir jusqu'à 4 dates */ export const datesFormation = mysqlTable("datesFormation", { id: int("id").autoincrement().primaryKey(), sequenceId: int("sequenceId").notNull(), /** Ordre de la date (1, 2, 3 ou 4) */ ordre: int("ordre").notNull(), dateDebut: datetime("dateDebut").notNull(), dateFin: datetime("dateFin").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type DateFormation = typeof datesFormation.$inferSelect; export type InsertDateFormation = typeof datesFormation.$inferInsert; /** * Table des apprenants */ export const apprenants = mysqlTable("apprenants", { id: int("id").autoincrement().primaryKey(), nom: varchar("nom", { length: 255 }).notNull(), prenom: varchar("prenom", { length: 255 }).notNull(), email: varchar("email", { length: 320 }).notNull().unique(), codeEtablissement: varchar("codeEtablissement", { length: 50 }), /** Fonction de l'apprenant (Directeur, Chef de service, Autre) */ fonction: varchar("fonction", { length: 100 }), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Apprenant = typeof apprenants.$inferSelect; export type InsertApprenant = typeof apprenants.$inferInsert; /** * Table des inscriptions */ export const inscriptions = mysqlTable("inscriptions", { id: int("id").autoincrement().primaryKey(), sequenceId: int("sequenceId").notNull(), apprenantId: int("apprenantId").notNull(), statut: mysqlEnum("statut", ["confirmee", "en_attente", "annulee"]).default("confirmee").notNull(), dateInscription: timestamp("dateInscription").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Inscription = typeof inscriptions.$inferSelect; export type InsertInscription = typeof inscriptions.$inferInsert; /** * Table de configuration des emails */ export const emailConfig = mysqlTable("emailConfig", { id: int("id").autoincrement().primaryKey(), /** Provider d'envoi d'emails (resend, smtp, simulation) */ provider: mysqlEnum("provider", ["resend", "smtp", "simulation"]).default("simulation").notNull(), /** Clé API (pour Resend ou autres) */ apiKey: text("apiKey"), /** Hôte SMTP */ smtpHost: varchar("smtpHost", { length: 255 }), /** Port SMTP */ smtpPort: int("smtpPort"), /** Sécurité SMTP (TLS, SSL, None) */ smtpSecure: mysqlEnum("smtpSecure", ["none", "tls", "ssl"]), /** Utilisateur SMTP */ smtpUser: varchar("smtpUser", { length: 320 }), /** Mot de passe SMTP */ smtpPassword: text("smtpPassword"), /** Email expéditeur */ fromEmail: varchar("fromEmail", { length: 320 }).notNull(), /** Nom de l'expéditeur */ fromName: varchar("fromName", { length: 255 }).default("Formation Manager Itinova").notNull(), /** Mode de fonctionnement (simulation ou production) */ mode: mysqlEnum("mode", ["simulation", "production"]).default("simulation").notNull(), /** Domaine vérifié */ domainVerified: boolean("domainVerified").default(false).notNull(), /** Configuration active */ active: boolean("active").default(true).notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type EmailConfig = typeof emailConfig.$inferSelect; export type InsertEmailConfig = typeof emailConfig.$inferInsert; /** * Table des templates d'emails */ export const emailTemplates = mysqlTable("emailTemplates", { id: int("id").autoincrement().primaryKey(), /** Type de template (inscription, teaser, rappel1, rappel2, rappel3, rappel4, rappel5, rappel6, reset_password) */ type: mysqlEnum("type", ["inscription", "teaser", "rappel1", "rappel2", "rappel3", "rappel4", "rappel5", "rappel6", "reset_password"]).notNull().unique(), titre: varchar("titre", { length: 255 }).notNull(), /** Corps du message avec variables dynamiques */ bodyContent: text("bodyContent").notNull(), couleurPrincipale: varchar("couleurPrincipale", { length: 7 }).default("#283581").notNull(), couleurSecondaire: varchar("couleurSecondaire", { length: 7 }).default("#0578BE").notNull(), piedDePage: text("piedDePage"), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type EmailTemplate = typeof emailTemplates.$inferSelect; export type InsertEmailTemplate = typeof emailTemplates.$inferInsert; /** * Table des formateurs */ export const formateurs = mysqlTable("formateurs", { id: int("id").autoincrement().primaryKey(), nom: varchar("nom", { length: 255 }).notNull(), email: varchar("email", { length: 320 }), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Formateur = typeof formateurs.$inferSelect; export type InsertFormateur = typeof formateurs.$inferInsert; /** * Table des rappels automatiques */ export const rappels = mysqlTable("rappels", { id: int("id").autoincrement().primaryKey(), nom: varchar("nom", { length: 255 }).notNull(), /** Type de template d'email à utiliser (rappel1, rappel2, rappel3, rappel4, rappel5, rappel6) */ templateType: mysqlEnum("templateType", ["rappel1", "rappel2", "rappel3", "rappel4", "rappel5", "rappel6"]).notNull(), /** Nombre de jours avant la séquence */ joursAvant: int("joursAvant").notNull(), /** Heure d'envoi (format HH:mm) */ heureEnvoi: varchar("heureEnvoi", { length: 5 }).default("09:00").notNull(), /** Timing du rappel (pre_formation ou post_formation) */ timing: mysqlEnum("timing", ["pre_formation", "post_formation"]).default("pre_formation").notNull(), /** Nom du fichier joint (optionnel) */ nomFichier: varchar("nomFichier", { length: 255 }), /** URL du fichier sur S3 (optionnel) */ urlFichier: varchar("urlFichier", { length: 500 }), /** Clé S3 du fichier (optionnel) */ s3Key: varchar("s3Key", { length: 500 }), /** Type MIME du fichier (optionnel) */ typeFichier: varchar("typeFichier", { length: 100 }), /** Taille du fichier en octets (optionnel) */ tailleFichier: int("tailleFichier"), /** Nom du fichier joint 2 (optionnel) */ nomFichier2: varchar("nomFichier2", { length: 255 }), /** URL du fichier 2 sur S3 (optionnel) */ urlFichier2: varchar("urlFichier2", { length: 500 }), /** Clé S3 du fichier 2 (optionnel) */ s3Key2: varchar("s3Key2", { length: 500 }), /** Type MIME du fichier 2 (optionnel) */ typeFichier2: varchar("typeFichier2", { length: 100 }), /** Taille du fichier 2 en octets (optionnel) */ tailleFichier2: int("tailleFichier2"), actif: boolean("actif").default(true).notNull(), derniereExecution: datetime("derniereExecution"), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Rappel = typeof rappels.$inferSelect; export type InsertRappel = typeof rappels.$inferInsert; /** * Table de liaison entre rappels et dates de formation * Si aucune date n'est associée, le rappel s'applique à toutes les dates */ export const rappelDates = mysqlTable("rappelDates", { id: int("id").autoincrement().primaryKey(), rappelId: int("rappelId").notNull(), dateFormationId: int("dateFormationId").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type RappelDate = typeof rappelDates.$inferSelect; export type InsertRappelDate = typeof rappelDates.$inferInsert; /** * Table des logs de rappels envoyés */ export const logsRappels = mysqlTable("logsrappels", { id: int("id").autoincrement().primaryKey(), rappelId: int("rappelId").notNull(), sequenceId: int("sequenceId").notNull(), apprenantId: int("apprenantId").notNull(), email: varchar("email", { length: 320 }).notNull(), /** Type de rappel (rappel1, rappel2, rappel3, rappel4, rappel5, rappel6) */ type: mysqlEnum("type", ["rappel1", "rappel2", "rappel3", "rappel4", "rappel5", "rappel6"]).notNull(), /** Type d'envoi (automatique, test) - les tests ne bloquent pas les envois automatiques */ typeEnvoi: mysqlEnum("typeEnvoi", ["automatique", "test"]).default("automatique").notNull(), statut: mysqlEnum("statut", ["succes", "echec"]).notNull(), messageErreur: text("messageErreur"), /** Nombre de tentatives d'envoi */ nbTentatives: int("nbTentatives").default(1).notNull(), /** Date du prochain essai en cas d'échec */ prochainEssai: datetime("prochainEssai"), dateEnvoi: timestamp("dateEnvoi").defaultNow().notNull(), }); export type LogRappel = typeof logsRappels.$inferSelect; export type InsertLogRappel = typeof logsRappels.$inferInsert; /** * Table des questionnaires de satisfaction et d'évaluation */ export const questionnaires = mysqlTable("questionnaires", { id: int("id").autoincrement().primaryKey(), titre: varchar("titre", { length: 255 }).notNull(), description: text("description"), /** Type de questionnaire (satisfaction, evaluation_pre, evaluation_post) */ type: mysqlEnum("type", ["satisfaction", "evaluation_pre", "evaluation_post"]).notNull(), actif: boolean("actif").default(true).notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Questionnaire = typeof questionnaires.$inferSelect; export type InsertQuestionnaire = typeof questionnaires.$inferInsert; /** * Table des questions d'un questionnaire */ export const questions = mysqlTable("questions", { id: int("id").autoincrement().primaryKey(), questionnaireId: int("questionnaireId").notNull(), texte: text("texte").notNull(), /** Type de question (choix_multiple, echelle, texte_libre, oui_non) */ typeQuestion: mysqlEnum("typeQuestion", ["choix_multiple", "echelle", "texte_libre", "oui_non"]).notNull(), /** Options pour les questions à choix multiples (JSON) */ options: text("options"), /** Valeur minimale pour les échelles */ valeurMin: int("valeurMin"), /** Valeur maximale pour les échelles */ valeurMax: int("valeurMax"), obligatoire: boolean("obligatoire").default(false).notNull(), ordre: int("ordre").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type Question = typeof questions.$inferSelect; export type InsertQuestion = typeof questions.$inferInsert; /** * Table des réponses aux questionnaires */ export const reponsesQuestionnaires = mysqlTable("reponsesQuestionnaires", { id: int("id").autoincrement().primaryKey(), questionnaireId: int("questionnaireId").notNull(), sequenceId: int("sequenceId").notNull(), apprenantId: int("apprenantId").notNull(), dateReponse: timestamp("dateReponse").defaultNow().notNull(), }); export type ReponseQuestionnaire = typeof reponsesQuestionnaires.$inferSelect; export type InsertReponseQuestionnaire = typeof reponsesQuestionnaires.$inferInsert; /** * Table des réponses individuelles aux questions */ export const reponsesQuestions = mysqlTable("reponsesQuestions", { id: int("id").autoincrement().primaryKey(), reponseQuestionnaireId: int("reponseQuestionnaireId").notNull(), questionId: int("questionId").notNull(), /** Réponse textuelle */ reponseTexte: text("reponseTexte"), /** Réponse numérique (pour échelles et oui/non) */ reponseNumerique: int("reponseNumerique"), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type ReponseQuestion = typeof reponsesQuestions.$inferSelect; export type InsertReponseQuestion = typeof reponsesQuestions.$inferInsert; /** * Table des envois de questionnaires */ export const envoisQuestionnaires = mysqlTable("envoisQuestionnaires", { id: int("id").autoincrement().primaryKey(), questionnaireId: int("questionnaireId").notNull(), sequenceId: int("sequenceId").notNull(), apprenantId: int("apprenantId").notNull(), /** Token unique pour accéder au questionnaire */ token: varchar("token", { length: 100 }).notNull().unique(), dateEnvoi: timestamp("dateEnvoi").defaultNow().notNull(), dateReponse: timestamp("dateReponse"), statut: mysqlEnum("statut", ["envoye", "repondu"]).default("envoye").notNull(), }); export type EnvoiQuestionnaire = typeof envoisQuestionnaires.$inferSelect; export type InsertEnvoiQuestionnaire = typeof envoisQuestionnaires.$inferInsert; /** * Table des supports de formation uploadés par les formateurs */ export const supportsFormation = mysqlTable("supportsFormation", { id: int("id").autoincrement().primaryKey(), sequenceId: int("sequenceId").notNull(), formateurId: int("formateurId").notNull(), nomFichier: varchar("nomFichier", { length: 255 }).notNull(), typeFichier: varchar("typeFichier", { length: 100 }).notNull(), tailleFichier: int("tailleFichier").notNull(), urlFichier: varchar("urlFichier", { length: 500 }).notNull(), s3Key: varchar("s3Key", { length: 500 }).notNull(), description: text("description"), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type SupportFormation = typeof supportsFormation.$inferSelect; export type InsertSupportFormation = typeof supportsFormation.$inferInsert; /** * Table des présences (émargement) */ export const presences = mysqlTable("presences", { id: int("id").autoincrement().primaryKey(), inscriptionId: int("inscriptionId").notNull(), dateFormationId: int("dateFormationId").notNull(), /** Période de la journée (matin ou après-midi) */ periode: mysqlEnum("periode", ["matin", "apres_midi"]).notNull().default("matin"), heurePresence: timestamp("heurePresence").defaultNow().notNull(), /** Mode de validation (qrcode, manuel) */ modeValidation: mysqlEnum("modeValidation", ["qrcode", "manuel"]).notNull(), /** ID de l'utilisateur qui a validé (formateur ou admin) */ validateurId: int("validateurId"), commentaire: text("commentaire"), /** URL de la signature manuscrite de l'apprenant */ signatureUrl: varchar("signatureUrl", { length: 500 }), /** Clé S3 de la signature manuscrite */ signatureS3Key: varchar("signatureS3Key", { length: 500 }), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type Presence = typeof presences.$inferSelect; export type InsertPresence = typeof presences.$inferInsert; /** * Table des attestations de formation */ export const attestations = mysqlTable("attestations", { id: int("id").autoincrement().primaryKey(), apprenantId: int("apprenantId").notNull(), sequenceId: int("sequenceId").notNull(), inscriptionId: int("inscriptionId").notNull(), urlPdf: varchar("urlPdf", { length: 500 }), s3Key: varchar("s3Key", { length: 500 }), /** URL du document uploadé manuellement */ documentUrl: varchar("documentUrl", { length: 500 }), /** Clé S3 du document uploadé manuellement */ documentS3Key: varchar("documentS3Key", { length: 500 }), /** ID de l'utilisateur qui a uploadé le document */ uploadedBy: int("uploadedBy"), /** Date d'upload du document */ uploadedAt: timestamp("uploadedAt"), /** Email envoyé ou non */ emailEnvoye: boolean("emailEnvoye").default(false).notNull(), /** Date d'envoi de l'email */ dateEnvoiEmail: timestamp("dateEnvoiEmail"), /** URL de la signature manuscrite */ signatureUrl: varchar("signatureUrl", { length: 500 }), /** Clé S3 de la signature manuscrite */ signatureS3Key: varchar("signatureS3Key", { length: 500 }), /** Date de signature */ dateSigned: timestamp("dateSigned"), dateGeneration: timestamp("dateGeneration").defaultNow().notNull(), }); export type Attestation = typeof attestations.$inferSelect; export type InsertAttestation = typeof attestations.$inferInsert; /** * Table de configuration des attestations */ export const configAttestation = mysqlTable("configAttestation", { id: int("id").autoincrement().primaryKey(), /** Texte personnalisable de l'attestation avec variables dynamiques */ texteAttestation: text("texteAttestation").notNull(), /** Nom du signataire */ nomSignataire: varchar("nomSignataire", { length: 255 }).notNull(), /** Fonction du signataire */ fonctionSignataire: varchar("fonctionSignataire", { length: 255 }).notNull(), /** URL du logo sur S3 */ logoUrl: varchar("logoUrl", { length: 500 }), /** Clé S3 du logo */ logoS3Key: varchar("logoS3Key", { length: 500 }), /** URL de la signature sur S3 */ signatureUrl: varchar("signatureUrl", { length: 500 }), /** Clé S3 de la signature */ signatureS3Key: varchar("signatureS3Key", { length: 500 }), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type ConfigAttestation = typeof configAttestation.$inferSelect; export type InsertConfigAttestation = typeof configAttestation.$inferInsert; /** * Table des notifications envoyées */ export const logsNotifications = mysqlTable("logsNotifications", { id: int("id").autoincrement().primaryKey(), /** Type de notification */ type: mysqlEnum("type", [ "remerciement", "notification_formateur_inscription", "notification_formateur_annulation", "alerte_capacite", "notification_liste_attente" ]).notNull(), /** Email du destinataire */ emailDestinataire: varchar("emailDestinataire", { length: 320 }).notNull(), /** Sujet de l'email */ sujet: varchar("sujet", { length: 500 }).notNull(), /** ID de la séquence concernée */ sequenceId: int("sequenceId"), /** ID de l'apprenant concerné */ apprenantId: int("apprenantId"), /** ID du formateur concerné */ formateurId: int("formateurId"), /** Statut de l'envoi */ statut: mysqlEnum("statut", ["success", "failed"]).notNull(), /** Message d'erreur en cas d'échec */ messageErreur: text("messageErreur"), /** Métadonnées supplémentaires (JSON) */ metadata: text("metadata"), dateEnvoi: timestamp("dateEnvoi").defaultNow().notNull(), }); export type LogNotification = typeof logsNotifications.$inferSelect; export type InsertLogNotification = typeof logsNotifications.$inferInsert; /** * Table des paramètres de l'application * Stocke les paramètres globaux de configuration */ export const parametres = mysqlTable("parametres", { id: int("id").autoincrement().primaryKey(), /** URL publique de l'application pour les QR codes d'émargement */ urlPublique: varchar("urlPublique", { length: 500 }).notNull().default("https://formations.itinova.org"), /** Délai d'expiration des QR codes en minutes (par défaut 30 minutes) */ delaiExpirationQR: int("delaiExpirationQR").default(30).notNull(), /** Durée de validité des tokens d'émargement en heures (par défaut 24 heures) */ dureeValiditeToken: int("dureeValiditeToken").default(24).notNull(), /** Activer ou désactiver les notifications automatiques */ notificationsActives: boolean("notificationsActives").default(true).notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Parametre = typeof parametres.$inferSelect; export type InsertParametre = typeof parametres.$inferInsert; /** * Table de l'historique des modifications des paramètres * Permet de tracer qui a modifié quoi et quand */ export const historiqueParametres = mysqlTable("historiqueParametres", { id: int("id").autoincrement().primaryKey(), /** ID de l'utilisateur qui a effectué la modification */ userId: int("userId").notNull(), /** Nom de l'utilisateur (pour historique même si user supprimé) */ userName: varchar("userName", { length: 255 }).notNull(), /** Champ modifié */ champModifie: varchar("champModifie", { length: 100 }).notNull(), /** Ancienne valeur (JSON) */ ancienneValeur: text("ancienneValeur"), /** Nouvelle valeur (JSON) */ nouvelleValeur: text("nouvelleValeur"), dateModification: timestamp("dateModification").defaultNow().notNull(), }); export type HistoriqueParametre = typeof historiqueParametres.$inferSelect; export type InsertHistoriqueParametre = typeof historiqueParametres.$inferInsert; /** * Table de l'historique des envois d'attestations de formation * Permet de tracer tous les envois d'attestations */ export const historiqueAttestations = mysqlTable("historiqueAttestations", { id: int("id").autoincrement().primaryKey(), sequenceId: int("sequenceId").notNull(), apprenantId: int("apprenantId").notNull(), dateEnvoi: timestamp("dateEnvoi").defaultNow().notNull(), statut: mysqlEnum("statut", ["envoye", "erreur"]).notNull(), messageErreur: text("messageErreur"), urlAttestation: varchar("urlAttestation", { length: 500 }), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type HistoriqueAttestation = typeof historiqueAttestations.$inferSelect; export type InsertHistoriqueAttestation = typeof historiqueAttestations.$inferInsert;