feat: stages module, mobility frontend, theme toggle, employeeOnly access control

- Add stages module with full CRUD API and admin overview island
- Add mobility overview island (Liste, Kanban, Detail CRUD views)
- Add contract PDF upload/download endpoints for mobilites
- Add light/dark theme toggle in header
- Add employeeOnly flag to hide entire modules from students (admin, students, stages)
- Add read-only GET endpoints for modules/ues/ue-modules in notes module
- Add [slug].tsx catch-all routes for direct URL navigation
- Replace old mobility table with mobilites + stages schema (migration 0004)
- Allow students to create mobilites and upload contracts
- Redirect authenticated users from / to /apps catalog
This commit is contained in:
2026-05-01 12:47:23 +02:00
parent df3957741d
commit 9a4c6863d1
65 changed files with 2597 additions and 681 deletions
+26 -10
View File
@@ -1,7 +1,7 @@
import {
date,
doublePrecision,
integer,
pgEnum,
pgTable,
primaryKey,
serial,
@@ -89,13 +89,29 @@ export const ajustements = pgTable("ajustements", {
pk: primaryKey({ columns: [t.numEtud, t.idUE] }),
}));
export const mobility = pgTable("mobility", {
id: serial("id").primaryKey(),
studentId: integer("studentId").references(() => students.numEtud),
startDate: date("startDate"),
endDate: date("endDate"),
weeksCount: integer("weeksCount"),
destinationCountry: text("destinationCountry"),
destinationName: text("destinationName"),
mobilityStatus: text("mobilityStatus").default("N/A"),
export const stages = pgTable("stages", {
id: serial("idStage").primaryKey(),
numEtud: integer("numEtud").notNull().references(() => students.numEtud),
duree: integer("duree").notNull(),
nomEntreprise: text("nomEntreprise").notNull(),
mission: text("mission"),
});
export const mobilityStatusEnum = pgEnum("mobility_status", [
"contracts_received",
"under_revision",
"done",
"validated",
"canceled",
]);
export const mobilites = pgTable("mobilites", {
id: serial("idMob").primaryKey(),
numEtud: integer("numEtud").notNull().references(() => students.numEtud),
duree: integer("duree").notNull(),
contratMob: text("contratMob"),
ecole: text("ecole"),
pays: text("pays"),
status: mobilityStatusEnum("status").notNull().default("contracts_received"),
idStage: integer("idStage").references(() => stages.id),
});