Checkpoint: Application complète de gestion des formations Manager Itinova avec toutes les fonctionnalités : gestion des formations/sessions/apprenants, système d'inscription avec validations, génération d'invitations Outlook, emails automatiques, exports PDF/Excel, et documentation complète.

This commit is contained in:
Manus Sandbox
2025-11-12 08:38:33 -05:00
parent 641200f3a4
commit 0fc35596d9
25 changed files with 4391 additions and 52 deletions

View File

@@ -0,0 +1,13 @@
CREATE TABLE `users` (
`id` int AUTO_INCREMENT NOT NULL,
`openId` varchar(64) NOT NULL,
`name` text,
`email` varchar(320),
`loginMethod` varchar(64),
`role` enum('user','admin') NOT NULL DEFAULT 'user',
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
`lastSignedIn` timestamp NOT NULL DEFAULT (now()),
CONSTRAINT `users_id` PRIMARY KEY(`id`),
CONSTRAINT `users_openId_unique` UNIQUE(`openId`)
);

View File

@@ -0,0 +1,54 @@
CREATE TABLE `apprenants` (
`id` int AUTO_INCREMENT NOT NULL,
`nom` varchar(255) NOT NULL,
`prenom` varchar(255) NOT NULL,
`email` varchar(320) NOT NULL,
`codeEtablissement` varchar(50) NOT NULL,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `apprenants_id` PRIMARY KEY(`id`),
CONSTRAINT `apprenants_email_unique` UNIQUE(`email`)
);
--> statement-breakpoint
CREATE TABLE `formations` (
`id` int AUTO_INCREMENT NOT NULL,
`nom` varchar(255) NOT NULL,
`description` text,
`lienUnique` varchar(100) NOT NULL,
`actif` boolean NOT NULL DEFAULT true,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `formations_id` PRIMARY KEY(`id`),
CONSTRAINT `formations_lienUnique_unique` UNIQUE(`lienUnique`)
);
--> statement-breakpoint
CREATE TABLE `inscriptions` (
`id` int AUTO_INCREMENT NOT NULL,
`apprenantId` int NOT NULL,
`sessionId` int NOT NULL,
`statut` enum('confirmee','liste_attente','annulee') NOT NULL DEFAULT 'confirmee',
`dateInscription` timestamp NOT NULL DEFAULT (now()),
`invitationEnvoyee` boolean NOT NULL DEFAULT false,
`emailConfirmationEnvoye` boolean NOT NULL DEFAULT false,
`emailTeaserEnvoye` boolean NOT NULL DEFAULT false,
`emailRappelEnvoye` boolean NOT NULL DEFAULT false,
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `inscriptions_id` PRIMARY KEY(`id`),
CONSTRAINT `inscriptions_apprenantId_sessionId_unique` UNIQUE(`apprenantId`,`sessionId`)
);
--> statement-breakpoint
CREATE TABLE `sessions` (
`id` int AUTO_INCREMENT NOT NULL,
`formationId` int NOT NULL,
`nom` varchar(255) NOT NULL,
`dateDebut` datetime NOT NULL,
`dateFin` datetime NOT NULL,
`lieu` varchar(500) NOT NULL,
`capaciteMax` int NOT NULL DEFAULT 12,
`dateBlocage` datetime NOT NULL,
`statut` enum('ouverte','bloquee','terminee') NOT NULL DEFAULT 'ouverte',
`createdAt` timestamp NOT NULL DEFAULT (now()),
`updatedAt` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP,
CONSTRAINT `sessions_id` PRIMARY KEY(`id`)
);

View File

