- Ajout du champ typeEnvoi (enum 'automatique', 'test') dans le schéma Drizzle de la table logsRappels - Ajout de la fonction createLogRappel dans server/db.ts pour enregistrer les logs - Modification de la procédure testerRappel pour enregistrer chaque envoi de test avec typeEnvoi='test' - Enregistrement des succès et des échecs dans l'historique - Les tests n'interfèrent pas avec les rappels automatiques (typeEnvoi='automatique') - Migration SQL appliquée sur le VPS : ALTER TABLE logsRappels ADD COLUMN typeEnvoi - Déployé sur le VPS de production (https://formations.itinova.org) - Mise à jour du fichier todo.md Les envois de test apparaissent maintenant dans l'historique des rappels et sont distingués des envois automatiques programmés.
564 lines
24 KiB
TypeScript
564 lines
24 KiB
TypeScript
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, rappel, rappelJ1, rappel3, rappel4, rappel5, rappel6, reset_password) */
|
|
type: mysqlEnum("type", ["inscription", "teaser", "rappel", "rappelJ1", "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 (rappel, rappelJ1, rappel3, rappel4, rappel5, rappel6) */
|
|
templateType: mysqlEnum("templateType", ["rappel", "rappelJ1", "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"),
|
|
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 (rappel, rappelJ1, rappel3, rappel4, rappel5, rappel6) */
|
|
type: mysqlEnum("type", ["rappel", "rappelJ1", "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(),
|
|
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"),
|
|
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"),
|
|
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", [
|
|
"inscription",
|
|
"annulation",
|
|
"capacite_atteinte",
|
|
"place_disponible",
|
|
"remerciement",
|
|
"formateur_inscription",
|
|
"formateur_annulation"
|
|
]).notNull(),
|
|
/** Email du destinataire */
|
|
destinataire: varchar("destinataire", { length: 320 }).notNull(),
|
|
/** ID de la séquence concernée */
|
|
sequenceId: int("sequenceId"),
|
|
/** ID de l'apprenant concerné */
|
|
apprenantId: int("apprenantId"),
|
|
/** Statut de l'envoi */
|
|
statut: mysqlEnum("statut", ["succes", "echec"]).notNull(),
|
|
/** Message d'erreur en cas d'échec */
|
|
messageErreur: text("messageErreur"),
|
|
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;
|
|
|