import { boolean, datetime, int, mysqlEnum, mysqlTable, text, timestamp, varchar } 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 }), loginMethod: varchar("loginMethod", { length: 64 }), role: mysqlEnum("role", ["user", "admin"]).default("user").notNull(), /** Statut du compte (actif/inactif) */ isActive: boolean("isActive").default(true).notNull(), 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 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(), 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 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 }).notNull(), fonction: mysqlEnum("fonction", ["directeur", "chef_service", "autre"]).notNull(), 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 séquences de formation * Une séquence peut contenir 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 de la formation */ lieu: varchar("lieu", { length: 500 }).notNull(), /** Public cible de la séquence (Directeur, Chef de service, Autre) */ publicCible: mysqlEnum("publicCible", ["directeur", "chef_service", "autre"]).notNull(), /** Capacité maximale (12 par défaut) */ capaciteMax: int("capaciteMax").default(12).notNull(), /** Date de blocage des inscriptions (J-15 avant la première date) */ dateBlocage: datetime("dateBlocage").notNull(), /** Statut de la séquence */ statut: mysqlEnum("statut", ["ouverte", "bloquee", "terminee"]).default("ouverte").notNull(), 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(), /** Date de début de cette journée de formation */ dateDebut: datetime("dateDebut").notNull(), /** Date de fin de cette journée de formation */ dateFin: datetime("dateFin").notNull(), /** Ordre de la date dans la séquence (1 à 4) */ ordre: int("ordre").notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), }); export type DateFormation = typeof datesFormation.$inferSelect; export type InsertDateFormation = typeof datesFormation.$inferInsert; /** * Table des inscriptions * L'inscription à une séquence inscrit automatiquement à toutes ses dates */ export const inscriptions = mysqlTable("inscriptions", { id: int("id").autoincrement().primaryKey(), apprenantId: int("apprenantId").notNull(), sequenceId: int("sequenceId").notNull(), /** Statut de l'inscription */ statut: mysqlEnum("statut", ["confirmee", "liste_attente", "annulee"]).notNull(), dateInscription: timestamp("dateInscription").defaultNow().notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Inscription = typeof inscriptions.$inferSelect; export type InsertInscription = typeof inscriptions.$inferInsert; /** * Table des tokens de réinitialisation de mot de passe * Stocke les tokens temporaires générés pour la réinitialisation */ export const passwordResetTokens = mysqlTable("passwordResetTokens", { id: int("id").autoincrement().primaryKey(), userId: int("userId").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 génération) */ expiresAt: timestamp("expiresAt").notNull(), /** Indique si le token a é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 templates d'emails personnalisables */ export const emailTemplates = mysqlTable("emailTemplates", { id: int("id").autoincrement().primaryKey(), /** Type de template (inscription, teaser, rappel, reset_password) */ type: varchar("type", { length: 50 }).notNull().unique(), /** Nom affiché du template */ name: varchar("name", { length: 255 }).notNull(), /** URL du logo (peut être vide pour utiliser le logo par défaut) */ logoUrl: text("logoUrl"), /** Couleur principale (hex) */ primaryColor: varchar("primaryColor", { length: 7 }).default("#2563eb").notNull(), /** Couleur de fond de l'en-tête (hex) */ headerBgColor: varchar("headerBgColor", { length: 7 }).default("#2563eb").notNull(), /** Couleur du texte de l'en-tête (hex) */ headerTextColor: varchar("headerTextColor", { length: 7 }).default("#ffffff").notNull(), /** Titre de l'en-tête */ headerTitle: varchar("headerTitle", { length: 255 }).default("Formation Manager Itinova").notNull(), /** Texte du pied de page */ footerText: text("footerText"), /** Actif ou non */ active: boolean("active").default(true).notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type EmailTemplate = typeof emailTemplates.$inferSelect; export type InsertEmailTemplate = typeof emailTemplates.$inferInsert; /** * Table de configuration SMTP/Email * Stocke la configuration pour l'envoi d'emails (Resend, etc.) */ export const emailConfig = mysqlTable("emailConfig", { id: int("id").autoincrement().primaryKey(), /** Service d'envoi (resend, smtp) */ provider: varchar("provider", { length: 50 }).default("resend").notNull(), /** Clé API Resend (chiffrée) */ apiKey: text("apiKey"), /** 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: simulation ou production */ mode: mysqlEnum("mode", ["simulation", "production"]).default("simulation").notNull(), /** Domaine vérifié (pour Resend) */ domainVerified: boolean("domainVerified").default(false).notNull(), /** Actif ou non */ 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 formateurs * Stocke la liste des formateurs disponibles pour animer les séquences */ export const formateurs = mysqlTable("formateurs", { id: int("id").autoincrement().primaryKey(), /** Nom complet du formateur */ nom: varchar("nom", { length: 255 }).notNull(), createdAt: timestamp("createdAt").defaultNow().notNull(), updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(), }); export type Formateur = typeof formateurs.$inferSelect; export type InsertFormateur = typeof formateurs.$inferInsert;