From 732b84fee8c2c2292ac2be8fc43040833b4ef3df Mon Sep 17 00:00:00 2001
From: Manus Sandbox
Date: Thu, 13 Nov 2025 11:18:02 -0500
Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Correction=20:=20d=C3=A9placement?=
=?UTF-8?q?=20du=20champ=20"Public=20cible"=20des=20formations=20vers=20le?=
=?UTF-8?q?s=20s=C3=A9quences.=20Le=20public=20cible=20est=20maintenant=20?=
=?UTF-8?q?d=C3=A9fini=20au=20niveau=20de=20chaque=20s=C3=A9quence,=20perm?=
=?UTF-8?q?ettant=20d'avoir=20des=20s=C3=A9quences=20diff=C3=A9rentes=20po?=
=?UTF-8?q?ur=20des=20publics=20diff=C3=A9rents=20au=20sein=20d'une=20m?=
=?UTF-8?q?=C3=AAme=20formation.=20Mise=20=C3=A0=20jour=20compl=C3=A8te=20?=
=?UTF-8?q?du=20sch=C3=A9ma=20de=20base=20de=20donn=C3=A9es,=20des=20proc?=
=?UTF-8?q?=C3=A9dures=20tRPC,=20du=20formulaire=20de=20cr=C3=A9ation/?=
=?UTF-8?q?=C3=A9dition=20de=20s=C3=A9quences=20avec=20affichage=20dans=20?=
=?UTF-8?q?le=20tableau,=20et=20affichage=20du=20public=20cible=20pour=20c?=
=?UTF-8?q?haque=20s=C3=A9quence=20sur=20la=20page=20d'inscription=20publi?=
=?UTF-8?q?que.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
client/src/pages/AdminFormations.tsx | 29 +-
client/src/pages/AdminSequences.tsx | 23 ++
client/src/pages/Inscription.tsx | 8 +-
drizzle/0005_cuddly_bushwacker.sql | 2 +
drizzle/meta/0005_snapshot.json | 492 +++++++++++++++++++++++++++
drizzle/meta/_journal.json | 7 +
drizzle/schema.ts | 4 +-
server/routers.ts | 4 +-
todo.md | 8 +
9 files changed, 543 insertions(+), 34 deletions(-)
create mode 100644 drizzle/0005_cuddly_bushwacker.sql
create mode 100644 drizzle/meta/0005_snapshot.json
diff --git a/client/src/pages/AdminFormations.tsx b/client/src/pages/AdminFormations.tsx
index 8291174..14a5c27 100644
--- a/client/src/pages/AdminFormations.tsx
+++ b/client/src/pages/AdminFormations.tsx
@@ -22,7 +22,6 @@ export default function AdminFormations() {
const [formData, setFormData] = useState({
nom: "",
description: "",
- publicCible: "autre" as "directeur" | "chef_service" | "autre",
lienUnique: "",
actif: true,
});
@@ -64,7 +63,7 @@ export default function AdminFormations() {
});
const resetForm = () => {
- setFormData({ nom: "", description: "", publicCible: "autre" as "directeur" | "chef_service" | "autre", lienUnique: "", actif: true });
+ setFormData({ nom: "", description: "", lienUnique: "", actif: true });
setEditingId(null);
};
@@ -72,7 +71,6 @@ export default function AdminFormations() {
setFormData({
nom: formation.nom,
description: formation.description || "",
- publicCible: formation.publicCible || "autre",
lienUnique: formation.lienUnique,
actif: formation.actif,
});
@@ -169,22 +167,6 @@ export default function AdminFormations() {
rows={3}
/>
-
-
-
-
Nom
Description
-
Public cible
Lien d'inscription
Statut
Actions
- {filteredFormations.map((formation) => {
- const publicCibleLabel = formation.publicCible === "directeur" ? "Directeur" : formation.publicCible === "chef_service" ? "Chef de service" : "Autre";
- return (
+ {filteredFormations.map((formation) => (
{formation.nom}
{formation.description || "-"}
- {publicCibleLabel}
- );
- })}
+ ))}
) : (
diff --git a/client/src/pages/AdminSequences.tsx b/client/src/pages/AdminSequences.tsx
index d5c9ee3..bfb30f9 100644
--- a/client/src/pages/AdminSequences.tsx
+++ b/client/src/pages/AdminSequences.tsx
@@ -33,6 +33,7 @@ export default function AdminSequences() {
formationId: "",
nom: "",
lieu: "",
+ publicCible: "autre" as "directeur" | "chef_service" | "autre",
capaciteMax: "12",
dateBlocage: "",
statut: "ouverte" as "ouverte" | "bloquee" | "terminee",
@@ -85,6 +86,7 @@ export default function AdminSequences() {
formationId: "",
nom: "",
lieu: "",
+ publicCible: "autre",
capaciteMax: "12",
dateBlocage: "",
statut: "ouverte",
@@ -135,6 +137,7 @@ export default function AdminSequences() {
formationId: sequence.formationId.toString(),
nom: sequence.nom || "",
lieu: sequence.lieu || "",
+ publicCible: sequence.publicCible || "autre",
capaciteMax: sequence.capaciteMax?.toString() || "12",
dateBlocage: safeFormatDate(sequence.dateBlocage),
statut: sequence.statut || "ouverte",
@@ -159,6 +162,7 @@ export default function AdminSequences() {
formationId: parseInt(formData.formationId),
nom: formData.nom,
lieu: formData.lieu,
+ publicCible: formData.publicCible,
capaciteMax: parseInt(formData.capaciteMax),
dateBlocage: formData.dateBlocage,
statut: formData.statut,
@@ -350,6 +354,23 @@ export default function AdminSequences() {
/>
+
+
+
+
+
Nom
Dates
Lieu
+
Public cible
Capacité
Statut
Actions
@@ -581,6 +603,7 @@ export default function AdminSequences() {
{sequence.lieu}
+ {sequence.publicCible === "directeur" ? "Directeur" : sequence.publicCible === "chef_service" ? "Chef de service" : "Autre"}
{sequence.capaciteMax}
diff --git a/client/src/pages/Inscription.tsx b/client/src/pages/Inscription.tsx
index 118ebae..1bd2b3b 100644
--- a/client/src/pages/Inscription.tsx
+++ b/client/src/pages/Inscription.tsx
@@ -230,10 +230,6 @@ export default function Inscription() {
{formation.description}
)}
-
-
- Public cible : {formation.publicCible === "directeur" ? "Directeurs" : formation.publicCible === "chef_service" ? "Chefs de service" : "Autre"}
-
{/* Sessions disponibles */}
@@ -277,6 +273,10 @@ export default function Inscription() {
{sequence.lieu}
+
+
+ Public cible : {sequence.publicCible === "directeur" ? "Directeurs" : sequence.publicCible === "chef_service" ? "Chefs de service" : "Autre"}
+
{sequence.placesRestantes} places disponibles
diff --git a/drizzle/0005_cuddly_bushwacker.sql b/drizzle/0005_cuddly_bushwacker.sql
new file mode 100644
index 0000000..d89c7ad
--- /dev/null
+++ b/drizzle/0005_cuddly_bushwacker.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `sequences` ADD `publicCible` enum('directeur','chef_service','autre') NOT NULL;--> statement-breakpoint
+ALTER TABLE `formations` DROP COLUMN `publicCible`;
\ No newline at end of file
diff --git a/drizzle/meta/0005_snapshot.json b/drizzle/meta/0005_snapshot.json
new file mode 100644
index 0000000..9929baf
--- /dev/null
+++ b/drizzle/meta/0005_snapshot.json
@@ -0,0 +1,492 @@
+{
+ "version": "5",
+ "dialect": "mysql",
+ "id": "b9b19655-1104-4112-ac8d-d568013faea2",
+ "prevId": "9dc05bf2-d92e-4abf-be1e-7357b388c9a5",
+ "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
+ },
+ "fonction": {
+ "name": "fonction",
+ "type": "enum('directeur','chef_service','autre')",
+ "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": {}
+ },
+ "datesFormation": {
+ "name": "datesFormation",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": true
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "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
+ },
+ "ordre": {
+ "name": "ordre",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "createdAt": {
+ "name": "createdAt",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {
+ "datesFormation_id": {
+ "name": "datesFormation_id",
+ "columns": [
+ "id"
+ ]
+ }
+ },
+ "uniqueConstraints": {},
+ "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
+ },
+ "sequenceId": {
+ "name": "sequenceId",
+ "type": "int",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "statut": {
+ "name": "statut",
+ "type": "enum('confirmee','liste_attente','annulee')",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "dateInscription": {
+ "name": "dateInscription",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false,
+ "default": "(now())"
+ },
+ "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": {},
+ "checkConstraint": {}
+ },
+ "sequences": {
+ "name": "sequences",
+ "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
+ },
+ "lieu": {
+ "name": "lieu",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "publicCible": {
+ "name": "publicCible",
+ "type": "enum('directeur','chef_service','autre')",
+ "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": {
+ "sequences_id": {
+ "name": "sequences_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": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json
index aa3420e..2596aee 100644
--- a/drizzle/meta/_journal.json
+++ b/drizzle/meta/_journal.json
@@ -36,6 +36,13 @@
"when": 1763047968048,
"tag": "0004_futuristic_malice",
"breakpoints": true
+ },
+ {
+ "idx": 5,
+ "version": "5",
+ "when": 1763050409917,
+ "tag": "0005_cuddly_bushwacker",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/drizzle/schema.ts b/drizzle/schema.ts
index c06cea2..5d7693a 100644
--- a/drizzle/schema.ts
+++ b/drizzle/schema.ts
@@ -32,8 +32,6 @@ export const formations = mysqlTable("formations", {
id: int("id").autoincrement().primaryKey(),
nom: varchar("nom", { length: 255 }).notNull(),
description: text("description"),
- /** Public cible de la formation (Directeur, Chef de service, Autre) */
- publicCible: mysqlEnum("publicCible", ["directeur", "chef_service", "autre"]).notNull(),
/** Lien unique pour l'inscription à cette formation */
lienUnique: varchar("lienUnique", { length: 100 }).notNull().unique(),
actif: boolean("actif").default(true).notNull(),
@@ -71,6 +69,8 @@ export const sequences = mysqlTable("sequences", {
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) */
diff --git a/server/routers.ts b/server/routers.ts
index c77598f..0acdc40 100644
--- a/server/routers.ts
+++ b/server/routers.ts
@@ -47,7 +47,6 @@ export const appRouter = router({
create: adminProcedure.input(z.object({
nom: z.string().min(1),
description: z.string().optional(),
- publicCible: z.enum(["directeur", "chef_service", "autre"]),
lienUnique: z.string().min(1),
actif: z.boolean().optional(),
})).mutation(async ({ input }) => {
@@ -59,7 +58,6 @@ export const appRouter = router({
id: z.number(),
nom: z.string().min(1).optional(),
description: z.string().optional(),
- publicCible: z.enum(["directeur", "chef_service", "autre"]).optional(),
lienUnique: z.string().min(1).optional(),
actif: z.boolean().optional(),
})).mutation(async ({ input }) => {
@@ -156,6 +154,7 @@ export const appRouter = router({
formationId: z.number(),
nom: z.string().min(1),
lieu: z.string().min(1),
+ publicCible: z.enum(["directeur", "chef_service", "autre"]),
capaciteMax: z.number().default(12),
dateBlocage: z.string(),
statut: z.enum(["ouverte", "bloquee", "terminee"]).optional(),
@@ -194,6 +193,7 @@ export const appRouter = router({
formationId: z.number(),
nom: z.string().min(1),
lieu: z.string().min(1),
+ publicCible: z.enum(["directeur", "chef_service", "autre"]),
capaciteMax: z.number(),
dateBlocage: z.string(),
statut: z.enum(["ouverte", "bloquee", "terminee"]),
diff --git a/todo.md b/todo.md
index 1fdeff0..531e075 100644
--- a/todo.md
+++ b/todo.md
@@ -122,3 +122,11 @@
- [x] Mettre à jour le schéma de base de données pour les formations
- [x] Mettre à jour les formulaires de gestion des formations
- [x] Afficher le public cible dans les listes et exports
+
+- [x] Déplacer le champ "Public cible" des formations vers les séquences
+- [x] Retirer publicCible du schéma formations
+- [x] Ajouter publicCible au schéma sequences
+- [x] Mettre à jour les procédures tRPC pour sequences
+- [x] Mettre à jour le formulaire de création/édition de séquences
+- [x] Afficher le public cible dans la liste des séquences
+- [x] Afficher le public cible par séquence sur la page d'inscription publique