255 lines
11 KiB
TypeScript
255 lines
11 KiB
TypeScript
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 }),
|
|
/** 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"]).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(),
|
|
/** ID du formateur assigné à cette séquence */
|
|
formateurId: int("formateurId"),
|
|
/** 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"),
|
|
/** Contenu du corps de l'email (HTML ou texte) */
|
|
bodyContent: text("bodyContent"),
|
|
/** 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;
|
|
|
|
/**
|
|
* Table des alertes automatiques
|
|
* Configure les alertes à envoyer selon certaines conditions
|
|
*/
|
|
export const alertes = mysqlTable("alertes", {
|
|
id: int("id").autoincrement().primaryKey(),
|
|
/** Nom de l'alerte */
|
|
nom: varchar("nom", { length: 255 }).notNull(),
|
|
/** Type d'alerte (taux_remplissage, seuil_inscriptions) */
|
|
type: mysqlEnum("type", ["taux_remplissage", "seuil_inscriptions"]).notNull(),
|
|
/** Condition de déclenchement (ex: ">=", ">", "<=", "<", "=") */
|
|
condition: varchar("condition", { length: 10 }).notNull(),
|
|
/** Valeur seuil pour déclencher l'alerte */
|
|
valeurSeuil: int("valeurSeuil").notNull(),
|
|
/** Code établissement concerné (null = tous) */
|
|
codeEtablissement: varchar("codeEtablissement", { length: 50 }),
|
|
/** Email(s) destinataire(s) séparés par des virgules */
|
|
emailsDestinataires: text("emailsDestinataires").notNull(),
|
|
/** Actif ou non */
|
|
actif: boolean("actif").default(true).notNull(),
|
|
/** Dernière vérification */
|
|
derniereVerification: timestamp("derniereVerification"),
|
|
/** Dernier envoi d'alerte */
|
|
dernierEnvoi: timestamp("dernierEnvoi"),
|
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
|
});
|
|
|
|
export type Alerte = typeof alertes.$inferSelect;
|
|
export type InsertAlerte = typeof alertes.$inferInsert;
|