@@ -0,0 +1,110 @@
{
"version": "5",
"dialect": "mysql",
"id": "72faa14d-1c17-40c3-99c8-b471711804bc",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -0,0 +1,473 @@
{
"version": "5",
"dialect": "mysql",
"id": "95d14d84-eb09-4770-af36-02daa58e4a5d",
"prevId": "72faa14d-1c17-40c3-99c8-b471711804bc",
"tables": {
"apprenants": {
"name": "apprenants",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"prenom": {
"name": "prenom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"codeEtablissement": {
"name": "codeEtablissement",
"type": "varchar(50)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"apprenants_id": {
"name": "apprenants_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"apprenants_email_unique": {
"name": "apprenants_email_unique",
"columns": [
"email"
]
}
},
"checkConstraint": {}
},
"formations": {
"name": "formations",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"lienUnique": {
"name": "lienUnique",
"type": "varchar(100)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"actif": {
"name": "actif",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": true
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"formations_id": {
"name": "formations_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"formations_lienUnique_unique": {
"name": "formations_lienUnique_unique",
"columns": [
"lienUnique"
]
}
},
"checkConstraint": {}
},
"inscriptions": {
"name": "inscriptions",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"apprenantId": {
"name": "apprenantId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"sessionId": {
"name": "sessionId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('confirmee','liste_attente','annulee')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'confirmee'"
},
"dateInscription": {
"name": "dateInscription",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"invitationEnvoyee": {
"name": "invitationEnvoyee",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"emailConfirmationEnvoye": {
"name": "emailConfirmationEnvoye",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"emailTeaserEnvoye": {
"name": "emailTeaserEnvoye",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"emailRappelEnvoye": {
"name": "emailRappelEnvoye",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": false
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"inscriptions_id": {
"name": "inscriptions_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"inscriptions_apprenantId_sessionId_unique": {
"name": "inscriptions_apprenantId_sessionId_unique",
"columns": [
"apprenantId",
"sessionId"
]
}
},
"checkConstraint": {}
},
"sessions": {
"name": "sessions",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"formationId": {
"name": "formationId",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"nom": {
"name": "nom",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"dateDebut": {
"name": "dateDebut",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"dateFin": {
"name": "dateFin",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"lieu": {
"name": "lieu",
"type": "varchar(500)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"capaciteMax": {
"name": "capaciteMax",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 12
},
"dateBlocage": {
"name": "dateBlocage",
"type": "datetime",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"statut": {
"name": "statut",
"type": "enum('ouverte','bloquee','terminee')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'ouverte'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"sessions_id": {
"name": "sessions_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": true
},
"openId": {
"name": "openId",
"type": "varchar(64)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(320)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"loginMethod": {
"name": "loginMethod",
"type": "varchar(64)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"role": {
"name": "role",
"type": "enum('user','admin')",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"createdAt": {
"name": "createdAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"updatedAt": {
"name": "updatedAt",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"onUpdate": true,
"default": "(now())"
},
"lastSignedIn": {
"name": "lastSignedIn",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"users_id": {
"name": "users_id",
"columns": [
"id"
]
}
},
"uniqueConstraints": {
"users_openId_unique": {
"name": "users_openId_unique",
"columns": [
"openId"
]
}
},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}

View File

@@ -1,5 +1,20 @@
{
"version": "7",
"dialect": "mysql",
"entries": []
}
"entries": [
{
"idx": 0,
"version": "5",
"when": 1762953852020,
"tag": "0000_charming_grey_gargoyle",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1762953926304,
"tag": "0001_smooth_warbird",
"breakpoints": true
}
]
}

View File

@@ -1,4 +1,4 @@
import { int, mysqlEnum, mysqlTable, text, timestamp, varchar } from "drizzle-orm/mysql-core";
import { int, mysqlEnum, mysqlTable, text, timestamp, varchar, boolean, datetime, unique } from "drizzle-orm/mysql-core";
/**
* Core user table backing auth flow.
@@ -25,4 +25,90 @@ export const users = mysqlTable("users", {
export type User = typeof users.$inferSelect;
export type InsertUser = typeof users.$inferInsert;
// TODO: Add your tables here
/**
* 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(),
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 sessions de formation
*/
export const sessions = mysqlTable("sessions", {
id: int("id").autoincrement().primaryKey(),
formationId: int("formationId").notNull(),
nom: varchar("nom", { length: 255 }).notNull(),
/** Date de début de la session */
dateDebut: datetime("dateDebut").notNull(),
/** Date de fin de la session */
dateFin: datetime("dateFin").notNull(),
/** Lieu de la formation */
lieu: varchar("lieu", { length: 500 }).notNull(),
/** Capacité maximale (12 par défaut) */
capaciteMax: int("capaciteMax").default(12).notNull(),
/** Date de blocage des inscriptions (J-15) */
dateBlocage: datetime("dateBlocage").notNull(),
/** Statut de la session */
statut: mysqlEnum("statut", ["ouverte", "bloquee", "terminee"]).default("ouverte").notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
});
export type Session = typeof sessions.$inferSelect;
export type InsertSession = typeof sessions.$inferInsert;
/**
* Table des inscriptions
*/
export const inscriptions = mysqlTable("inscriptions", {
id: int("id").autoincrement().primaryKey(),
apprenantId: int("apprenantId").notNull(),
sessionId: int("sessionId").notNull(),
/** Statut de l'inscription */
statut: mysqlEnum("statut", ["confirmee", "liste_attente", "annulee"]).default("confirmee").notNull(),
/** Date d'inscription */
dateInscription: timestamp("dateInscription").defaultNow().notNull(),
/** Invitation Outlook envoyée */
invitationEnvoyee: boolean("invitationEnvoyee").default(false).notNull(),
/** Email de confirmation envoyé */
emailConfirmationEnvoye: boolean("emailConfirmationEnvoye").default(false).notNull(),
/** Email teaser envoyé */
emailTeaserEnvoye: boolean("emailTeaserEnvoye").default(false).notNull(),
/** Email rappel J-7 envoyé */
emailRappelEnvoye: boolean("emailRappelEnvoye").default(false).notNull(),
createdAt: timestamp("createdAt").defaultNow().notNull(),
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
}, (table) => ({
// Contrainte d'unicité pour éviter les doubles inscriptions
uniqueInscription: unique().on(table.apprenantId, table.sessionId),
}));
export type Inscription = typeof inscriptions.$inferSelect;
export type InsertInscription = typeof inscriptions.$inferInsert;