From b33c8c3acbd70973d54f7fea4610bce199dd2ac8 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 14 Nov 2023 11:54:15 +0100 Subject: [PATCH 01/20] refactor: poc http-wizard --- package.json | 2 +- server/package.json | 6 +- server/src/index.ts | 13 ++- server/src/modules/core/index.ts | 7 +- server/src/modules/core/routes/core.routes.ts | 9 -- .../modules/core/usecases/home/home.route.ts | 20 ++++ server/src/server.ts | 11 +- shared/package.json | 3 +- ui/api.client.ts | 12 ++ ui/package.json | 6 +- yarn.lock | 108 +++++++++++++----- 11 files changed, 146 insertions(+), 51 deletions(-) delete mode 100644 server/src/modules/core/routes/core.routes.ts create mode 100644 server/src/modules/core/usecases/home/home.route.ts diff --git a/package.json b/package.json index e69ceba0a..c562d52eb 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "prettier": "3.0.3", "semantic-release": "19.0.2", "semantic-release-slack-bot": "3.5.2", - "typescript": "4.9.4" + "typescript": "5.1.6" }, "prettier": { "printWidth": 80, diff --git a/server/package.json b/server/package.json index 4a8e0c1f6..a1d2b4630 100644 --- a/server/package.json +++ b/server/package.json @@ -6,7 +6,7 @@ "private": true, "author": "MNA", "license": "MIT", - "main": "src/index.js", + "types": "src/index.ts", "scripts": { "start": "node --enable-source-maps dist/index.js", "dev": "tsup src/index.ts --watch . --watch ../shared --onSuccess 'yarn run copyPublic && yarn run start'", @@ -23,6 +23,7 @@ "@fastify/swagger-ui": "^1.6.0", "@fastify/type-provider-typebox": "^3.5.0", "@hapi/boom": "^10.0.1", + "@http-wizard/core": "^1.3.16", "@json2csv/node": "^6.1.3", "@json2csv/plainjs": "^6.1.3", "@slack/bolt": "^3.14.0", @@ -35,6 +36,7 @@ "env-var": "7.1.1", "fastify": "^4.23.2", "fastify-qs": "^4.0.1", + "fastify-type-provider-zod": "^1.1.9", "jsonwebtoken": "8.5.1", "kysely": "0.26.0", "lodash": "^4.17.21", @@ -77,6 +79,6 @@ "jest": "^29.5.0", "kysely-codegen": "^0.10.0", "ts-jest": "^29.1.0", - "typescript": "4.9.4" + "typescript": "5.1.6" } } diff --git a/server/src/index.ts b/server/src/index.ts index d86ea0ad1..28d02795e 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -9,7 +9,7 @@ import { migrateToLatest } from "./migrations/migrate"; import { extractUserInRequest, registerCoreModule } from "./modules/core"; import { registerFormationModule } from "./modules/data/index"; import { registerIntentionsModule } from "./modules/intentions/index"; -import { server } from "./server"; +import { Server, server } from "./server"; server.register(fastifyCors, {}); server.register(fastifySwagger, { @@ -73,9 +73,16 @@ process.on("uncaughtExceptionMonitor", (error, origin) => { server.addHook("onRequest", extractUserInRequest); server.register(loggerContextPlugin); +const registerRoutes = (instance: Server) => { + return { ...registerCoreModule({ server: instance }) }; +}; + +export type Router = ReturnType; + server.register( - async (instance) => { - registerCoreModule({ server: instance }); + async (instance: Server) => { + registerRoutes(instance); + // registerCoreModule({ server: instance }); registerFormationModule({ server: instance }); registerIntentionsModule({ server: instance }); }, diff --git a/server/src/modules/core/index.ts b/server/src/modules/core/index.ts index a728f53b0..1b27943d8 100644 --- a/server/src/modules/core/index.ts +++ b/server/src/modules/core/index.ts @@ -1,11 +1,14 @@ import { Server } from "../../server"; import { authRoutes } from "./routes/auth.routes"; -import { coreRoutes } from "./routes/core.routes"; +import { homeRoute } from "./usecases/home/home.route"; export { extractUserInRequest } from "./utils/extractUserInRequest/extractUserInRequest"; export const registerCoreModule = ({ server }: { server: Server }) => { authRoutes({ server }); - coreRoutes({ server }); + + return { + ...homeRoute({ server }), + }; }; export { hasPermissionHandler } from "./utils/hasPermission"; diff --git a/server/src/modules/core/routes/core.routes.ts b/server/src/modules/core/routes/core.routes.ts deleted file mode 100644 index 8945026a0..000000000 --- a/server/src/modules/core/routes/core.routes.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { salut } from "shared"; - -import { Server } from "../../../server"; - -export const coreRoutes = ({ server }: { server: Server }) => { - server.get("/", async (request, response) => { - response.status(200).send({ hello: salut }); - }); -}; diff --git a/server/src/modules/core/usecases/home/home.route.ts b/server/src/modules/core/usecases/home/home.route.ts new file mode 100644 index 000000000..50d361c6f --- /dev/null +++ b/server/src/modules/core/usecases/home/home.route.ts @@ -0,0 +1,20 @@ +import { createRoute } from "@http-wizard/core"; +import { z } from "zod"; + +import { Server } from "../../../../server"; + +export const homeRoute = ({ server }: { server: Server }) => { + return createRoute("/", { + method: "GET", + schema: { + response: { 200: z.object({ hello: z.string() }) }, + }, + }).handle((props) => { + server.route({ + ...props, + handler: async (_, response) => { + response.status(200).send({ hello: "dsf" }); + }, + }); + }); +}; diff --git a/server/src/server.ts b/server/src/server.ts index 7987a8f08..218afeab0 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -1,5 +1,9 @@ -import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import fastify from "fastify"; +import { + serializerCompiler, + validatorCompiler, + ZodTypeProvider, +} from "fastify-type-provider-zod"; import qs from "qs"; const adapter = () => ({ @@ -23,6 +27,9 @@ export const server = fastify({ keywords: ["kind", "modifier"], }, }, -}).withTypeProvider(); +}).withTypeProvider(); + +server.setValidatorCompiler(validatorCompiler); +server.setSerializerCompiler(serializerCompiler); export type Server = typeof server; diff --git a/shared/package.json b/shared/package.json index 8afd07a64..8dcd0d3d2 100644 --- a/shared/package.json +++ b/shared/package.json @@ -9,8 +9,7 @@ "private": true, "dependencies": { "@sinclair/typebox": "^0.25.21", - "injecti": "^1.0.5", - "typebox-client": "^1.0.20" + "injecti": "^1.0.5" }, "devDependencies": { "axios": "1.3.5" diff --git a/ui/api.client.ts b/ui/api.client.ts index 028d0bc71..4a606ef10 100644 --- a/ui/api.client.ts +++ b/ui/api.client.ts @@ -1,4 +1,7 @@ +import { ZodTypeProvider } from "@http-wizard/core"; +import { createQueryClient } from "@http-wizard/react-query"; import axios from "axios"; +import { Router } from "server"; import { createClient } from "shared"; export const api = createClient( @@ -8,3 +11,12 @@ export const api = createClient( export const serverApi = createClient( axios.create({ baseURL: process.env.NEXT_PUBLIC_APP_CONTAINER_URL }) ); + +export const client = createQueryClient({ + instance: axios.create({ + baseURL: + typeof document === "undefined" + ? process.env.NEXT_PUBLIC_APP_CONTAINER_URL + : process.env.NEXT_PUBLIC_SERVER_URL, + }), +}); diff --git a/ui/package.json b/ui/package.json index 9de55c644..6f4ceb125 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,6 +15,8 @@ "@chakra-ui/react": "^2.8.0", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", + "@http-wizard/core": "^1.3.16", + "@http-wizard/react-query": "^1.3.16", "@json2csv/plainjs": "^7.0.3", "@tanstack/react-query": "^4.29.3", "@types/node": "18.11.18", @@ -33,7 +35,9 @@ "react-hook-form": "^7.47.0", "react-notion-x": "^6.16.0", "react-select": "^5.7.4", + "server": "*", "shared": "*", - "typescript": "4.9.4" + "typescript": "5.1.6", + "zod": "^3.22.4" } } diff --git a/yarn.lock b/yarn.lock index 821fd938d..28fce50d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2447,6 +2447,25 @@ __metadata: languageName: node linkType: hard +"@http-wizard/core@npm:^1.3.16": + version: 1.3.16 + resolution: "@http-wizard/core@npm:1.3.16" + peerDependencies: + axios: ^1.4.0 + checksum: f6fe31e8333899fd1252e1019f2561329b56b9ed3ddcc6e66da4aea2ac6c5758c53b008a4205019633666d175fbb9467adde17fc0ac41c658b0fab5956110f32 + languageName: node + linkType: hard + +"@http-wizard/react-query@npm:^1.3.16": + version: 1.3.16 + resolution: "@http-wizard/react-query@npm:1.3.16" + peerDependencies: + "@http-wizard/core": 1.3.16 + "@tanstack/react-query": 5.x + checksum: 4b4e6bd280f25547a259ca0455ea0043ca076d1a5d9e56deceea248d8b479f0c938c8360df5516a4f1267e459c44ccadf2d6e32bce551ac81ce098d8e7628f15 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.10.5": version: 0.10.7 resolution: "@humanwhocodes/config-array@npm:0.10.7" @@ -5143,17 +5162,6 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.4.0": - version: 1.4.0 - resolution: "axios@npm:1.4.0" - dependencies: - follow-redirects: ^1.15.0 - form-data: ^4.0.0 - proxy-from-env: ^1.1.0 - checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b - languageName: node - linkType: hard - "axobject-query@npm:^2.2.0": version: 2.2.0 resolution: "axobject-query@npm:2.2.0" @@ -8391,6 +8399,18 @@ __metadata: languageName: node linkType: hard +"fastify-type-provider-zod@npm:^1.1.9": + version: 1.1.9 + resolution: "fastify-type-provider-zod@npm:1.1.9" + dependencies: + zod-to-json-schema: ^3.17.1 + peerDependencies: + fastify: ^4.0.0 + zod: ^3.14.2 + checksum: 86792031057cf3807a2e77ef41b16161d94c89ab95bd6284574502351df9c4d1e1621d4f7e7f3f14cd81aec9923c3a4f61c8889d5abb3b57e52ee9c0015056e1 + languageName: node + linkType: hard + "fastify@npm:^4.23.2": version: 4.23.2 resolution: "fastify@npm:4.23.2" @@ -16026,7 +16046,7 @@ __metadata: languageName: node linkType: hard -"server@workspace:server": +"server@*, server@workspace:server": version: 0.0.0-use.local resolution: "server@workspace:server" dependencies: @@ -16035,6 +16055,7 @@ __metadata: "@fastify/swagger-ui": ^1.6.0 "@fastify/type-provider-typebox": ^3.5.0 "@hapi/boom": ^10.0.1 + "@http-wizard/core": ^1.3.16 "@json2csv/node": ^6.1.3 "@json2csv/plainjs": ^6.1.3 "@slack/bolt": ^3.14.0 @@ -16062,6 +16083,7 @@ __metadata: esbuild-jest: ^0.5.0 fastify: ^4.23.2 fastify-qs: ^4.0.1 + fastify-type-provider-zod: ^1.1.9 jest: ^29.5.0 jsonwebtoken: 8.5.1 kysely: 0.26.0 @@ -16079,7 +16101,7 @@ __metadata: ts-jest: ^29.1.0 ts-node: ^10.9.1 tsup: ^6.5.0 - typescript: 4.9.4 + typescript: 5.1.6 uglify-js: ^3.17.4 uuid: ^9.0.1 winston: ^3.10.0 @@ -16146,7 +16168,6 @@ __metadata: "@sinclair/typebox": ^0.25.21 axios: 1.3.5 injecti: ^1.0.5 - typebox-client: ^1.0.20 languageName: unknown linkType: soft @@ -17115,7 +17136,7 @@ __metadata: prettier: 3.0.3 semantic-release: 19.0.2 semantic-release-slack-bot: 3.5.2 - typescript: 4.9.4 + typescript: 5.1.6 languageName: unknown linkType: soft @@ -17515,17 +17536,6 @@ __metadata: languageName: node linkType: hard -"typebox-client@npm:^1.0.20": - version: 1.0.23 - resolution: "typebox-client@npm:1.0.23" - dependencies: - axios: ^1.4.0 - peerDependencies: - "@sinclair/typebox": ^0.28.14 - checksum: 837d9ca8a839ef849c7e13835d045b4389649b485d1f8f3fb8763f6e13ab4b14150fb4e595605672d2718cd33035e4db92ed0edd5d8d8f528016ed0cf0af3dc0 - languageName: node - linkType: hard - "typed-array-buffer@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-buffer@npm:1.0.0" @@ -17582,7 +17592,17 @@ __metadata: languageName: node linkType: hard -"typescript@npm:4.9.4, typescript@npm:^4.4.3": +"typescript@npm:5.1.6": + version: 5.1.6 + resolution: "typescript@npm:5.1.6" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + languageName: node + linkType: hard + +"typescript@npm:^4.4.3": version: 4.9.4 resolution: "typescript@npm:4.9.4" bin: @@ -17592,7 +17612,17 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@4.9.4#~builtin, typescript@patch:typescript@^4.4.3#~builtin": +"typescript@patch:typescript@5.1.6#~builtin": + version: 5.1.6 + resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=ad5954" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 + languageName: node + linkType: hard + +"typescript@patch:typescript@^4.4.3#~builtin": version: 4.9.4 resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=ad5954" bin: @@ -17620,6 +17650,8 @@ __metadata: "@chakra-ui/react": ^2.8.0 "@emotion/react": ^11.10.6 "@emotion/styled": ^11.10.6 + "@http-wizard/core": ^1.3.16 + "@http-wizard/react-query": ^1.3.16 "@json2csv/plainjs": ^7.0.3 "@tanstack/react-query": ^4.29.3 "@types/node": 18.11.18 @@ -17638,8 +17670,10 @@ __metadata: react-hook-form: ^7.47.0 react-notion-x: ^6.16.0 react-select: ^5.7.4 + server: "*" shared: "*" - typescript: 4.9.4 + typescript: 5.1.6 + zod: ^3.22.4 languageName: unknown linkType: soft @@ -18399,6 +18433,15 @@ __metadata: languageName: node linkType: hard +"zod-to-json-schema@npm:^3.17.1": + version: 3.21.4 + resolution: "zod-to-json-schema@npm:3.21.4" + peerDependencies: + zod: ^3.21.4 + checksum: 899c1f461fb6547c0b08a265c82040c250be9b88d3f408f2f3ff77a418fdfad7549077e589d418fccb312c1f6d555c3c7217b199cc9072762e1fab20716dd2a6 + languageName: node + linkType: hard + "zod@npm:^3.22.2": version: 3.22.2 resolution: "zod@npm:3.22.2" @@ -18406,6 +18449,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.22.4": + version: 3.22.4 + resolution: "zod@npm:3.22.4" + checksum: 80bfd7f8039b24fddeb0718a2ec7c02aa9856e4838d6aa4864335a047b6b37a3273b191ef335bf0b2002e5c514ef261ffcda5a589fb084a48c336ffc4cdbab7f + languageName: node + linkType: hard + "zrender@npm:5.4.3": version: 5.4.3 resolution: "zrender@npm:5.4.3" From e874ff9af442afd9e5f3ca227e1316fd6190ea2c Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 16 Nov 2023 09:31:30 +0100 Subject: [PATCH 02/20] refactor: migrate routes --- server/src/index.ts | 22 ++-- server/src/modules/data/index.ts | 19 ++- .../data/routes/departements.routes.ts | 29 ----- .../data/routes/etablissements.routes.ts | 30 ----- .../modules/data/routes/formations.routes.ts | 20 --- .../getDepartement/getDepartement.route.ts | 24 ++++ .../getDepartement/getDepartement.schema.ts | 21 ++++ .../getDepartementsStats.query.ts | 12 +- .../getDepartements/getDepartements.query.ts | 0 .../getDepartements/getDepartements.route.ts | 20 +++ .../getDepartements/getDepartements.schema.ts | 12 ++ .../getEtablissement/etablissements.routes.ts | 22 ++++ .../getEtablissement.query.ts | 14 +-- .../getEtablissement.schema.ts | 41 +++++++ .../getEtablissements.routes.ts | 24 ++++ .../getEtablissements.schema.ts | 89 ++++++++++++++ .../getFormations/formations.routes.ts | 24 ++++ .../getFormations/getFormation.schema.ts | 115 ++++++++++++++++++ server/src/modules/intentions/index.ts | 1 + .../departement/[codeDepartement]/page.tsx | 42 ++++--- .../(wrapped)/panorama/departement/page.tsx | 5 +- .../panorama/etablissement/[uai]/page.tsx | 15 ++- ui/app/layoutClient.tsx | 3 +- 23 files changed, 462 insertions(+), 142 deletions(-) delete mode 100644 server/src/modules/data/routes/departements.routes.ts delete mode 100644 server/src/modules/data/routes/etablissements.routes.ts delete mode 100644 server/src/modules/data/routes/formations.routes.ts create mode 100644 server/src/modules/data/usecases/getDepartement/getDepartement.route.ts create mode 100644 server/src/modules/data/usecases/getDepartement/getDepartement.schema.ts rename server/src/modules/data/{queries/getDepartementsStats => usecases/getDepartement}/getDepartementsStats.query.ts (86%) rename server/src/modules/data/{queries => usecases}/getDepartements/getDepartements.query.ts (100%) create mode 100644 server/src/modules/data/usecases/getDepartements/getDepartements.route.ts create mode 100644 server/src/modules/data/usecases/getDepartements/getDepartements.schema.ts create mode 100644 server/src/modules/data/usecases/getEtablissement/etablissements.routes.ts rename server/src/modules/data/{queries => usecases}/getEtablissement/getEtablissement.query.ts (91%) create mode 100644 server/src/modules/data/usecases/getEtablissement/getEtablissement.schema.ts create mode 100644 server/src/modules/data/usecases/getEtablissements/getEtablissements.routes.ts create mode 100644 server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts create mode 100644 server/src/modules/data/usecases/getFormations/formations.routes.ts create mode 100644 server/src/modules/data/usecases/getFormations/getFormation.schema.ts diff --git a/server/src/index.ts b/server/src/index.ts index 28d02795e..aa813522d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -7,8 +7,8 @@ import { config } from "../config/config"; import { logger, loggerContextPlugin } from "./logger"; import { migrateToLatest } from "./migrations/migrate"; import { extractUserInRequest, registerCoreModule } from "./modules/core"; -import { registerFormationModule } from "./modules/data/index"; -import { registerIntentionsModule } from "./modules/intentions/index"; +import { registerFormationModule } from "./modules/data"; +import { registerIntentionsModule } from "./modules/intentions"; import { Server, server } from "./server"; server.register(fastifyCors, {}); @@ -74,20 +74,18 @@ server.addHook("onRequest", extractUserInRequest); server.register(loggerContextPlugin); const registerRoutes = (instance: Server) => { - return { ...registerCoreModule({ server: instance }) }; + return { + ...registerCoreModule({ server: instance }), + ...registerFormationModule({ server: instance }), + ...registerIntentionsModule({ server: instance }), + }; }; export type Router = ReturnType; -server.register( - async (instance: Server) => { - registerRoutes(instance); - // registerCoreModule({ server: instance }); - registerFormationModule({ server: instance }); - registerIntentionsModule({ server: instance }); - }, - { prefix: "/api" } -); +server.register(async (instance: Server) => registerRoutes(instance), { + prefix: "/api", +}); const cb = (error: Error | null) => { if (error) { diff --git a/server/src/modules/data/index.ts b/server/src/modules/data/index.ts index 0d9a5ea9b..0740f3e55 100644 --- a/server/src/modules/data/index.ts +++ b/server/src/modules/data/index.ts @@ -1,20 +1,27 @@ import { Server } from "../../server"; -import { departementsRoutes } from "./routes/departements.routes"; -import { etablissementsRoutes } from "./routes/etablissements.routes"; -import { formationsRoutes } from "./routes/formations.routes"; import { panoramaRoutes } from "./routes/panorama.routes"; import { pilotageReformeRoutes } from "./routes/pilotageReforme.routes"; import { pilotageTransformationRoutes } from "./routes/pilotageTransformation.routes"; import { regionsRoutes } from "./routes/regions.routes"; import { restitutionIntentionsRoutes } from "./routes/restitutionIntentions.routes"; +import { getDepartementRoute } from "./usecases/getDepartement/getDepartement.route"; +import { getDepartementsRoute } from "./usecases/getDepartements/getDepartements.route"; +import { getEtablissementRoute } from "./usecases/getEtablissement/etablissements.routes"; +import { getEtablissementsRoutes } from "./usecases/getEtablissements/getEtablissements.routes"; +import { getFormationsRoutes } from "./usecases/getFormations/formations.routes"; export const registerFormationModule = ({ server }: { server: Server }) => { - formationsRoutes({ server }); - etablissementsRoutes({ server }); panoramaRoutes({ server }); regionsRoutes({ server }); - departementsRoutes({ server }); pilotageReformeRoutes({ server }); pilotageTransformationRoutes({ server }); restitutionIntentionsRoutes({ server }); + + return { + ...getFormationsRoutes({ server }), + ...getEtablissementsRoutes({ server }), + ...getEtablissementRoute({ server }), + ...getDepartementRoute({ server }), + ...getDepartementsRoute({ server }), + }; }; diff --git a/server/src/modules/data/routes/departements.routes.ts b/server/src/modules/data/routes/departements.routes.ts deleted file mode 100644 index ef7d32cf8..000000000 --- a/server/src/modules/data/routes/departements.routes.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { getDepartements } from "../queries/getDepartements/getDepartements.query"; -import { getDepartementsStats } from "../queries/getDepartementsStats/getDepartementsStats.query"; - -export const departementsRoutes = ({ server }: { server: Server }) => { - server.get( - "/departements", - { schema: ROUTES_CONFIG.getDepartements }, - async (_, response) => { - const departements = await getDepartements(); - response.status(200).send(departements); - } - ); - - server.get( - "/departement/:codeDepartement", - { schema: ROUTES_CONFIG.getDepartementStats }, - async (request, response) => { - const departementsStats = await getDepartementsStats({ - ...request.params, - ...request.query, - }); - if (!departementsStats) return response.status(404).send(); - response.status(200).send(departementsStats); - } - ); -}; diff --git a/server/src/modules/data/routes/etablissements.routes.ts b/server/src/modules/data/routes/etablissements.routes.ts deleted file mode 100644 index 553c437e8..000000000 --- a/server/src/modules/data/routes/etablissements.routes.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { getEtablissement } from "../queries/getEtablissement/getEtablissement.query"; -import { getEtablissements } from "../usecases/getEtablissements/getEtablissements.usecase"; -export const etablissementsRoutes = ({ server }: { server: Server }) => { - server.get( - "/etablissements", - { schema: ROUTES_CONFIG.getEtablissements }, - async (request, response) => { - const { order, orderBy, ...rest } = request.query; - const etablissements = await getEtablissements({ - ...rest, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(etablissements); - } - ); - - server.get( - "/etablissement/:uai", - { schema: ROUTES_CONFIG.getEtablissement }, - async (request, response) => { - const { uai } = request.params; - const etablissement = await getEtablissement({ uai }); - if (!etablissement) return response.status(404).send(); - response.status(200).send(etablissement); - } - ); -}; diff --git a/server/src/modules/data/routes/formations.routes.ts b/server/src/modules/data/routes/formations.routes.ts deleted file mode 100644 index 466ba40dd..000000000 --- a/server/src/modules/data/routes/formations.routes.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { getFormations } from "../usecases/getFormations/getFormations.usecase"; -export const formationsRoutes = ({ server }: { server: Server }) => { - server.get( - "/formations", - { - schema: ROUTES_CONFIG.getFormations, - }, - async (request, response) => { - const { order, orderBy, ...rest } = request.query; - const formations = await getFormations({ - ...rest, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(formations); - } - ); -}; diff --git a/server/src/modules/data/usecases/getDepartement/getDepartement.route.ts b/server/src/modules/data/usecases/getDepartement/getDepartement.route.ts new file mode 100644 index 000000000..f33b6b57d --- /dev/null +++ b/server/src/modules/data/usecases/getDepartement/getDepartement.route.ts @@ -0,0 +1,24 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getDepartementSchema } from "./getDepartement.schema"; +import { getDepartementsStats } from "./getDepartementsStats.query"; + +export const getDepartementRoute = ({ server }: { server: Server }) => { + return createRoute("/departement/:codeDepartement", { + method: "GET", + schema: getDepartementSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const departementsStats = await getDepartementsStats({ + ...request.params, + ...request.query, + }); + if (!departementsStats) return response.status(404).send(); + response.status(200).send(departementsStats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getDepartement/getDepartement.schema.ts b/server/src/modules/data/usecases/getDepartement/getDepartement.schema.ts new file mode 100644 index 000000000..bd7f7ee1d --- /dev/null +++ b/server/src/modules/data/usecases/getDepartement/getDepartement.schema.ts @@ -0,0 +1,21 @@ +import { z } from "zod"; +export const getDepartementSchema = { + params: z.object({ + codeDepartement: z.string(), + }), + querystring: z.object({ + codeDiplome: z.array(z.string()).optional(), + }), + response: { + 200: z.object({ + codeRegion: z.string(), + libelleDepartement: z.string(), + effectif: z.coerce.number(), + nbFormations: z.coerce.number(), + tauxPression: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + }), + }, +}; diff --git a/server/src/modules/data/queries/getDepartementsStats/getDepartementsStats.query.ts b/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts similarity index 86% rename from server/src/modules/data/queries/getDepartementsStats/getDepartementsStats.query.ts rename to server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts index e2ee64b56..185c3ceeb 100644 --- a/server/src/modules/data/queries/getDepartementsStats/getDepartementsStats.query.ts +++ b/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts @@ -1,12 +1,12 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; -import { effectifAnnee } from "../utils/effectifAnnee"; -import { notHistorique } from "../utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../utils/tauxRemplissage"; +import { effectifAnnee } from "../../queries/utils/effectifAnnee"; +import { notHistorique } from "../../queries/utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; export const getDepartementsStats = async ({ codeDepartement, diff --git a/server/src/modules/data/queries/getDepartements/getDepartements.query.ts b/server/src/modules/data/usecases/getDepartements/getDepartements.query.ts similarity index 100% rename from server/src/modules/data/queries/getDepartements/getDepartements.query.ts rename to server/src/modules/data/usecases/getDepartements/getDepartements.query.ts diff --git a/server/src/modules/data/usecases/getDepartements/getDepartements.route.ts b/server/src/modules/data/usecases/getDepartements/getDepartements.route.ts new file mode 100644 index 000000000..e6a91962f --- /dev/null +++ b/server/src/modules/data/usecases/getDepartements/getDepartements.route.ts @@ -0,0 +1,20 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getDepartements } from "./getDepartements.query"; +import { getDepartementsSchema } from "./getDepartements.schema"; + +export const getDepartementsRoute = ({ server }: { server: Server }) => { + return createRoute("/departements", { + method: "GET", + schema: getDepartementsSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (_, response) => { + const departements = await getDepartements(); + response.status(200).send(departements); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getDepartements/getDepartements.schema.ts b/server/src/modules/data/usecases/getDepartements/getDepartements.schema.ts new file mode 100644 index 000000000..4f884388a --- /dev/null +++ b/server/src/modules/data/usecases/getDepartements/getDepartements.schema.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const getDepartementsSchema = { + response: { + 200: z.array( + z.object({ + label: z.string(), + value: z.string(), + }) + ), + }, +}; diff --git a/server/src/modules/data/usecases/getEtablissement/etablissements.routes.ts b/server/src/modules/data/usecases/getEtablissement/etablissements.routes.ts new file mode 100644 index 000000000..75c79eae6 --- /dev/null +++ b/server/src/modules/data/usecases/getEtablissement/etablissements.routes.ts @@ -0,0 +1,22 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getEtablissementQuery } from "./getEtablissement.query"; +import { getEtablissementSchema } from "./getEtablissement.schema"; + +export const getEtablissementRoute = ({ server }: { server: Server }) => { + return createRoute("/etablissement/:uai", { + method: "GET", + schema: getEtablissementSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { uai } = request.params; + const etablissement = await getEtablissementQuery({ uai }); + if (!etablissement) return response.status(404).send(); + response.status(200).send(etablissement); + }, + }); + }); +}; diff --git a/server/src/modules/data/queries/getEtablissement/getEtablissement.query.ts b/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts similarity index 91% rename from server/src/modules/data/queries/getEtablissement/getEtablissement.query.ts rename to server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts index 380ec4d9f..ebc83384c 100644 --- a/server/src/modules/data/queries/getEtablissement/getEtablissement.query.ts +++ b/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts @@ -3,14 +3,14 @@ import { jsonArrayFrom } from "kysely/helpers/postgres"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; -import { hasContinuum } from "../utils/hasContinuum"; -import { notHistorique } from "../utils/notHistorique"; -import { withPositionCadran } from "../utils/positionCadran"; -import { withInsertionReg } from "../utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../utils/tauxPoursuite"; -import { selectTauxPression } from "../utils/tauxPression"; +import { hasContinuum } from "../../queries/utils/hasContinuum"; +import { notHistorique } from "../../queries/utils/notHistorique"; +import { withPositionCadran } from "../../queries/utils/positionCadran"; +import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; +import { selectTauxPression } from "../../queries/utils/tauxPression"; -export const getEtablissement = async ({ +export const getEtablissementQuery = async ({ uai, millesimeSortie = "2020_2021", rentreeScolaire = "2022", diff --git a/server/src/modules/data/usecases/getEtablissement/getEtablissement.schema.ts b/server/src/modules/data/usecases/getEtablissement/getEtablissement.schema.ts new file mode 100644 index 000000000..3f9178aa6 --- /dev/null +++ b/server/src/modules/data/usecases/getEtablissement/getEtablissement.schema.ts @@ -0,0 +1,41 @@ +import { z } from "zod"; + +export const getEtablissementSchema = { + params: z.object({ uai: z.string() }), + response: { + 200: z.object({ + uai: z.string(), + rentreeScolaire: z.string(), + libelleEtablissement: z.string().optional(), + valeurAjoutee: z.coerce.number().optional(), + codeRegion: z.string().optional(), + libelleRegion: z.string().optional(), + formations: z.array( + z.object({ + cfd: z.string(), + codeNiveauDiplome: z.string(), + libelleDiplome: z.string(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + libelleNiveauDiplome: z.string().optional(), + effectif: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number().optional(), + positionCadran: z.string().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), + }) + ), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getEtablissements/getEtablissements.routes.ts b/server/src/modules/data/usecases/getEtablissements/getEtablissements.routes.ts new file mode 100644 index 000000000..ce7fe57b8 --- /dev/null +++ b/server/src/modules/data/usecases/getEtablissements/getEtablissements.routes.ts @@ -0,0 +1,24 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getEtablissementsSchema } from "./getEtablissements.schema"; +import { getEtablissements } from "./getEtablissements.usecase"; + +export const getEtablissementsRoutes = ({ server }: { server: Server }) => { + return createRoute("/etablissements", { + method: "GET", + schema: getEtablissementsSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { order, orderBy, ...rest } = request.query; + const etablissements = await getEtablissements({ + ...rest, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(etablissements); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts b/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts new file mode 100644 index 000000000..8e7a06f0a --- /dev/null +++ b/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts @@ -0,0 +1,89 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); + +const EtablissementLineSchema = z.object({ + libelleEtablissement: z.string().optional(), + UAI: z.string(), + rentreeScolaire: z.string().optional(), + secteur: z.string().optional(), + commune: z.string().optional(), + departement: z.string().optional(), + codeFormationDiplome: z.string(), + libelleDiplome: z.string(), + codeNiveauDiplome: z.string(), + libelleOfficielFamille: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + libelleNiveauDiplome: z.string().optional(), + anneeDebut: z.coerce.number().optional(), + capacite: z.coerce.number().optional(), + effectif: z.coerce.number().optional(), + effectif1: z.coerce.number().optional(), + effectif2: z.coerce.number().optional(), + effectif3: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number().optional(), + valeurAjoutee: z.coerce.number().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), +}); + +export const getEtablissementsSchema = { + querystring: z.object({ + cfd: z.array(z.string()).optional(), + codeRegion: z.array(z.string()).optional(), + codeAcademie: z.array(z.string()).optional(), + codeDepartement: z.array(z.string()).optional(), + commune: z.array(z.string()).optional(), + codeDiplome: z.array(z.string()).optional(), + codeDispositif: z.array(z.string()).optional(), + cfdFamille: z.array(z.string()).optional(), + rentreeScolaire: z.array(z.string()).optional(), + secteur: z.array(z.string()).optional(), + uai: z.array(z.string()).optional(), + CPC: z.array(z.string()).optional(), + CPCSecteur: z.array(z.string()).optional(), + CPCSousSecteur: z.array(z.string()).optional(), + libelleFiliere: z.array(z.string()).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: EtablissementLineSchema.keyof().optional(), + offset: z.coerce.number().optional(), + limit: z.coerce.number().optional(), + }), + response: { + 200: z.object({ + count: z.coerce.number(), + filters: z.object({ + regions: z.array(OptionSchema), + academies: z.array(OptionSchema), + departements: z.array(OptionSchema), + communes: z.array(OptionSchema), + diplomes: z.array(OptionSchema), + dispositifs: z.array(OptionSchema), + familles: z.array(OptionSchema), + formations: z.array(OptionSchema), + etablissements: z.array(OptionSchema), + CPCs: z.array(OptionSchema), + CPCSecteurs: z.array(OptionSchema), + CPCSousSecteurs: z.array(OptionSchema), + libelleFilieres: z.array(OptionSchema), + }), + etablissements: z.array(EtablissementLineSchema), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getFormations/formations.routes.ts b/server/src/modules/data/usecases/getFormations/formations.routes.ts new file mode 100644 index 000000000..5fad0eed8 --- /dev/null +++ b/server/src/modules/data/usecases/getFormations/formations.routes.ts @@ -0,0 +1,24 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getFormationSchema } from "./getFormation.schema"; +import { getFormations } from "./getFormations.usecase"; + +export const getFormationsRoutes = ({ server }: { server: Server }) => { + return createRoute("/formations", { + method: "GET", + schema: getFormationSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { order, orderBy, ...rest } = request.query; + const formations = await getFormations({ + ...rest, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(formations); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getFormations/getFormation.schema.ts b/server/src/modules/data/usecases/getFormations/getFormation.schema.ts new file mode 100644 index 000000000..93456cdc6 --- /dev/null +++ b/server/src/modules/data/usecases/getFormations/getFormation.schema.ts @@ -0,0 +1,115 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); + +export const FormationLineSchema = z.object({ + codeFormationDiplome: z.string(), + libelleDiplome: z.string(), + rentreeScolaire: z.string().optional(), + codeNiveauDiplome: z.string(), + libelleOfficielFamille: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + libelleNiveauDiplome: z.string().optional(), + nbEtablissement: z.coerce.number().optional(), + anneeDebut: z.coerce.number().optional(), + effectif: z.coerce.number().optional(), + effectif1: z.coerce.number().optional(), + effectif2: z.coerce.number().optional(), + effectif3: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), + positionCadran: z.string().optional(), +}); + +export const getFormationSchema = { + querystring: z.object({ + cfd: z.array(z.string()).optional(), + codeRegion: z.array(z.string()).optional(), + codeAcademie: z.array(z.string()).optional(), + codeDepartement: z.array(z.string()).optional(), + commune: z.array(z.string()).optional(), + codeDiplome: z.array(z.string()).optional(), + codeDispositif: z.array(z.string()).optional(), + cfdFamille: z.array(z.string()).optional(), + rentreeScolaire: z.array(z.string()).optional(), + CPC: z.array(z.string()).optional(), + CPCSecteur: z.array(z.string()).optional(), + CPCSousSecteur: z.array(z.string()).optional(), + libelleFiliere: z.array(z.string()).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: FormationLineSchema.keyof().optional(), + withEmptyFormations: z.boolean().optional(), + offset: z.coerce.number().optional(), + limit: z.coerce.number().optional(), + }), + response: { + 200: z.object({ + count: z.coerce.number(), + filters: z.object({ + regions: z.array(OptionSchema), + academies: z.array(OptionSchema), + departements: z.array(OptionSchema), + communes: z.array(OptionSchema), + diplomes: z.array(OptionSchema), + dispositifs: z.array(OptionSchema), + familles: z.array(OptionSchema), + formations: z.array(OptionSchema), + CPCs: z.array(OptionSchema), + CPCSecteurs: z.array(OptionSchema), + CPCSousSecteurs: z.array(OptionSchema), + libelleFilieres: z.array(OptionSchema), + }), + formations: z.array( + z.object({ + codeFormationDiplome: z.string(), + libelleDiplome: z.string(), + rentreeScolaire: z.string().optional(), + codeNiveauDiplome: z.string(), + libelleOfficielFamille: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + libelleNiveauDiplome: z.string().optional(), + nbEtablissement: z.coerce.number().optional(), + anneeDebut: z.coerce.number().optional(), + effectif: z.coerce.number().optional(), + effectif1: z.coerce.number().optional(), + effectif2: z.coerce.number().optional(), + effectif3: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), + positionCadran: z.string().optional(), + }) + ), + }), + }, +}; diff --git a/server/src/modules/intentions/index.ts b/server/src/modules/intentions/index.ts index 38751171a..d30a2e375 100644 --- a/server/src/modules/intentions/index.ts +++ b/server/src/modules/intentions/index.ts @@ -5,4 +5,5 @@ import { validationRoutes } from "./routes/validation.routes"; export const registerIntentionsModule = ({ server }: { server: Server }) => { validationRoutes({ server }); demandeRoutes({ server }); + return {}; }; diff --git a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx index 3b556713c..00da8fb7a 100644 --- a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx +++ b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx @@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { api } from "../../../../../api.client"; +import { api, client } from "../../../../../api.client"; import { CadranSection } from "../../components/CadranSection"; import { FiltersSection } from "../../components/FiltersSection"; import { IndicateursSection } from "../../components/IndicateursSection"; @@ -26,26 +26,28 @@ export default function Panorama({ const [codeNiveauDiplome, setCodeNiveauDiplome] = useState(); const [libelleFiliere, setLibelleFiliere] = useState(); - const { data: departementsOptions } = useQuery( - ["departements"], - api.getDepartements({}).call, - { - keepPreviousData: true, - staleTime: 10000000, - } - ); + const { data: departementsOptions } = client + .ref("[GET]/departements") + .useQuery( + {}, + { + keepPreviousData: true, + staleTime: 10000000, + } + ); - const { data: stats } = useQuery( - ["departement", codeDepartement, codeNiveauDiplome], - api.getDepartementStats({ - params: { codeDepartement }, - query: { codeDiplome: codeNiveauDiplome }, - }).call, - { - keepPreviousData: true, - staleTime: 10000000, - } - ); + const { data: stats } = client + .ref("[GET]/departement/:codeDepartement") + .useQuery( + { + params: { codeDepartement }, + query: { codeDiplome: codeNiveauDiplome }, + }, + { + keepPreviousData: true, + staleTime: 10000000, + } + ); const { data } = useQuery( ["formationForPanorama", { codeDepartement }], diff --git a/ui/app/(wrapped)/panorama/departement/page.tsx b/ui/app/(wrapped)/panorama/departement/page.tsx index c794af9e1..98c01f3c4 100644 --- a/ui/app/(wrapped)/panorama/departement/page.tsx +++ b/ui/app/(wrapped)/panorama/departement/page.tsx @@ -1,10 +1,9 @@ -import { serverApi } from "@/api.client"; - +import { client } from "../../../../api.client"; import { PanoramaSelection } from "./PanoramaSelection"; export const revalidate = 0; export default async function Panorama() { - const departementsOptions = await serverApi.getDepartements({}).call(); + const departementsOptions = await client.ref("[GET]/departements").query({}); return ; } diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx index b4820157d..e74e96ccd 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx @@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useContext, useState } from "react"; -import { api } from "../../../../../api.client"; +import { api, client } from "../../../../../api.client"; import { UaiFilterContext } from "../../../../layoutClient"; import { InfoSection } from "../../components/InfoSection"; import { PanoramaSelection } from "../PanoramaSelection"; @@ -30,13 +30,12 @@ export default function Panorama({ }; const [codeNiveauDiplome, setCodeNiveauDiplome] = useState(); - const { data: etablissement, isError } = useQuery( - ["getEtablissement", { uai }], - api.getEtablissement({ - params: { uai }, - }).call, - { keepPreviousData: true, staleTime: 10000000, retry: false } - ); + const { data: etablissement, isError } = client + .ref("[GET]/etablissement/:uai") + .useQuery( + { params: { uai } }, + { keepPreviousData: true, staleTime: 10000000, retry: false } + ); const { data: regionStats } = useQuery( ["getRegionStats", { codeRegion: etablissement?.codeRegion }], diff --git a/ui/app/layoutClient.tsx b/ui/app/layoutClient.tsx index c7918a563..b17207d07 100644 --- a/ui/app/layoutClient.tsx +++ b/ui/app/layoutClient.tsx @@ -16,6 +16,7 @@ import { useRef, useState, } from "react"; +import { z } from "zod"; import { Auth, AuthContext } from "@/app/(wrapped)/auth/authContext"; @@ -30,7 +31,7 @@ const useTracking = () => { (typeof localStorage !== "undefined" && localStorage.getItem("notracking") === "true")) ); - + console.log(z); useEffect(() => { if (param === "reset") { localStorage.removeItem("notracking"); From 19241a9c54b733741608adf741b25b49041739f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Thu, 16 Nov 2023 17:33:12 +0100 Subject: [PATCH 03/20] refactor: region/regions/dataForPanoramaDepartement/dataForPanoramaRegion routes --- server/src/modules/data/index.ts | 12 +- .../data/queries/utils/positionCadran.ts | 32 ++-- .../modules/data/routes/panorama.routes.ts | 37 ---- .../src/modules/data/routes/regions.routes.ts | 29 --- .../getDataForPanorama.usecase.ts | 32 ---- .../dependencies.ts | 44 +---- .../getDataForPanoramaDepartement.route.ts | 30 +++ .../getDataForPanoramaDepartement.schema.ts | 42 ++++ .../getDataForPanoramaDepartement.usecase.ts | 17 ++ .../getDataForPanoramaRegion/dependencies.ts | 179 ++++++++++++++++++ .../getDataForPanoramaRegion.route.ts | 30 +++ .../getDataForPanoramaRegion.schema.ts | 42 ++++ .../getDataForPanoramaRegion.usecase.ts | 15 ++ .../usecases/getFormations/dependencies.ts | 34 ++-- .../getFormationsStatsQuery.dep.ts | 4 +- .../usecases/getRegion/getRegion.route.ts | 24 +++ .../usecases/getRegion/getRegion.schema.ts | 20 ++ .../getRegion/getRegionsStats.query.ts | 92 +++++++++ .../usecases/getRegions/getRegions.query.ts | 17 ++ .../usecases/getRegions/getRegions.route.ts | 20 ++ .../usecases/getRegions/getRegions.schema.ts | 12 ++ .../dependencies.ts | 6 +- 22 files changed, 595 insertions(+), 175 deletions(-) delete mode 100644 server/src/modules/data/routes/panorama.routes.ts delete mode 100644 server/src/modules/data/routes/regions.routes.ts delete mode 100644 server/src/modules/data/usecases/getDataForPanorama/getDataForPanorama.usecase.ts rename server/src/modules/data/usecases/{getDataForPanorama => getDataForPanoramaDepartement}/dependencies.ts (88%) create mode 100644 server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.schema.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.usecase.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.schema.ts create mode 100644 server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.usecase.ts create mode 100644 server/src/modules/data/usecases/getRegion/getRegion.route.ts create mode 100644 server/src/modules/data/usecases/getRegion/getRegion.schema.ts create mode 100644 server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts create mode 100644 server/src/modules/data/usecases/getRegions/getRegions.query.ts create mode 100644 server/src/modules/data/usecases/getRegions/getRegions.route.ts create mode 100644 server/src/modules/data/usecases/getRegions/getRegions.schema.ts diff --git a/server/src/modules/data/index.ts b/server/src/modules/data/index.ts index 0740f3e55..26282966f 100644 --- a/server/src/modules/data/index.ts +++ b/server/src/modules/data/index.ts @@ -1,18 +1,18 @@ import { Server } from "../../server"; -import { panoramaRoutes } from "./routes/panorama.routes"; import { pilotageReformeRoutes } from "./routes/pilotageReforme.routes"; import { pilotageTransformationRoutes } from "./routes/pilotageTransformation.routes"; -import { regionsRoutes } from "./routes/regions.routes"; import { restitutionIntentionsRoutes } from "./routes/restitutionIntentions.routes"; +import { getDataForPanoramaDepartementRoute } from "./usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route"; +import { getDataForPanoramaRegionRoute } from "./usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route"; import { getDepartementRoute } from "./usecases/getDepartement/getDepartement.route"; import { getDepartementsRoute } from "./usecases/getDepartements/getDepartements.route"; import { getEtablissementRoute } from "./usecases/getEtablissement/etablissements.routes"; import { getEtablissementsRoutes } from "./usecases/getEtablissements/getEtablissements.routes"; import { getFormationsRoutes } from "./usecases/getFormations/formations.routes"; +import { getRegionRoute } from "./usecases/getRegion/getRegion.route"; +import { getRegionsRoute } from "./usecases/getRegions/getRegions.route"; export const registerFormationModule = ({ server }: { server: Server }) => { - panoramaRoutes({ server }); - regionsRoutes({ server }); pilotageReformeRoutes({ server }); pilotageTransformationRoutes({ server }); restitutionIntentionsRoutes({ server }); @@ -23,5 +23,9 @@ export const registerFormationModule = ({ server }: { server: Server }) => { ...getEtablissementRoute({ server }), ...getDepartementRoute({ server }), ...getDepartementsRoute({ server }), + ...getDataForPanoramaDepartementRoute({ server }), + ...getDataForPanoramaRegionRoute({ server }), + ...getRegionRoute({ server }), + ...getRegionsRoute({ server }), }; }; diff --git a/server/src/modules/data/queries/utils/positionCadran.ts b/server/src/modules/data/queries/utils/positionCadran.ts index bd5ee3c6e..b311812b6 100644 --- a/server/src/modules/data/queries/utils/positionCadran.ts +++ b/server/src/modules/data/queries/utils/positionCadran.ts @@ -101,32 +101,32 @@ export function withPositionCadran>({ sql` CASE WHEN (${tauxInsertionReg} >= ${selectTauxInsertion6moisAgg( - "indicateurRegionSortie" - )} + "indicateurRegionSortie" + )} AND ${tauxPoursuiteReg} > ${selectTauxPoursuiteAgg( - "indicateurRegionSortie" - )}) + "indicateurRegionSortie" + )}) THEN 'Q1' WHEN (${tauxInsertionReg} >= ${selectTauxInsertion6moisAgg( - "indicateurRegionSortie" - )} + "indicateurRegionSortie" + )} AND ${tauxPoursuiteReg} < ${selectTauxPoursuiteAgg( - "indicateurRegionSortie" - )}) + "indicateurRegionSortie" + )}) THEN 'Q2' WHEN (${tauxInsertionReg} < ${selectTauxInsertion6moisAgg( - "indicateurRegionSortie" - )} + "indicateurRegionSortie" + )} AND ${tauxPoursuiteReg} >= ${selectTauxPoursuiteAgg( - "indicateurRegionSortie" - )}) + "indicateurRegionSortie" + )}) THEN 'Q3' WHEN (${tauxInsertionReg} < ${selectTauxInsertion6moisAgg( - "indicateurRegionSortie" - )} + "indicateurRegionSortie" + )} AND ${tauxPoursuiteReg} < ${selectTauxPoursuiteAgg( - "indicateurRegionSortie" - )}) + "indicateurRegionSortie" + )}) THEN 'Q4' ELSE 'Hors cadran' END`.as("positionCadran"), diff --git a/server/src/modules/data/routes/panorama.routes.ts b/server/src/modules/data/routes/panorama.routes.ts deleted file mode 100644 index 20cc9c1e3..000000000 --- a/server/src/modules/data/routes/panorama.routes.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { - getDataForPanoramaDepartement, - getDataForPanoramaRegion, -} from "../usecases/getDataForPanorama/getDataForPanorama.usecase"; -export const panoramaRoutes = ({ server }: { server: Server }) => { - server.get( - "/panorama/stats/region", - { schema: ROUTES_CONFIG.getDataForPanoramaRegion }, - async (request, response) => { - const q = request.query; - if (!("codeRegion" in q)) { - q; - } - const stats = await getDataForPanoramaRegion({ - ...request.query, - }); - response.status(200).send(stats); - } - ); - server.get( - "/panorama/stats/departement", - { schema: ROUTES_CONFIG.getDataForPanoramaDepartement }, - async (request, response) => { - const q = request.query; - if (!("codeDepartement" in q)) { - q; - } - const stats = await getDataForPanoramaDepartement({ - ...request.query, - }); - response.status(200).send(stats); - } - ); -}; diff --git a/server/src/modules/data/routes/regions.routes.ts b/server/src/modules/data/routes/regions.routes.ts deleted file mode 100644 index 47d75db6b..000000000 --- a/server/src/modules/data/routes/regions.routes.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { getRegions } from "../queries/getRegions/getRegions.query"; -import { getRegionStats } from "../queries/getRegionStats/getRegionStats.query"; - -export const regionsRoutes = ({ server }: { server: Server }) => { - server.get( - "/regions", - { schema: ROUTES_CONFIG.getRegions }, - async (_, response) => { - const regions = await getRegions(); - response.status(200).send(regions); - } - ); - - server.get( - "/region/:codeRegion", - { schema: ROUTES_CONFIG.getRegionStats }, - async (request, response) => { - const regionStats = await getRegionStats({ - ...request.params, - ...request.query, - }); - if (!regionStats) return response.status(404).send(); - response.status(200).send(regionStats); - } - ); -}; diff --git a/server/src/modules/data/usecases/getDataForPanorama/getDataForPanorama.usecase.ts b/server/src/modules/data/usecases/getDataForPanorama/getDataForPanorama.usecase.ts deleted file mode 100644 index 3dc37bd89..000000000 --- a/server/src/modules/data/usecases/getDataForPanorama/getDataForPanorama.usecase.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { inject } from "injecti"; - -import { - queryFormationsDepartement, - queryFormationsRegion, -} from "./dependencies"; - -export const [getDataForPanoramaRegion] = inject( - { queryFormationsRegion }, - (deps) => - async ({ codeRegion }: { codeRegion: string }) => { - const formations = await deps.queryFormationsRegion({ codeRegion }); - - return { - formations, - }; - } -); - -export const [getDataForPanoramaDepartement] = inject( - { queryFormationsDepartement }, - (deps) => - async ({ codeDepartement }: { codeDepartement: string }) => { - const formations = await deps.queryFormationsDepartement({ - codeDepartement, - }); - - return { - formations, - }; - } -); diff --git a/server/src/modules/data/usecases/getDataForPanorama/dependencies.ts b/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts similarity index 88% rename from server/src/modules/data/usecases/getDataForPanorama/dependencies.ts rename to server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts index d8acef126..6403d6df7 100644 --- a/server/src/modules/data/usecases/getDataForPanorama/dependencies.ts +++ b/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts @@ -4,6 +4,7 @@ import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; import { effectifAnnee } from "../../queries/utils/effectifAnnee"; import { hasContinuum } from "../../queries/utils/hasContinuum"; +import { notHistorique } from "../../queries/utils/notHistorique"; import { withPositionCadran } from "../../queries/utils/positionCadran"; import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; @@ -11,14 +12,16 @@ import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; -const queryFormations = ({ +export const queryFormationsDepartement = async ({ + codeDepartement, rentreeScolaire = "2022", millesimeSortie = "2020_2021", }: { + codeDepartement: string; rentreeScolaire?: string; millesimeSortie?: string; -}) => - kdb +}) => { + const formations = await kdb .selectFrom("formation") .leftJoin( "formationEtablissement", @@ -49,16 +52,13 @@ const queryFormations = ({ "etablissement.UAI", "formationEtablissement.UAI" ) - .where( - "codeFormationDiplome", - "not in", - sql`(SELECT DISTINCT "ancienCFD" FROM "formationHistorique")` - ) .leftJoin("indicateurEntree as iep", (join) => join .onRef("formationEtablissement.id", "=", "iep.formationEtablissementId") .on("iep.rentreeScolaire", "=", "2021") ) + .where(notHistorique) + .where("etablissement.codeDepartement", "=", codeDepartement) .select([ "codeFormationDiplome", "libelleDiplome", @@ -173,34 +173,6 @@ const queryFormations = ({ "dispositif.codeDispositif", "niveauDiplome.libelleNiveauDiplome", ]) - -export const queryFormationsRegion = async ({ - codeRegion, - rentreeScolaire = "2022", - millesimeSortie = "2020_2021", -}: { - codeRegion: string; - rentreeScolaire?: string; - millesimeSortie?: string; -}) => { - const formations = await queryFormations({ rentreeScolaire, millesimeSortie }) - .where("etablissement.codeRegion", "=", codeRegion) - .execute(); - - return formations.map(cleanNull); -}; - -export const queryFormationsDepartement = async ({ - codeDepartement, - rentreeScolaire = "2022", - millesimeSortie = "2020_2021", -}: { - codeDepartement: string; - rentreeScolaire?: string; - millesimeSortie?: string; -}) => { - const formations = await queryFormations({ rentreeScolaire, millesimeSortie }) - .where("etablissement.codeDepartement", "=", codeDepartement) .execute(); return formations.map(cleanNull); diff --git a/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route.ts b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route.ts new file mode 100644 index 000000000..d3379cf69 --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route.ts @@ -0,0 +1,30 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getDataForPanoramaDepartementSchema } from "./getDataForPanoramaDepartement.schema"; +import { getDataForPanoramaDepartement } from "./getDataForPanoramaDepartement.usecase"; + +export const getDataForPanoramaDepartementRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/panorama/stats/departement", { + method: "GET", + schema: getDataForPanoramaDepartementSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const q = request.query; + if (!("codeDepartement" in q)) { + q; + } + const stats = await getDataForPanoramaDepartement({ + ...request.query, + }); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.schema.ts b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.schema.ts new file mode 100644 index 000000000..32d42149d --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.schema.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; + +export const getDataForPanoramaDepartementSchema = { + querystring: z.object({ + codeDepartement: z.string(), + }), + response: { + 200: z.object({ + formations: z.array( + z.object({ + codeFormationDiplome: z.string(), + libelleDiplome: z.string(), + codeNiveauDiplome: z.string(), + libelleNiveauDiplome: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + nbEtablissement: z.coerce.number(), + effectif: z.coerce.number().optional(), + effectifPrecedent: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number(), + tauxInsertion6moisPrecedent: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number(), + tauxPoursuiteEtudesPrecedent: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number(), + positionCadran: z.string().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), + }) + ), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.usecase.ts b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.usecase.ts new file mode 100644 index 000000000..b5ea6e378 --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.usecase.ts @@ -0,0 +1,17 @@ +import { inject } from "injecti"; + +import { queryFormationsDepartement } from "./dependencies"; + +export const [getDataForPanoramaDepartement] = inject( + { queryFormationsDepartement }, + (deps) => + async ({ codeDepartement }: { codeDepartement: string }) => { + const formations = await deps.queryFormationsDepartement({ + codeDepartement, + }); + + return { + formations, + }; + } +); diff --git a/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts b/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts new file mode 100644 index 000000000..4737055c1 --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts @@ -0,0 +1,179 @@ +import { sql } from "kysely"; + +import { kdb } from "../../../../db/db"; +import { cleanNull } from "../../../../utils/noNull"; +import { effectifAnnee } from "../../queries/utils/effectifAnnee"; +import { hasContinuum } from "../../queries/utils/hasContinuum"; +import { notHistorique } from "../../queries/utils/notHistorique"; +import { withPositionCadran } from "../../queries/utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; +import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; + +export const queryFormationsRegion = async ({ + codeRegion, + rentreeScolaire = "2022", + millesimeSortie = "2020_2021", +}: { + codeRegion: string; + rentreeScolaire?: string; + millesimeSortie?: string; +}) => { + const formations = await kdb + .selectFrom("formation") + .leftJoin( + "formationEtablissement", + "formationEtablissement.cfd", + "formation.codeFormationDiplome" + ) + .leftJoin( + "niveauDiplome", + "niveauDiplome.codeNiveauDiplome", + "formation.codeNiveauDiplome" + ) + .leftJoin( + "dispositif", + "formationEtablissement.dispositifId", + "dispositif.codeDispositif" + ) + .innerJoin("indicateurEntree", (join) => + join + .onRef( + "formationEtablissement.id", + "=", + "indicateurEntree.formationEtablissementId" + ) + .on("indicateurEntree.rentreeScolaire", "=", rentreeScolaire) + ) + .leftJoin( + "etablissement", + "etablissement.UAI", + "formationEtablissement.UAI" + ) + .leftJoin("indicateurEntree as iep", (join) => + join + .onRef("formationEtablissement.id", "=", "iep.formationEtablissementId") + .on("iep.rentreeScolaire", "=", "2021") + ) + .where(notHistorique) + .where("etablissement.codeRegion", "=", codeRegion) + .select([ + "codeFormationDiplome", + "libelleDiplome", + "formationEtablissement.dispositifId", + "libelleDispositif", + "libelleNiveauDiplome", + "formation.codeNiveauDiplome", + "formation.libelleFiliere", + "formation.CPC", + "formation.CPCSecteur", + "formation.CPCSousSecteur", + sql`COUNT(etablissement."UAI")`.as("nbEtablissement"), + selectTauxRemplissageAgg("indicateurEntree").as("tauxRemplissage"), + sql`SUM(${effectifAnnee({ alias: "indicateurEntree" })})`.as( + "effectif" + ), + sql`SUM(${effectifAnnee({ alias: "iep" })})`.as( + "effectifPrecedent" + ), + selectTauxPressionAgg("indicateurEntree").as("tauxPression"), + (eb) => + withInsertionReg({ + eb, + millesimeSortie: "2019_2020", + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("tauxInsertion6moisPrecedent"), + (eb) => + withPoursuiteReg({ + eb, + millesimeSortie: "2019_2020", + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("tauxPoursuiteEtudesPrecedent"), + (eb) => + withInsertionReg({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("tauxInsertion6mois"), + (eb) => + withPoursuiteReg({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("tauxPoursuiteEtudes"), + (eb) => + hasContinuum({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("continuum"), + (eb) => + withTauxDevenirFavorableReg({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("tauxDevenirFavorable"), + (eb) => + withPositionCadran({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }).as("positionCadran"), + ]) + .$narrowType<{ + tauxInsertion6mois: number; + tauxPoursuiteEtudes: number; + tauxDevenirFavorable: number; + }>() + .having( + (eb) => + withInsertionReg({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }), + "is not", + null + ) + .having( + (eb) => + withPoursuiteReg({ + eb, + millesimeSortie, + cfdRef: "formationEtablissement.cfd", + dispositifIdRef: "formationEtablissement.dispositifId", + codeRegionRef: "etablissement.codeRegion", + }), + "is not", + null + ) + .groupBy([ + "formationEtablissement.cfd", + "formation.id", + "indicateurEntree.rentreeScolaire", + "formationEtablissement.dispositifId", + "dispositif.codeDispositif", + "niveauDiplome.libelleNiveauDiplome", + ]) + .execute(); + + return formations.map(cleanNull); +}; diff --git a/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route.ts b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route.ts new file mode 100644 index 000000000..3bed80ecb --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route.ts @@ -0,0 +1,30 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getDataForPanoramaRegionSchema } from "./getDataForPanoramaRegion.schema"; +import { getDataForPanoramaRegion } from "./getDataForPanoramaRegion.usecase"; + +export const getDataForPanoramaRegionRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/panorama/stats/region", { + method: "GET", + schema: getDataForPanoramaRegionSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const q = request.query; + if (!("codeRegion" in q)) { + q; + } + const stats = await getDataForPanoramaRegion({ + ...request.query, + }); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.schema.ts b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.schema.ts new file mode 100644 index 000000000..92d4ef7ab --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.schema.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; + +export const getDataForPanoramaRegionSchema = { + querystring: z.object({ + codeRegion: z.string(), + }), + response: { + 200: z.object({ + formations: z.array( + z.object({ + codeFormationDiplome: z.string(), + libelleDiplome: z.string(), + codeNiveauDiplome: z.string(), + libelleNiveauDiplome: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + nbEtablissement: z.coerce.number(), + effectif: z.coerce.number().optional(), + effectifPrecedent: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPression: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number(), + tauxInsertion6moisPrecedent: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number(), + tauxPoursuiteEtudesPrecedent: z.coerce.number().optional(), + tauxDevenirFavorable: z.coerce.number(), + positionCadran: z.string().optional(), + CPC: z.string().optional(), + CPCSecteur: z.string().optional(), + CPCSousSecteur: z.string().optional(), + libelleFiliere: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), + }) + ), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.usecase.ts b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.usecase.ts new file mode 100644 index 000000000..76d9f1ea0 --- /dev/null +++ b/server/src/modules/data/usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.usecase.ts @@ -0,0 +1,15 @@ +import { inject } from "injecti"; + +import { queryFormationsRegion } from "./dependencies"; + +export const [getDataForPanoramaRegion] = inject( + { queryFormationsRegion }, + (deps) => + async ({ codeRegion }: { codeRegion: string }) => { + const formations = await deps.queryFormationsRegion({ codeRegion }); + + return { + formations, + }; + } +); diff --git a/server/src/modules/data/usecases/getFormations/dependencies.ts b/server/src/modules/data/usecases/getFormations/dependencies.ts index 032fa9486..de94c11a7 100644 --- a/server/src/modules/data/usecases/getFormations/dependencies.ts +++ b/server/src/modules/data/usecases/getFormations/dependencies.ts @@ -189,24 +189,24 @@ const findFormationsInDb = async ({ cmpr("indicateurEntree.rentreeScolaire", "is not", null), withEmptyFormations ? not( - exists( - selectFrom("formationEtablissement as fe") - .select("cfd") - .distinct() - .innerJoin( - "indicateurEntree", - "id", - "formationEtablissementId" - ) - .where("rentreeScolaire", "in", rentreeScolaire) - .whereRef( - "fe.dispositifId", - "=", - "formationEtablissement.dispositifId" - ) - .whereRef("fe.cfd", "=", "formationEtablissement.cfd") + exists( + selectFrom("formationEtablissement as fe") + .select("cfd") + .distinct() + .innerJoin( + "indicateurEntree", + "id", + "formationEtablissementId" + ) + .where("rentreeScolaire", "in", rentreeScolaire) + .whereRef( + "fe.dispositifId", + "=", + "formationEtablissement.dispositifId" + ) + .whereRef("fe.cfd", "=", "formationEtablissement.cfd") + ) ) - ) : sql`false`, ]) ) diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts index ee2f7f63f..cdb1cce22 100644 --- a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts @@ -144,7 +144,9 @@ export const getFormationsTransformationStatsQuery = ({ cfdRef: "demande.cfd", dispositifIdRef: "demande.dispositifId", codeRegionRef: "dataEtablissement.codeRegion", - codeNiveauDiplomeRef: codeNiveauDiplome ? "dataFormation.codeNiveauDiplome" : undefined, + codeNiveauDiplomeRef: codeNiveauDiplome + ? "dataFormation.codeNiveauDiplome" + : undefined, }).as("positionCadran"), selectNbDemandes(eb).as("nbDemandes"), selectNbEtablissements(eb).as("nbEtablissements"), diff --git a/server/src/modules/data/usecases/getRegion/getRegion.route.ts b/server/src/modules/data/usecases/getRegion/getRegion.route.ts new file mode 100644 index 000000000..8fae1976a --- /dev/null +++ b/server/src/modules/data/usecases/getRegion/getRegion.route.ts @@ -0,0 +1,24 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getRegionSchema } from "./getRegion.schema"; +import { getRegionStats } from "./getRegionsStats.query"; + +export const getRegionRoute = ({ server }: { server: Server }) => { + return createRoute("/region/:codeRegion", { + method: "GET", + schema: getRegionSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const regionsStats = await getRegionStats({ + ...request.params, + ...request.query, + }); + if (!regionsStats) return response.status(404).send(); + response.status(200).send(regionsStats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getRegion/getRegion.schema.ts b/server/src/modules/data/usecases/getRegion/getRegion.schema.ts new file mode 100644 index 000000000..8a29e2716 --- /dev/null +++ b/server/src/modules/data/usecases/getRegion/getRegion.schema.ts @@ -0,0 +1,20 @@ +import { z } from "zod"; +export const getRegionSchema = { + params: z.object({ + codeRegion: z.string(), + }), + querystring: z.object({ + codeDiplome: z.array(z.string()).optional(), + }), + response: { + 200: z.object({ + libelleRegion: z.string(), + effectif: z.coerce.number(), + nbFormations: z.coerce.number(), + tauxPression: z.coerce.number().optional(), + tauxRemplissage: z.coerce.number().optional(), + tauxPoursuiteEtudes: z.coerce.number().optional(), + tauxInsertion6mois: z.coerce.number().optional(), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts b/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts new file mode 100644 index 000000000..f984c4cc9 --- /dev/null +++ b/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts @@ -0,0 +1,92 @@ +import { sql } from "kysely"; + +import { kdb } from "../../../../db/db"; +import { effectifAnnee } from "../../queries/utils/effectifAnnee"; +import { + notHistorique, + notHistoriqueIndicateurRegionSortie, +} from "../../queries/utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; + +export const getRegionStats = async ({ + codeRegion, + codeDiplome, + rentreeScolaire = "2022", + millesimeSortie = "2020_2021", +}: { + codeRegion: string; + codeDiplome?: string[]; + rentreeScolaire?: string; + millesimeSortie?: string; +}) => { + const statsSortie = await kdb + .selectFrom("indicateurRegionSortie") + .innerJoin( + "formation", + "formation.codeFormationDiplome", + "indicateurRegionSortie.cfd" + ) + .where("indicateurRegionSortie.codeRegion", "=", codeRegion) + .$call((q) => { + if (!codeDiplome?.length) return q; + return q.where("formation.codeNiveauDiplome", "in", codeDiplome); + }) + .where("indicateurRegionSortie.millesimeSortie", "=", millesimeSortie) + .where(notHistoriqueIndicateurRegionSortie) + .select([ + selectTauxInsertion6moisAgg("indicateurRegionSortie").as( + "tauxInsertion6mois" + ), + selectTauxPoursuiteAgg("indicateurRegionSortie").as( + "tauxPoursuiteEtudes" + ), + ]) + .executeTakeFirstOrThrow(); + + const stats = await kdb + .selectFrom("formationEtablissement") + .leftJoin( + "formation", + "formation.codeFormationDiplome", + "formationEtablissement.cfd" + ) + .innerJoin("indicateurEntree", (join) => + join + .onRef( + "formationEtablissement.id", + "=", + "indicateurEntree.formationEtablissementId" + ) + .on("indicateurEntree.rentreeScolaire", "=", rentreeScolaire) + ) + .innerJoin( + "etablissement", + "formationEtablissement.UAI", + "etablissement.UAI" + ) + .where("etablissement.codeRegion", "=", codeRegion) + .innerJoin("region", "region.codeRegion", "etablissement.codeRegion") + .$call((q) => { + if (!codeDiplome?.length) return q; + return q.where("formation.codeNiveauDiplome", "in", codeDiplome); + }) + .where(notHistorique) + .select([ + "region.libelleRegion", + sql`COUNT(distinct CONCAT("formationEtablissement"."cfd", "formationEtablissement"."dispositifId"))`.as( + "nbFormations" + ), + sql`SUM(${effectifAnnee({ alias: "indicateurEntree" })}) + `.as("effectif"), + + selectTauxPressionAgg("indicateurEntree").as("tauxPression"), + selectTauxRemplissageAgg("indicateurEntree").as("tauxRemplissage"), + ]) + .groupBy("region.libelleRegion") + .executeTakeFirstOrThrow(); + + return { ...stats, ...statsSortie }; +}; diff --git a/server/src/modules/data/usecases/getRegions/getRegions.query.ts b/server/src/modules/data/usecases/getRegions/getRegions.query.ts new file mode 100644 index 000000000..2445831b1 --- /dev/null +++ b/server/src/modules/data/usecases/getRegions/getRegions.query.ts @@ -0,0 +1,17 @@ +import { kdb } from "../../../../db/db"; + +export const getRegions = () => { + return kdb + .selectFrom("region") + .innerJoin("etablissement", "etablissement.codeRegion", "region.codeRegion") + .innerJoin( + "formationEtablissement", + "formationEtablissement.UAI", + "etablissement.UAI" + ) + .select(["region.codeRegion as value", "region.libelleRegion as label"]) + .where("region.codeRegion", "is not", null) + .distinct() + .orderBy("label", "asc") + .execute(); +}; diff --git a/server/src/modules/data/usecases/getRegions/getRegions.route.ts b/server/src/modules/data/usecases/getRegions/getRegions.route.ts new file mode 100644 index 000000000..d0a16b889 --- /dev/null +++ b/server/src/modules/data/usecases/getRegions/getRegions.route.ts @@ -0,0 +1,20 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { getRegions } from "./getRegions.query"; +import { getRegionsSchema } from "./getRegions.schema"; + +export const getRegionsRoute = ({ server }: { server: Server }) => { + return createRoute("/regions", { + method: "GET", + schema: getRegionsSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (_, response) => { + const regions = await getRegions(); + response.status(200).send(regions); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getRegions/getRegions.schema.ts b/server/src/modules/data/usecases/getRegions/getRegions.schema.ts new file mode 100644 index 000000000..c67d4dd4b --- /dev/null +++ b/server/src/modules/data/usecases/getRegions/getRegions.schema.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const getRegionsSchema = { + response: { + 200: z.array( + z.object({ + label: z.string(), + value: z.string(), + }) + ), + }, +}; diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts index 48278110e..c4875a7d4 100644 --- a/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts @@ -701,10 +701,10 @@ const findFiltersInDb = async ({ ]), motif ? eb.or( - motif.map( - (m) => sql`${m} = any(${eb.ref("demande.motif")})` + motif.map( + (m) => sql`${m} = any(${eb.ref("demande.motif")})` + ) ) - ) : sql`false`, ]); }) From 587b099a996e13b1fc8731b8dcefca846af9021c Mon Sep 17 00:00:00 2001 From: Florian Date: Thu, 16 Nov 2023 18:20:55 +0100 Subject: [PATCH 04/20] refactor: migrate routes --- server/src/modules/data/index.ts | 4 +- .../getFormations/formations.routes.ts | 2 +- server/src/modules/intentions/index.ts | 25 +++- .../intentions/routes/demande.routes.ts | 121 ------------------ .../intentions/routes/validation.routes.ts | 48 ------- .../countDemandes}/countDemandes.query.ts | 6 +- .../countDemandes/countDemandes.route.ts | 27 ++++ .../countDemandes/countDemandes.schema.ts | 11 ++ .../deleteDemande/deleteDemande.route.ts | 25 ++++ .../deleteDemande/deleteDemande.schema.ts | 7 + .../getDemande}/findDemande.query.ts | 8 +- .../usecases/getDemande/getDemande.route.ts | 35 +++++ .../usecases/getDemande/getDemande.schema.ts | 72 +++++++++++ .../getDemandes}/findDemandes.query.ts | 8 +- .../usecases/getDemandes/getDemandes.route.ts | 30 +++++ .../getDemandes/getDemandes.schema.ts | 57 +++++++++ .../usecases/getEtab/getEtab.route.ts | 23 ++++ .../usecases/getEtab/getEtab.schema.ts | 14 ++ .../searchDiplome/searchDiplome.route.ts | 23 ++++ .../searchDiplome/searchDiplome.schema.ts | 26 ++++ .../usecases/searchEtab/searchEtab.route.ts | 23 ++++ .../usecases/searchEtab/searchEtab.schema.ts | 16 +++ .../submitDemande/submitDemande.route.ts | 29 +++++ .../submitDemande/submitDemande.schema.ts | 36 ++++++ .../submitDraftDemande.route.ts | 29 +++++ .../submitDraftDemande.schema.ts | 36 ++++++ 26 files changed, 553 insertions(+), 188 deletions(-) delete mode 100644 server/src/modules/intentions/routes/demande.routes.ts delete mode 100644 server/src/modules/intentions/routes/validation.routes.ts rename server/src/modules/intentions/{queries => usecases/countDemandes}/countDemandes.query.ts (80%) create mode 100644 server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts create mode 100644 server/src/modules/intentions/usecases/countDemandes/countDemandes.schema.ts create mode 100644 server/src/modules/intentions/usecases/deleteDemande/deleteDemande.route.ts create mode 100644 server/src/modules/intentions/usecases/deleteDemande/deleteDemande.schema.ts rename server/src/modules/intentions/{queries => usecases/getDemande}/findDemande.query.ts (95%) create mode 100644 server/src/modules/intentions/usecases/getDemande/getDemande.route.ts create mode 100644 server/src/modules/intentions/usecases/getDemande/getDemande.schema.ts rename server/src/modules/intentions/{queries => usecases/getDemandes}/findDemandes.query.ts (91%) create mode 100644 server/src/modules/intentions/usecases/getDemandes/getDemandes.route.ts create mode 100644 server/src/modules/intentions/usecases/getDemandes/getDemandes.schema.ts create mode 100644 server/src/modules/intentions/usecases/getEtab/getEtab.route.ts create mode 100644 server/src/modules/intentions/usecases/getEtab/getEtab.schema.ts create mode 100644 server/src/modules/intentions/usecases/searchDiplome/searchDiplome.route.ts create mode 100644 server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts create mode 100644 server/src/modules/intentions/usecases/searchEtab/searchEtab.route.ts create mode 100644 server/src/modules/intentions/usecases/searchEtab/searchEtab.schema.ts create mode 100644 server/src/modules/intentions/usecases/submitDemande/submitDemande.route.ts create mode 100644 server/src/modules/intentions/usecases/submitDemande/submitDemande.schema.ts create mode 100644 server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.route.ts create mode 100644 server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.schema.ts diff --git a/server/src/modules/data/index.ts b/server/src/modules/data/index.ts index 0740f3e55..fcbec3a2c 100644 --- a/server/src/modules/data/index.ts +++ b/server/src/modules/data/index.ts @@ -8,7 +8,7 @@ import { getDepartementRoute } from "./usecases/getDepartement/getDepartement.ro import { getDepartementsRoute } from "./usecases/getDepartements/getDepartements.route"; import { getEtablissementRoute } from "./usecases/getEtablissement/etablissements.routes"; import { getEtablissementsRoutes } from "./usecases/getEtablissements/getEtablissements.routes"; -import { getFormationsRoutes } from "./usecases/getFormations/formations.routes"; +import { getFormationsRoute } from "./usecases/getFormations/formations.routes"; export const registerFormationModule = ({ server }: { server: Server }) => { panoramaRoutes({ server }); @@ -18,7 +18,7 @@ export const registerFormationModule = ({ server }: { server: Server }) => { restitutionIntentionsRoutes({ server }); return { - ...getFormationsRoutes({ server }), + ...getFormationsRoute({ server }), ...getEtablissementsRoutes({ server }), ...getEtablissementRoute({ server }), ...getDepartementRoute({ server }), diff --git a/server/src/modules/data/usecases/getFormations/formations.routes.ts b/server/src/modules/data/usecases/getFormations/formations.routes.ts index 5fad0eed8..6eb4ae588 100644 --- a/server/src/modules/data/usecases/getFormations/formations.routes.ts +++ b/server/src/modules/data/usecases/getFormations/formations.routes.ts @@ -4,7 +4,7 @@ import { Server } from "../../../../server"; import { getFormationSchema } from "./getFormation.schema"; import { getFormations } from "./getFormations.usecase"; -export const getFormationsRoutes = ({ server }: { server: Server }) => { +export const getFormationsRoute = ({ server }: { server: Server }) => { return createRoute("/formations", { method: "GET", schema: getFormationSchema, diff --git a/server/src/modules/intentions/index.ts b/server/src/modules/intentions/index.ts index d30a2e375..25c6eb7e4 100644 --- a/server/src/modules/intentions/index.ts +++ b/server/src/modules/intentions/index.ts @@ -1,9 +1,24 @@ import { Server } from "../../server"; -import { demandeRoutes } from "./routes/demande.routes"; -import { validationRoutes } from "./routes/validation.routes"; +import { countDemandesRoute } from "./usecases/countDemandes/countDemandes.route"; +import { deleteDemandeRoute } from "./usecases/deleteDemande/deleteDemande.route"; +import { getDemandeRoute } from "./usecases/getDemande/getDemande.route"; +import { getDemandesRoute } from "./usecases/getDemandes/getDemandes.route"; +import { getEtabRoute } from "./usecases/getEtab/getEtab.route"; +import { searchDiplomeRoute } from "./usecases/searchDiplome/searchDiplome.route"; +import { searchEtabRoute } from "./usecases/searchEtab/searchEtab.route"; +import { submitDemandeRoute } from "./usecases/submitDemande/submitDemande.route"; +import { submitDraftDemandeRoute } from "./usecases/submitDraftDemande/submitDraftDemande.route"; export const registerIntentionsModule = ({ server }: { server: Server }) => { - validationRoutes({ server }); - demandeRoutes({ server }); - return {}; + return { + ...submitDemandeRoute({ server }), + ...submitDraftDemandeRoute(server), + ...getDemandeRoute(server), + ...getDemandesRoute(server), + ...countDemandesRoute(server), + ...deleteDemandeRoute(server), + ...getEtabRoute(server), + ...searchEtabRoute(server), + ...searchDiplomeRoute(server), + }; }; diff --git a/server/src/modules/intentions/routes/demande.routes.ts b/server/src/modules/intentions/routes/demande.routes.ts deleted file mode 100644 index c45fcc6f2..000000000 --- a/server/src/modules/intentions/routes/demande.routes.ts +++ /dev/null @@ -1,121 +0,0 @@ -import Boom from "@hapi/boom"; -import { getPermissionScope, guardScope, ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { hasPermissionHandler } from "../../core"; -import { countDemandes } from "../queries/countDemandes.query"; -import { findDemande } from "../queries/findDemande.query"; -import { findDemandes } from "../queries/findDemandes.query"; -import { deleteDemande } from "../usecases/deleteDemande/deleteDemande.usecase"; -import { submitDemande } from "../usecases/submitDemande/submitDemande.usecase"; -import { submitDraftDemande } from "../usecases/submitDraftDemande/submitDraftDemande.usecase"; - -export const demandeRoutes = ({ server }: { server: Server }) => { - server.post( - "/demande/submit", - { - schema: ROUTES_CONFIG.submitDemande, - preHandler: hasPermissionHandler("intentions/ecriture"), - }, - async (request, response) => { - const { demande } = request.body; - if (!request.user) throw Boom.unauthorized(); - - const result = await submitDemande({ - demande, - user: request.user, - }); - response.status(200).send(result); - } - ); - - server.post( - "/demande/draft", - { - schema: ROUTES_CONFIG.submitDraftDemande, - preHandler: hasPermissionHandler("intentions/ecriture"), - }, - async (request, response) => { - const { demande } = request.body; - if (!request.user) throw Boom.unauthorized(); - - const result = await submitDraftDemande({ - demande, - user: request.user, - }); - response.status(200).send(result); - } - ); - - server.get( - "/demande/:id", - { - schema: ROUTES_CONFIG.getDemande, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const user = request.user; - if (!user) throw Boom.forbidden(); - const demande = await findDemande({ id: request.params.id, user }); - if (!demande) return response.status(404).send(); - - const scope = getPermissionScope(user.role, "intentions/ecriture"); - const canEdit = guardScope(scope?.default, { - user: () => user.id === demande.createurId, - region: () => user.codeRegion === demande.codeRegion, - national: () => true, - }); - - response.status(200).send({ ...demande, canEdit }); - } - ); - - server.delete( - "/demande/:id", - { - schema: ROUTES_CONFIG.deleteDemande, - preHandler: hasPermissionHandler("intentions/ecriture"), - }, - async (request, response) => { - const user = request.user; - if (!user) throw Boom.forbidden(); - await deleteDemande({ id: request.params.id, user }); - response.status(200).send(); - } - ); - - server.get( - "/demandes", - { - schema: ROUTES_CONFIG.getDemandes, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { order, orderBy, ...rest } = request.query; - if (!request.user) throw Boom.forbidden(); - - const result = await findDemandes({ - ...rest, - user: request.user, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(result); - } - ); - - server.get( - "/demandes/count", - { - schema: ROUTES_CONFIG.countDemandes, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - if (!request.user) throw Boom.forbidden(); - - const result = await countDemandes({ - user: request.user, - }); - response.status(200).send(result); - } - ); -}; diff --git a/server/src/modules/intentions/routes/validation.routes.ts b/server/src/modules/intentions/routes/validation.routes.ts deleted file mode 100644 index e37226134..000000000 --- a/server/src/modules/intentions/routes/validation.routes.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { hasPermissionHandler } from "../../core"; -import { getEtab } from "../usecases/getEtab/getEtab.usecase"; -import { searchDiplome } from "../usecases/searchDiplome/searchDiplome.usecase"; -import { searchEtab } from "../usecases/searchEtab/searchEtab.usecase"; - -export const validationRoutes = ({ server }: { server: Server }) => { - server.get( - "/etab/search/:search", - { - schema: ROUTES_CONFIG.searchEtab, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { search } = request.params; - const result = await searchEtab({ search, user: request.user }); - response.status(200).send(result); - } - ); - - server.get( - "/etab/:uai", - { - schema: ROUTES_CONFIG.getEtab, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { uai } = request.params; - const result = await getEtab({ uai }); - response.status(200).send(result); - } - ); - - server.get( - "/diplome/search/:search", - { - schema: ROUTES_CONFIG.searchDiplome, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { search } = request.params; - const result = await searchDiplome({ search }); - response.status(200).send(result); - } - ); -}; diff --git a/server/src/modules/intentions/queries/countDemandes.query.ts b/server/src/modules/intentions/usecases/countDemandes/countDemandes.query.ts similarity index 80% rename from server/src/modules/intentions/queries/countDemandes.query.ts rename to server/src/modules/intentions/usecases/countDemandes/countDemandes.query.ts index faf19272b..f91f7e7d8 100644 --- a/server/src/modules/intentions/queries/countDemandes.query.ts +++ b/server/src/modules/intentions/usecases/countDemandes/countDemandes.query.ts @@ -1,8 +1,8 @@ import { sql } from "kysely"; -import { kdb } from "../../../db/db"; -import { RequestUser } from "../../core/model/User"; -import { isDemandeSelectable } from "../../utils/isDemandeSelectable"; +import { kdb } from "../../../../db/db"; +import { RequestUser } from "../../../core/model/User"; +import { isDemandeSelectable } from "../../../utils/isDemandeSelectable"; export const countDemandes = async ({ user, diff --git a/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts b/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts new file mode 100644 index 000000000..ac4de1915 --- /dev/null +++ b/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts @@ -0,0 +1,27 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { countDemandes } from "./countDemandes.query"; +import { countDemandesSchema } from "./countDemandes.schema"; + +export const countDemandesRoute = (server: Server) => { + return createRoute("/", { + method: "GET", + schema: countDemandesSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + if (!request.user) throw Boom.forbidden(); + + const result = await countDemandes({ + user: request.user, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/countDemandes/countDemandes.schema.ts b/server/src/modules/intentions/usecases/countDemandes/countDemandes.schema.ts new file mode 100644 index 000000000..196a39069 --- /dev/null +++ b/server/src/modules/intentions/usecases/countDemandes/countDemandes.schema.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +export const countDemandesSchema = { + response: { + 200: z.object({ + total: z.string(), + draft: z.string(), + submitted: z.string(), + }), + }, +}; diff --git a/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.route.ts b/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.route.ts new file mode 100644 index 000000000..d4bcb3010 --- /dev/null +++ b/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.route.ts @@ -0,0 +1,25 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { deleteDemandeSchema } from "./deleteDemande.schema"; +import { deleteDemande } from "./deleteDemande.usecase"; + +export const deleteDemandeRoute = (server: Server) => { + return createRoute("/demande/:id", { + method: "DELETE", + schema: deleteDemandeSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/ecriture"), + handler: async (request, response) => { + const user = request.user; + if (!user) throw Boom.forbidden(); + await deleteDemande({ id: request.params.id, user }); + response.status(200).send(); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.schema.ts b/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.schema.ts new file mode 100644 index 000000000..98e9408e8 --- /dev/null +++ b/server/src/modules/intentions/usecases/deleteDemande/deleteDemande.schema.ts @@ -0,0 +1,7 @@ +import { z } from "zod"; +export const deleteDemandeSchema = { + params: z.object({ id: z.string() }), + response: { + 200: z.void(), + }, +}; diff --git a/server/src/modules/intentions/queries/findDemande.query.ts b/server/src/modules/intentions/usecases/getDemande/findDemande.query.ts similarity index 95% rename from server/src/modules/intentions/queries/findDemande.query.ts rename to server/src/modules/intentions/usecases/getDemande/findDemande.query.ts index f7148679b..b5682dce8 100644 --- a/server/src/modules/intentions/queries/findDemande.query.ts +++ b/server/src/modules/intentions/usecases/getDemande/findDemande.query.ts @@ -5,10 +5,10 @@ import { jsonObjectFrom, } from "kysely/helpers/postgres"; -import { kdb } from "../../../db/db"; -import { cleanNull } from "../../../utils/noNull"; -import { RequestUser } from "../../core/model/User"; -import { isDemandeSelectable } from "../../utils/isDemandeSelectable"; +import { kdb } from "../../../../db/db"; +import { cleanNull } from "../../../../utils/noNull"; +import { RequestUser } from "../../../core/model/User"; +import { isDemandeSelectable } from "../../../utils/isDemandeSelectable"; export const findDemande = async ({ id, diff --git a/server/src/modules/intentions/usecases/getDemande/getDemande.route.ts b/server/src/modules/intentions/usecases/getDemande/getDemande.route.ts new file mode 100644 index 000000000..6e79b0ccf --- /dev/null +++ b/server/src/modules/intentions/usecases/getDemande/getDemande.route.ts @@ -0,0 +1,35 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; +import { getPermissionScope, guardScope } from "shared"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { findDemande } from "./findDemande.query"; +import { getDemandeSchema } from "./getDemande.schema"; + +export const getDemandeRoute = (server: Server) => { + return createRoute("/demande/:id", { + method: "GET", + schema: getDemandeSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + const user = request.user; + if (!user) throw Boom.forbidden(); + const demande = await findDemande({ id: request.params.id, user }); + if (!demande) return response.status(404).send(); + + const scope = getPermissionScope(user.role, "intentions/ecriture"); + const canEdit = guardScope(scope?.default, { + user: () => user.id === demande.createurId, + region: () => user.codeRegion === demande.codeRegion, + national: () => true, + }); + + response.status(200).send({ ...demande, canEdit }); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/getDemande/getDemande.schema.ts b/server/src/modules/intentions/usecases/getDemande/getDemande.schema.ts new file mode 100644 index 000000000..cb1037bdc --- /dev/null +++ b/server/src/modules/intentions/usecases/getDemande/getDemande.schema.ts @@ -0,0 +1,72 @@ +import { z } from "zod"; + +const EtablissementMetadataSchema = z + .object({ + libelle: z.string().optional(), + commune: z.string().optional(), + }) + .optional(); + +const FormationMetadataSchema = z + .object({ + libelle: z.string().optional(), + isFCIL: z.boolean().optional(), + dispositifs: z + .array( + z.object({ + codeDispositif: z.string().optional(), + libelleDispositif: z.string().optional(), + }) + ) + .optional(), + }) + .optional(); + +const MetadataSchema = z.object({ + etablissement: EtablissementMetadataSchema, + formation: FormationMetadataSchema, + etablissementCompensation: EtablissementMetadataSchema, + formationCompensation: FormationMetadataSchema, +}); + +const DemandeSchema = z.object({ + id: z.string(), + createdAt: z.string(), + status: z.string(), + uai: z.string(), + cfd: z.string(), + dispositifId: z.string(), + libelleFCIL: z.string().optional(), + rentreeScolaire: z.coerce.number(), + typeDemande: z.string(), + compensationUai: z.string().optional(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationRentreeScolaire: z.coerce.number().optional(), + motif: z.array(z.string()), + autreMotif: z.string().optional(), + libelleColoration: z.string().optional(), + coloration: z.boolean(), + amiCma: z.boolean(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + mixte: z.boolean().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), +}); + +export const getDemandeSchema = { + params: z.object({ id: z.string() }), + response: { + 200: DemandeSchema.partial().merge( + z.object({ + metadata: MetadataSchema, + canEdit: z.boolean(), + }) + ), + }, +}; diff --git a/server/src/modules/intentions/queries/findDemandes.query.ts b/server/src/modules/intentions/usecases/getDemandes/findDemandes.query.ts similarity index 91% rename from server/src/modules/intentions/queries/findDemandes.query.ts rename to server/src/modules/intentions/usecases/getDemandes/findDemandes.query.ts index 75d949b28..526782bd8 100644 --- a/server/src/modules/intentions/queries/findDemandes.query.ts +++ b/server/src/modules/intentions/usecases/getDemandes/findDemandes.query.ts @@ -1,10 +1,10 @@ import { sql } from "kysely"; import { jsonObjectFrom } from "kysely/helpers/postgres"; -import { kdb } from "../../../db/db"; -import { cleanNull } from "../../../utils/noNull"; -import { RequestUser } from "../../core/model/User"; -import { isDemandeSelectable } from "../../utils/isDemandeSelectable"; +import { kdb } from "../../../../db/db"; +import { cleanNull } from "../../../../utils/noNull"; +import { RequestUser } from "../../../core/model/User"; +import { isDemandeSelectable } from "../../../utils/isDemandeSelectable"; export const findDemandes = async ({ status, diff --git a/server/src/modules/intentions/usecases/getDemandes/getDemandes.route.ts b/server/src/modules/intentions/usecases/getDemandes/getDemandes.route.ts new file mode 100644 index 000000000..c48c8120a --- /dev/null +++ b/server/src/modules/intentions/usecases/getDemandes/getDemandes.route.ts @@ -0,0 +1,30 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { findDemandes } from "./findDemandes.query"; +import { getDemandesSchema } from "./getDemandes.schema"; + +export const getDemandesRoute = (server: Server) => { + return createRoute("/demandes", { + method: "GET", + schema: getDemandesSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + const { order, orderBy, ...rest } = request.query; + if (!request.user) throw Boom.forbidden(); + + const result = await findDemandes({ + ...rest, + user: request.user, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/getDemandes/getDemandes.schema.ts b/server/src/modules/intentions/usecases/getDemandes/getDemandes.schema.ts new file mode 100644 index 000000000..a8fe74ab1 --- /dev/null +++ b/server/src/modules/intentions/usecases/getDemandes/getDemandes.schema.ts @@ -0,0 +1,57 @@ +import { z } from "zod"; + +const DemandesItem = z.object({ + id: z.string(), + cfd: z.string().optional(), + libelleDiplome: z.string().optional(), + libelleEtablissement: z.string().optional(), + libelleDepartement: z.string().optional(), + libelleDispositif: z.string().optional(), + libelleFCIL: z.string().optional(), + uai: z.string().optional(), + createdAt: z.string(), + updatedAt: z.string(), + createurId: z.string(), + status: z.string(), + typeDemande: z.string().optional(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationUai: z.string().optional(), + compensationRentreeScolaire: z.coerce.number().optional(), + idCompensation: z.string().optional(), + typeCompensation: z.string().optional(), + dispositifId: z.string().optional(), + rentreeScolaire: z.coerce.number().optional(), + motif: z.array(z.string()).optional(), + autreMotif: z.string().optional(), + libelleColoration: z.string().optional(), + coloration: z.boolean().optional(), + amiCma: z.boolean().optional(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + mixte: z.boolean().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), + codeRegion: z.string(), + codeAcademie: z.string().optional(), +}); + +export const getDemandesSchema = { + querystring: z.object({ + status: z.enum(["draft", "submitted"]).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: DemandesItem.keyof().optional(), + offset: z.coerce.number().optional(), + limit: z.coerce.number().optional(), + }), + response: { + 200: z.object({ + count: z.coerce.number(), + demandes: z.array(DemandesItem), + }), + }, +}; diff --git a/server/src/modules/intentions/usecases/getEtab/getEtab.route.ts b/server/src/modules/intentions/usecases/getEtab/getEtab.route.ts new file mode 100644 index 000000000..05ff6d76f --- /dev/null +++ b/server/src/modules/intentions/usecases/getEtab/getEtab.route.ts @@ -0,0 +1,23 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getEtabSchema } from "./getEtab.schema"; +import { getEtab } from "./getEtab.usecase"; + +export const getEtabRoute = (server: Server) => { + return createRoute("/api/etab/:uai", { + method: "GET", + schema: getEtabSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + const { uai } = request.params; + const result = await getEtab({ uai }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/getEtab/getEtab.schema.ts b/server/src/modules/intentions/usecases/getEtab/getEtab.schema.ts new file mode 100644 index 000000000..876286eaf --- /dev/null +++ b/server/src/modules/intentions/usecases/getEtab/getEtab.schema.ts @@ -0,0 +1,14 @@ +import { z } from "zod"; + +export const getEtabSchema = { + params: z.object({ + uai: z.string(), + }), + response: { + 200: z.object({ + value: z.string(), + label: z.string().optional(), + commune: z.string().optional(), + }), + }, +}; diff --git a/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.route.ts b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.route.ts new file mode 100644 index 000000000..b7fdc4d43 --- /dev/null +++ b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.route.ts @@ -0,0 +1,23 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { searchDiplomeSchema } from "./searchDiplome.schema"; +import { searchDiplome } from "./searchDiplome.usecase"; + +export const searchDiplomeRoute = (server: Server) => { + return createRoute("/diplome/search/:search", { + method: "GET", + schema: searchDiplomeSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + const { search } = request.params; + const result = await searchDiplome({ search }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts new file mode 100644 index 000000000..7549f6a14 --- /dev/null +++ b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; + +export const searchDiplomeSchema = { + params: z.object({ + search: z.string(), + }), + response: { + 200: z.array( + z.object({ + value: z.string(), + label: z.string(), + isSpecialite: z.boolean(), + isFCIL: z.boolean(), + dateFermeture: z.string(), + dispositifs: z + .array( + z.object({ + codeDispositif: z.string().optional(), + libelleDispositif: z.string().optional(), + }) + ) + .optional(), + }) + ), + }, +}; diff --git a/server/src/modules/intentions/usecases/searchEtab/searchEtab.route.ts b/server/src/modules/intentions/usecases/searchEtab/searchEtab.route.ts new file mode 100644 index 000000000..5b194968b --- /dev/null +++ b/server/src/modules/intentions/usecases/searchEtab/searchEtab.route.ts @@ -0,0 +1,23 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { searchEtabSchema } from "./searchEtab.schema"; +import { searchEtab } from "./searchEtab.usecase"; + +export const searchEtabRoute = (server: Server) => { + return createRoute("/etab/search/:search", { + method: "GET", + schema: searchEtabSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/lecture"), + handler: async (request, response) => { + const { search } = request.params; + const result = await searchEtab({ search, user: request.user }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/searchEtab/searchEtab.schema.ts b/server/src/modules/intentions/usecases/searchEtab/searchEtab.schema.ts new file mode 100644 index 000000000..0744276e2 --- /dev/null +++ b/server/src/modules/intentions/usecases/searchEtab/searchEtab.schema.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +export const searchEtabSchema = { + params: z.object({ + search: z.string(), + }), + response: { + 200: z.array( + z.object({ + value: z.string(), + label: z.string().optional(), + commune: z.string().optional(), + }) + ), + }, +}; diff --git a/server/src/modules/intentions/usecases/submitDemande/submitDemande.route.ts b/server/src/modules/intentions/usecases/submitDemande/submitDemande.route.ts new file mode 100644 index 000000000..946a9102e --- /dev/null +++ b/server/src/modules/intentions/usecases/submitDemande/submitDemande.route.ts @@ -0,0 +1,29 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { submitDemandeSchema } from "./submitDemande.schema"; +import { submitDemande } from "./submitDemande.usecase"; + +export const submitDemandeRoute = ({ server }: { server: Server }) => { + return createRoute("/demande/submit", { + method: "POST", + schema: submitDemandeSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/ecriture"), + handler: async (request, response) => { + const { demande } = request.body; + if (!request.user) throw Boom.unauthorized(); + + const result = await submitDemande({ + demande, + user: request.user, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/submitDemande/submitDemande.schema.ts b/server/src/modules/intentions/usecases/submitDemande/submitDemande.schema.ts new file mode 100644 index 000000000..a9fe16790 --- /dev/null +++ b/server/src/modules/intentions/usecases/submitDemande/submitDemande.schema.ts @@ -0,0 +1,36 @@ +import { z } from "zod"; + +export const submitDemandeSchema = { + body: z.object({ + demande: z.object({ + id: z.string().optional(), + uai: z.string(), + cfd: z.string(), + dispositifId: z.string(), + libelleFCIL: z.string().optional(), + rentreeScolaire: z.coerce.number(), + typeDemande: z.string(), + compensationUai: z.string().optional(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationRentreeScolaire: z.coerce.number().optional(), + motif: z.array(z.string()), + autreMotif: z.string().optional(), + libelleColoration: z.string().optional(), + coloration: z.boolean(), + amiCma: z.boolean(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + mixte: z.boolean().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), + }), + }), + response: { + 200: z.object({ id: z.string() }), + }, +}; diff --git a/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.route.ts b/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.route.ts new file mode 100644 index 000000000..f7c06a2a1 --- /dev/null +++ b/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.route.ts @@ -0,0 +1,29 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { submitDraftDemandeSchema } from "./submitDraftDemande.schema"; +import { submitDraftDemande } from "./submitDraftDemande.usecase"; + +export const submitDraftDemandeRoute = (server: Server) => { + return createRoute("/demande/draft", { + method: "POST", + schema: submitDraftDemandeSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("intentions/ecriture"), + handler: async (request, response) => { + const { demande } = request.body; + if (!request.user) throw Boom.unauthorized(); + + const result = await submitDraftDemande({ + demande, + user: request.user, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.schema.ts b/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.schema.ts new file mode 100644 index 000000000..69136ec67 --- /dev/null +++ b/server/src/modules/intentions/usecases/submitDraftDemande/submitDraftDemande.schema.ts @@ -0,0 +1,36 @@ +import { z } from "zod"; + +export const submitDraftDemandeSchema = { + body: z.object({ + demande: z.object({ + id: z.string().optional(), + uai: z.string(), + cfd: z.string(), + dispositifId: z.string(), + libelleFCIL: z.string().optional(), + rentreeScolaire: z.coerce.number(), + typeDemande: z.string(), + compensationUai: z.string().optional(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationRentreeScolaire: z.coerce.number().optional(), + motif: z.array(z.string()), + autreMotif: z.string().optional(), + libelleColoration: z.string().optional(), + coloration: z.boolean(), + amiCma: z.boolean(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + mixte: z.boolean().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), + }), + }), + response: { + 200: z.object({ id: z.string() }), + }, +}; From 018a070afdbc5fab42cf9d41a8b004356db230e1 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 20 Nov 2023 12:41:40 +0100 Subject: [PATCH 05/20] wip --- server/src/modules/core/index.ts | 17 ++- server/src/modules/core/routes/auth.routes.ts | 101 ------------------ .../activateUser/activateUser.route.ts | 26 +++++ .../activateUser/activateUser.schema.ts | 11 ++ .../checkActivationToken.route.ts | 24 +++++ .../checkActivationToken.schema.ts | 12 +++ .../core/usecases/login/login.route.ts | 30 ++++++ .../core/usecases/login/login.schema.ts | 12 +++ .../core/usecases/logout/logout.route.ts | 26 +++++ .../core/usecases/logout/logout.schema.ts | 6 ++ .../resetPassword/resetPassword.route.ts | 21 ++++ .../resetPassword/resetPassword.schema.ts | 11 ++ .../sendResetPassword.route.ts | 21 ++++ .../sendResetPassword.schema.ts | 8 ++ .../core/usecases/whoAmI/whoAmI.route.ts | 19 ++++ .../core/usecases/whoAmI/whoAmI.schema.ts | 17 +++ .../countDemandes/countDemandes.route.ts | 2 +- 17 files changed, 259 insertions(+), 105 deletions(-) delete mode 100644 server/src/modules/core/routes/auth.routes.ts create mode 100644 server/src/modules/core/usecases/activateUser/activateUser.route.ts create mode 100644 server/src/modules/core/usecases/activateUser/activateUser.schema.ts create mode 100644 server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts create mode 100644 server/src/modules/core/usecases/checkActivationToken/checkActivationToken.schema.ts create mode 100644 server/src/modules/core/usecases/login/login.route.ts create mode 100644 server/src/modules/core/usecases/login/login.schema.ts create mode 100644 server/src/modules/core/usecases/logout/logout.route.ts create mode 100644 server/src/modules/core/usecases/logout/logout.schema.ts create mode 100644 server/src/modules/core/usecases/resetPassword/resetPassword.route.ts create mode 100644 server/src/modules/core/usecases/resetPassword/resetPassword.schema.ts create mode 100644 server/src/modules/core/usecases/sendResetPassword/sendResetPassword.route.ts create mode 100644 server/src/modules/core/usecases/sendResetPassword/sendResetPassword.schema.ts create mode 100644 server/src/modules/core/usecases/whoAmI/whoAmI.route.ts create mode 100644 server/src/modules/core/usecases/whoAmI/whoAmI.schema.ts diff --git a/server/src/modules/core/index.ts b/server/src/modules/core/index.ts index 1b27943d8..cdab83e0d 100644 --- a/server/src/modules/core/index.ts +++ b/server/src/modules/core/index.ts @@ -1,13 +1,24 @@ import { Server } from "../../server"; -import { authRoutes } from "./routes/auth.routes"; +import { activateUserRoute } from "./usecases/activateUser/activateUser.route"; +import { checkActivationTokenRoute } from "./usecases/checkActivationToken/checkActivationToken.route"; import { homeRoute } from "./usecases/home/home.route"; +import { loginRoute } from "./usecases/login/login.route"; +import { logoutRoute } from "./usecases/logout/logout.route"; +import { resetPasswordRoute } from "./usecases/resetPassword/resetPassword.route"; +import { sendResetPasswordRoute } from "./usecases/sendResetPassword/sendResetPassword.route"; +import { whoAmIRoute } from "./usecases/whoAmI/whoAmI.route"; export { extractUserInRequest } from "./utils/extractUserInRequest/extractUserInRequest"; export const registerCoreModule = ({ server }: { server: Server }) => { - authRoutes({ server }); - return { ...homeRoute({ server }), + ...activateUserRoute(server), + ...checkActivationTokenRoute(server), + ...loginRoute(server), + ...logoutRoute(server), + ...resetPasswordRoute(server), + ...sendResetPasswordRoute(server), + ...whoAmIRoute(server), }; }; diff --git a/server/src/modules/core/routes/auth.routes.ts b/server/src/modules/core/routes/auth.routes.ts deleted file mode 100644 index fa6a35cc3..000000000 --- a/server/src/modules/core/routes/auth.routes.ts +++ /dev/null @@ -1,101 +0,0 @@ -import cookie from "cookie"; -import { ROUTES_CONFIG } from "shared"; - -import { Server } from "../../../server"; -import { activateUser } from "../usecases/activateUser/activateUser.usecase"; -import { checkActivationToken } from "../usecases/checkActivationToken/checkActivationToken.usecase"; -import { login } from "../usecases/login/login.usecase"; -import { resetPassword } from "../usecases/resetPassword/resetPassword.usecase"; -import { sendResetPassword } from "../usecases/sendResetPassword/sendResetPassword.usecase"; - -export const authRoutes = ({ server }: { server: Server }) => { - server.post( - "/auth/login", - { schema: ROUTES_CONFIG.login }, - async (request, response) => { - const { email, password } = request.body; - - const token = await login({ email, password }); - const cookies = cookie.serialize("Authorization", token, { - maxAge: 30 * 24 * 3600000, - httpOnly: true, - sameSite: "lax", - secure: true, - path: "/", - }); - response.status(200).header("set-cookie", cookies).send({ token }); - } - ); - - server.post( - "/auth/logout", - { schema: ROUTES_CONFIG.logout }, - async (_, response) => { - const cookies = cookie.serialize("Authorization", "", { - maxAge: 30 * 24 * 3600000, - httpOnly: true, - sameSite: "lax", - secure: true, - path: "/", - }); - response.status(200).header("set-cookie", cookies).send(); - } - ); - - server.get( - "/auth/whoAmI", - { schema: ROUTES_CONFIG.whoAmI }, - async (request, response) => { - const user = request.user; - response.status(200).send(user && { user }); - } - ); - - server.post( - "/auth/activate", - { schema: ROUTES_CONFIG.activateUser }, - async (request, response) => { - const { password, repeatPassword, activationToken } = request.body; - - await activateUser({ - password, - repeatPassword, - activationToken, - }); - response.status(200).send(); - } - ); - - server.get( - "/auth/check-activation-token", - { schema: ROUTES_CONFIG.checkActivationToken }, - async (request, response) => { - const { activationToken } = request.query; - - const valid = await checkActivationToken({ - activationToken, - }); - response.status(200).send({ valid }); - } - ); - - server.post( - "/auth/send-reset-password", - { schema: ROUTES_CONFIG.sendResetPassword }, - async (request, response) => { - const { email } = request.body; - await sendResetPassword({ email }); - response.status(200).send(); - } - ); - - server.post( - "/auth/reset-password", - { schema: ROUTES_CONFIG.resetPassword }, - async (request, response) => { - const { password, repeatPassword, resetPasswordToken } = request.body; - await resetPassword({ password, repeatPassword, resetPasswordToken }); - response.status(200).send(); - } - ); -}; diff --git a/server/src/modules/core/usecases/activateUser/activateUser.route.ts b/server/src/modules/core/usecases/activateUser/activateUser.route.ts new file mode 100644 index 000000000..379bdec00 --- /dev/null +++ b/server/src/modules/core/usecases/activateUser/activateUser.route.ts @@ -0,0 +1,26 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { activateUserSchema } from "./activateUser.schema"; +import { activateUser } from "./activateUser.usecase"; + +export const activateUserRoute = (server: Server) => { + return createRoute("/auth/activate", { + method: "POST", + schema: activateUserSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { password, repeatPassword, activationToken } = request.body; + + await activateUser({ + password, + repeatPassword, + activationToken, + }); + response.status(200).send(); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/activateUser/activateUser.schema.ts b/server/src/modules/core/usecases/activateUser/activateUser.schema.ts new file mode 100644 index 000000000..69a218835 --- /dev/null +++ b/server/src/modules/core/usecases/activateUser/activateUser.schema.ts @@ -0,0 +1,11 @@ +import { z } from "zod"; + +import { passwordRegex } from "../../../../../../shared/utils/passwordRegex"; +export const activateUserSchema = { + body: z.object({ + password: z.string().regex(new RegExp(passwordRegex)), + repeatPassword: z.string(), + activationToken: z.string(), + }), + response: { 200: z.void() }, +}; diff --git a/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts new file mode 100644 index 000000000..80a2fc869 --- /dev/null +++ b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts @@ -0,0 +1,24 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { checkActivationTokenSchema } from "./checkActivationToken.schema"; +import { checkActivationToken } from "./checkActivationToken.usecase"; + +export const checkActivationTokenRoute = (server: Server) => { + return createRoute("/auth/activate", { + method: "GET", + schema: checkActivationTokenSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { activationToken } = request.query; + + const valid = await checkActivationToken({ + activationToken, + }); + response.status(200).send({ valid }); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.schema.ts b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.schema.ts new file mode 100644 index 000000000..da92ad4eb --- /dev/null +++ b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.schema.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; + +export const checkActivationTokenSchema = { + querystring: z.object({ + activationToken: z.string(), + }), + response: { + 200: z.object({ + valid: z.literal(true), + }), + }, +}; diff --git a/server/src/modules/core/usecases/login/login.route.ts b/server/src/modules/core/usecases/login/login.route.ts new file mode 100644 index 000000000..2d9701109 --- /dev/null +++ b/server/src/modules/core/usecases/login/login.route.ts @@ -0,0 +1,30 @@ +import { createRoute } from "@http-wizard/core"; +import cookie from "cookie"; + +import { Server } from "../../../../server"; +import { loginSchema } from "./login.schema"; +import { login } from "./login.usecase"; + +export const loginRoute = (server: Server) => { + return createRoute("/auth/login", { + method: "POST", + schema: loginSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { email, password } = request.body; + + const token = await login({ email, password }); + const cookies = cookie.serialize("Authorization", token, { + maxAge: 30 * 24 * 3600000, + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + }); + response.status(200).header("set-cookie", cookies).send({ token }); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/login/login.schema.ts b/server/src/modules/core/usecases/login/login.schema.ts new file mode 100644 index 000000000..78db07c2e --- /dev/null +++ b/server/src/modules/core/usecases/login/login.schema.ts @@ -0,0 +1,12 @@ +import { z } from "zod"; +export const loginSchema = { + body: z.object({ + email: z.string().email(), + password: z.string(), + }), + response: { + 200: z.object({ + token: z.string(), + }), + }, +}; diff --git a/server/src/modules/core/usecases/logout/logout.route.ts b/server/src/modules/core/usecases/logout/logout.route.ts new file mode 100644 index 000000000..50fd7628e --- /dev/null +++ b/server/src/modules/core/usecases/logout/logout.route.ts @@ -0,0 +1,26 @@ +import { createRoute } from "@http-wizard/core"; +import cookie from "cookie"; + +import { Server } from "../../../../server"; +import { logoutSchema } from "./logout.schema"; + +export const logoutRoute = (server: Server) => { + return createRoute("/auth/logout", { + method: "POST", + schema: logoutSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (_, response) => { + const cookies = cookie.serialize("Authorization", "", { + maxAge: 30 * 24 * 3600000, + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + }); + response.status(200).header("set-cookie", cookies).send(); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/logout/logout.schema.ts b/server/src/modules/core/usecases/logout/logout.schema.ts new file mode 100644 index 000000000..ae5590cd1 --- /dev/null +++ b/server/src/modules/core/usecases/logout/logout.schema.ts @@ -0,0 +1,6 @@ +import { z } from "zod"; +export const logoutSchema = { + response: { + 200: z.void, + }, +}; diff --git a/server/src/modules/core/usecases/resetPassword/resetPassword.route.ts b/server/src/modules/core/usecases/resetPassword/resetPassword.route.ts new file mode 100644 index 000000000..5a4b05b0e --- /dev/null +++ b/server/src/modules/core/usecases/resetPassword/resetPassword.route.ts @@ -0,0 +1,21 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { resetPasswordSchema } from "./resetPassword.schema"; +import { resetPassword } from "./resetPassword.usecase"; + +export const resetPasswordRoute = (server: Server) => { + return createRoute("/auth/reset-password", { + method: "POST", + schema: resetPasswordSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { password, repeatPassword, resetPasswordToken } = request.body; + await resetPassword({ password, repeatPassword, resetPasswordToken }); + response.status(200).send(); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/resetPassword/resetPassword.schema.ts b/server/src/modules/core/usecases/resetPassword/resetPassword.schema.ts new file mode 100644 index 000000000..226bf4888 --- /dev/null +++ b/server/src/modules/core/usecases/resetPassword/resetPassword.schema.ts @@ -0,0 +1,11 @@ +import { passwordRegex } from "shared"; +import { z } from "zod"; + +export const resetPasswordSchema = { + body: z.object({ + password: z.string().regex(new RegExp(passwordRegex)), + repeatPassword: z.string(), + resetPasswordToken: z.string(), + }), + response: { 200: z.void() }, +}; diff --git a/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.route.ts b/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.route.ts new file mode 100644 index 000000000..c1edd5f3d --- /dev/null +++ b/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.route.ts @@ -0,0 +1,21 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { sendResetPasswordSchema } from "./sendResetPassword.schema"; +import { sendResetPassword } from "./sendResetPassword.usecase"; + +export const sendResetPasswordRoute = (server: Server) => { + return createRoute("/auth/send-reset-password", { + method: "POST", + schema: sendResetPasswordSchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const { email } = request.body; + await sendResetPassword({ email }); + response.status(200).send(); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.schema.ts b/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.schema.ts new file mode 100644 index 000000000..f628960ed --- /dev/null +++ b/server/src/modules/core/usecases/sendResetPassword/sendResetPassword.schema.ts @@ -0,0 +1,8 @@ +import { z } from "zod"; + +export const sendResetPasswordSchema = { + body: z.object({ + email: z.string().email(), + }), + response: { 200: z.void() }, +}; diff --git a/server/src/modules/core/usecases/whoAmI/whoAmI.route.ts b/server/src/modules/core/usecases/whoAmI/whoAmI.route.ts new file mode 100644 index 000000000..a9e07f541 --- /dev/null +++ b/server/src/modules/core/usecases/whoAmI/whoAmI.route.ts @@ -0,0 +1,19 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { whoAmISchema } from "./whoAmI.schema"; + +export const whoAmIRoute = (server: Server) => { + return createRoute("/auth/whoAmI", { + method: "GET", + schema: whoAmISchema, + }).handle((props) => { + server.route({ + ...props, + handler: async (request, response) => { + const user = request.user; + response.status(200).send(user && { user }); + }, + }); + }); +}; diff --git a/server/src/modules/core/usecases/whoAmI/whoAmI.schema.ts b/server/src/modules/core/usecases/whoAmI/whoAmI.schema.ts new file mode 100644 index 000000000..2e9fd03ef --- /dev/null +++ b/server/src/modules/core/usecases/whoAmI/whoAmI.schema.ts @@ -0,0 +1,17 @@ +import { PERMISSIONS, Role } from "shared"; +import { z } from "zod"; + +export const whoAmISchema = { + response: { + 200: z + .object({ + user: z.object({ + id: z.string(), + email: z.string(), + role: z.enum(Object.keys(PERMISSIONS) as [Role]).optional(), + codeRegion: z.string().optional(), + }), + }) + .optional(), + }, +}; diff --git a/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts b/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts index ac4de1915..50a589c88 100644 --- a/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts +++ b/server/src/modules/intentions/usecases/countDemandes/countDemandes.route.ts @@ -7,7 +7,7 @@ import { countDemandes } from "./countDemandes.query"; import { countDemandesSchema } from "./countDemandes.schema"; export const countDemandesRoute = (server: Server) => { - return createRoute("/", { + return createRoute("/demandes/count", { method: "GET", schema: countDemandesSchema, }).handle((props) => { From 388ddfe3230cf77c484c6c3bfdb5941e74d4c86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Mon, 20 Nov 2023 13:14:48 +0100 Subject: [PATCH 06/20] refactor: data routes --- server/src/modules/data/index.ts | 8 + .../routes/pilotageTransformation.routes.ts | 17 - .../routes/restitutionIntentions.routes.ts | 2 +- .../dependencies.ts | 260 ------------- .../getCountRestitutionIntentionsStats.ts | 36 -- .../getFormationsStatsQuery.dep.ts | 265 ------------- ...etFormationsTransformationStats.usecase.ts | 60 +-- .../getRegionStats.dep.ts | 62 --- .../getTransformationStats.usecase.ts | 85 ++--- .../getTransformationStatsQuery.dep.ts | 354 ------------------ 10 files changed, 84 insertions(+), 1065 deletions(-) delete mode 100644 server/src/modules/data/usecases/getCountRestitutionIntentionsStats/dependencies.ts delete mode 100644 server/src/modules/data/usecases/getCountRestitutionIntentionsStats/getCountRestitutionIntentionsStats.ts delete mode 100644 server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts delete mode 100644 server/src/modules/data/usecases/getFormationsTransformationStats/getRegionStats.dep.ts delete mode 100644 server/src/modules/data/usecases/getTransformationStats/getTransformationStatsQuery.dep.ts diff --git a/server/src/modules/data/index.ts b/server/src/modules/data/index.ts index 79f897f49..a608f3c42 100644 --- a/server/src/modules/data/index.ts +++ b/server/src/modules/data/index.ts @@ -2,6 +2,7 @@ import { Server } from "../../server"; import { pilotageReformeRoutes } from "./routes/pilotageReforme.routes"; import { pilotageTransformationRoutes } from "./routes/pilotageTransformation.routes"; import { restitutionIntentionsRoutes } from "./routes/restitutionIntentions.routes"; +import { countRestitutionIntentionsStatsRoute } from "./usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route"; import { getDataForPanoramaDepartementRoute } from "./usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route"; import { getDataForPanoramaRegionRoute } from "./usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route"; import { getDepartementRoute } from "./usecases/getDepartement/getDepartement.route"; @@ -9,8 +10,11 @@ import { getDepartementsRoute } from "./usecases/getDepartements/getDepartements import { getEtablissementRoute } from "./usecases/getEtablissement/etablissements.routes"; import { getEtablissementsRoutes } from "./usecases/getEtablissements/getEtablissements.routes"; import { getFormationsRoute } from "./usecases/getFormations/formations.routes"; +import { getFormationsTransformationsRoute } from "./usecases/getFormationsTransformationStats/getFormationsTransformations.route"; import { getRegionRoute } from "./usecases/getRegion/getRegion.route"; import { getRegionsRoute } from "./usecases/getRegions/getRegions.route"; +import { getRestitutionIntentionsStatsRoute } from "./usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route"; +import { getTransformationsStatsRoutes } from "./usecases/getTransformationStats/getTransformationsStats.route"; export const registerFormationModule = ({ server }: { server: Server }) => { pilotageReformeRoutes({ server }); @@ -27,5 +31,9 @@ export const registerFormationModule = ({ server }: { server: Server }) => { ...getDataForPanoramaRegionRoute({ server }), ...getRegionRoute({ server }), ...getRegionsRoute({ server }), + ...getFormationsTransformationsRoute({ server }), + ...getTransformationsStatsRoutes({ server }), + ...getRestitutionIntentionsStatsRoute({ server }), + ...countRestitutionIntentionsStatsRoute({ server }), }; }; diff --git a/server/src/modules/data/routes/pilotageTransformation.routes.ts b/server/src/modules/data/routes/pilotageTransformation.routes.ts index fe480fa1d..1fc32fb02 100644 --- a/server/src/modules/data/routes/pilotageTransformation.routes.ts +++ b/server/src/modules/data/routes/pilotageTransformation.routes.ts @@ -2,7 +2,6 @@ import { ROUTES_CONFIG } from "shared"; import { Server } from "../../../server"; import { hasPermissionHandler } from "../../core"; -import { getFormationsTransformationStats } from "../usecases/getFormationsTransformationStats/getFormationsTransformationStats.usecase"; import { getTransformationStats } from "../usecases/getTransformationStats/getTransformationStats.usecase"; export const pilotageTransformationRoutes = ({ @@ -26,20 +25,4 @@ export const pilotageTransformationRoutes = ({ response.status(200).send(stats); } ); - - server.get( - "/pilotage-transformation/formations", - { - schema: ROUTES_CONFIG.getFormationsTransformationStats, - preHandler: hasPermissionHandler("pilotage-intentions/lecture"), - }, - async (request, response) => { - const { order, orderBy, ...filters } = request.query; - const stats = await getFormationsTransformationStats({ - ...filters, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(stats); - } - ); }; diff --git a/server/src/modules/data/routes/restitutionIntentions.routes.ts b/server/src/modules/data/routes/restitutionIntentions.routes.ts index 5034431df..3a1424c7d 100644 --- a/server/src/modules/data/routes/restitutionIntentions.routes.ts +++ b/server/src/modules/data/routes/restitutionIntentions.routes.ts @@ -3,7 +3,7 @@ import { ROUTES_CONFIG } from "shared"; import { Server } from "../../../server"; import { hasPermissionHandler } from "../../core"; -import { getCountRestitutionIntentionsStats } from "../usecases/getCountRestitutionIntentionsStats/getCountRestitutionIntentionsStats"; +import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats"; import { getRestitutionIntentionsStats } from "../usecases/getRestitutionIntentionsStats/getRestitutionIntentionsStats.usecase"; export const restitutionIntentionsRoutes = ({ server }: { server: Server }) => { diff --git a/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/dependencies.ts b/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/dependencies.ts deleted file mode 100644 index 4300e8cd0..000000000 --- a/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/dependencies.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { sql } from "kysely"; -import { jsonBuildObject } from "kysely/helpers/postgres"; - -import { kdb } from "../../../../db/db"; -import { RequestUser } from "../../../core/model/User"; -import { - countFermetures, - countFermeturesApprentissage, - countFermeturesSco, - countOuvertures, - countOuverturesApprentissage, - countOuverturesSco, -} from "../../../utils/countCapacite"; -import { isIntentionVisible } from "../../../utils/isIntentionVisible"; - -const countRestitutionIntentionsStatsInDB = async ({ - status, - codeRegion, - rentreeScolaire, - typeDemande, - motif, - cfd, - codeNiveauDiplome, - dispositif, - filiere, - coloration, - amiCMA, - secteur, - cfdFamille, - codeDepartement, - codeAcademie, - commune, - uai, - compensation, - user, -}: { - status?: "draft" | "submitted"; - codeRegion?: string[]; - rentreeScolaire?: string; - typeDemande?: string[]; - motif?: string[]; - cfd?: string[]; - codeNiveauDiplome?: string[]; - dispositif?: string[]; - filiere?: string[]; - coloration?: string; - amiCMA?: string; - secteur?: string; - cfdFamille?: string[]; - codeDepartement?: string[]; - codeAcademie?: string[]; - commune?: string[]; - uai?: string[]; - compensation?: string; - user: Pick; -}) => { - const countDemandes = await kdb - .selectFrom("demande") - .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") - .leftJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") - .leftJoin("familleMetier", "familleMetier.cfdSpecialite", "demande.cfd") - .select((eb) => - jsonBuildObject({ - total: sql`SUM(${countOuvertures(eb)} + ${countFermetures( - eb - )})`, - scolaire: sql`SUM(${countOuverturesSco( - eb - )} + ${countFermeturesSco(eb)})`, - apprentissage: sql`SUM(${countOuverturesApprentissage( - eb - )} + ${countFermeturesApprentissage(eb)})`, - }).as("total") - ) - .select((eb) => - jsonBuildObject({ - total: sql`SUM(${countOuvertures(eb)})`, - scolaire: sql`SUM(${countOuverturesSco(eb)})`, - apprentissage: sql`SUM(${countOuverturesApprentissage(eb)})`, - }).as("ouvertures") - ) - .select((eb) => - jsonBuildObject({ - total: sql`SUM(${countFermetures(eb)})`, - scolaire: sql`SUM(${countFermeturesSco(eb)})`, - apprentissage: sql`SUM(${countFermeturesApprentissage(eb)})`, - }).as("fermetures") - ) - .select((eb) => - jsonBuildObject({ - total: sql`SUM( - CASE WHEN - ${eb.ref("demande.amiCma")} = true - THEN ${countOuvertures(eb)} - ELSE 0 - END - )`, - scolaire: sql`SUM( - CASE WHEN - ${eb.ref("demande.amiCma")} = true - THEN ${countOuverturesSco(eb)} - ELSE 0 - END - )`, - apprentissage: sql`SUM( - CASE WHEN - ${eb.ref("demande.amiCma")} = true - THEN ${countOuverturesApprentissage(eb)} - ELSE 0 - END - )`, - }).as("amiCMAs") - ) - .select((eb) => - jsonBuildObject({ - total: sql`SUM( - CASE WHEN - ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') - THEN ${countOuvertures(eb)} - ELSE 0 - END - )`, - scolaire: sql`SUM( - CASE WHEN - ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') - THEN ${countOuverturesSco(eb)} - ELSE 0 - END - )`, - apprentissage: sql`SUM( - CASE WHEN - ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') - THEN ${countOuverturesApprentissage(eb)} - ELSE 0 - END - )`, - }).as("FCILs") - ) - .$call((eb) => { - if (status && status != undefined) - return eb.where("demande.status", "=", status); - return eb; - }) - .$call((eb) => { - if (codeRegion) return eb.where("demande.codeRegion", "in", codeRegion); - return eb; - }) - .$call((eb) => { - if (codeDepartement) - return eb.where( - "dataEtablissement.codeDepartement", - "in", - codeDepartement - ); - return eb; - }) - .$call((eb) => { - if (codeAcademie) - return eb.where("dataEtablissement.codeAcademie", "in", codeAcademie); - return eb; - }) - .$call((eb) => { - if (commune) return eb.where("dataEtablissement.commune", "in", commune); - return eb; - }) - .$call((eb) => { - if (uai) return eb.where("dataEtablissement.uai", "in", uai); - return eb; - }) - .$call((eb) => { - if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) - return eb.where( - "demande.rentreeScolaire", - "=", - parseInt(rentreeScolaire) - ); - return eb; - }) - .$call((eb) => { - if (motif) - return eb.where((eb) => - eb.or( - motif.map( - (m) => sql`${m} = any(${eb.ref("demande.motif")})` - ) - ) - ); - return eb; - }) - .$call((eb) => { - if (typeDemande) - return eb.where("demande.typeDemande", "in", typeDemande); - return eb; - }) - .$call((eb) => { - if (cfd) return eb.where("demande.cfd", "in", cfd); - return eb; - }) - .$call((eb) => { - if (codeNiveauDiplome) - return eb.where( - "dataFormation.codeNiveauDiplome", - "in", - codeNiveauDiplome - ); - return eb; - }) - .$call((eb) => { - if (dispositif) return eb.where("demande.dispositifId", "in", dispositif); - return eb; - }) - .$call((eb) => { - if (filiere) - return eb.where("dataFormation.libelleFiliere", "in", filiere); - return eb; - }) - .$call((eb) => { - if (cfdFamille) - return eb.where("familleMetier.cfdFamille", "in", cfdFamille); - return eb; - }) - .$call((eb) => { - if (coloration) - return eb.where( - "demande.coloration", - "=", - coloration === "true" ? sql`true` : sql`false` - ); - return eb; - }) - .$call((eb) => { - if (amiCMA) - return eb.where( - "demande.amiCma", - "=", - amiCMA === "true" ? sql`true` : sql`false` - ); - return eb; - }) - .$call((eb) => { - if (compensation) - return eb.where("demande.typeDemande", "in", [ - "ouverture_compensation", - "augmentation_compensation", - ]); - return eb; - }) - .$call((eb) => { - if (secteur) return eb.where("dataEtablissement.secteur", "=", secteur); - return eb; - }) - .where(isIntentionVisible({ user })) - .executeTakeFirstOrThrow(); - - return countDemandes; -}; - -export const dependencies = { - countRestitutionIntentionsStatsInDB, -}; diff --git a/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/getCountRestitutionIntentionsStats.ts b/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/getCountRestitutionIntentionsStats.ts deleted file mode 100644 index 626a1203c..000000000 --- a/server/src/modules/data/usecases/getCountRestitutionIntentionsStats/getCountRestitutionIntentionsStats.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { RequestUser } from "../../../core/model/User"; -import { dependencies } from "./dependencies"; - -const getCountRestitutionIntentionsStatsFactory = - ({ - countRestitutionIntentionsStatsInDB = dependencies.countRestitutionIntentionsStatsInDB, - }) => - async (activeFilters: { - status?: "draft" | "submitted"; - codeRegion?: string[]; - rentreeScolaire?: string; - typeDemande?: string[]; - motif?: string[]; - cfd?: string[]; - codeNiveauDiplome?: string[]; - dispositif?: string[]; - filiere?: string[]; - coloration?: string; - amiCMA?: string; - secteur?: string; - cfdFamille?: string[]; - codeDepartement?: string[]; - codeAcademie?: string[]; - commune?: string[]; - uai?: string[]; - compensation?: string; - user: Pick; - }) => { - const countStatsDemandesPromise = - countRestitutionIntentionsStatsInDB(activeFilters); - - return await countStatsDemandesPromise; - }; - -export const getCountRestitutionIntentionsStats = - getCountRestitutionIntentionsStatsFactory({}); diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts deleted file mode 100644 index cdb1cce22..000000000 --- a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsStatsQuery.dep.ts +++ /dev/null @@ -1,265 +0,0 @@ -import { ExpressionBuilder, sql } from "kysely"; - -import { kdb } from "../../../../db/db"; -import { DB } from "../../../../db/schema"; -import { cleanNull } from "../../../../utils/noNull"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { withTauxPressionReg } from "../../queries/utils/tauxPression"; - -const selectDifferencePlaces = ( - eb: ExpressionBuilder, - type?: "fermeture" | "ouverture" -) => { - if (type === "ouverture") - return sql`GREATEST(${eb.ref("capaciteScolaire")} - - ${eb.ref("capaciteScolaireActuelle")}, 0) -+ GREATEST(${eb.ref("capaciteApprentissage")} - - ${eb.ref("capaciteApprentissageActuelle")}, 0)`; - - if (type === "fermeture") - return sql`GREATEST(${eb.ref("capaciteScolaireActuelle")} - - ${eb.ref("capaciteScolaire")}, 0) -+ GREATEST(${eb.ref("capaciteApprentissageActuelle")} - - ${eb.ref("capaciteApprentissage")}, 0)`; - - return sql`ABS(${eb.ref("capaciteScolaire")} - - ${eb.ref("capaciteScolaireActuelle")}) -+ ABS(${eb.ref("capaciteApprentissage")} - - ${eb.ref("capaciteApprentissageActuelle")})`; -}; - -const selectPlacesTransformees = (eb: ExpressionBuilder) => - sql`GREATEST(${eb.ref("capaciteScolaire")} - - ${eb.ref("capaciteScolaireActuelle")}, 0) - + GREATEST(${eb.ref("capaciteApprentissage")} - - ${eb.ref("capaciteApprentissageActuelle")}, 0) - + GREATEST(${eb.ref("capaciteScolaireActuelle")} - - ${eb.ref("capaciteScolaire")}, 0)`; - -const selectPlacesOuvertes = (eb: ExpressionBuilder) => - sql`GREATEST(${eb.ref("capaciteScolaire")} - - ${eb.ref("capaciteScolaireActuelle")}, 0) - + GREATEST(${eb.ref("capaciteApprentissage")} - - ${eb.ref("capaciteApprentissageActuelle")}, 0)`; - -const selectPlacesFermees = (eb: ExpressionBuilder) => - sql`GREATEST(${eb.ref("capaciteScolaireActuelle")} - - ${eb.ref("capaciteScolaire")}, 0) - + GREATEST(${eb.ref("capaciteApprentissageActuelle")} - - ${eb.ref("capaciteApprentissage")}, 0)`; - -const selectNbDemandes = (eb: ExpressionBuilder) => - eb.fn.count("demande.id").distinct(); - -const selectNbEtablissements = ( - eb: ExpressionBuilder -) => eb.fn.count("dataEtablissement.uai").distinct(); - -export const getFormationsTransformationStatsQuery = ({ - status, - type, - rentreeScolaire = "2024", - codeRegion, - codeAcademie, - codeDepartement, - tauxPression, - codeNiveauDiplome, - filiere, - orderBy, -}: { - status?: "draft" | "submitted"; - type?: "fermeture" | "ouverture"; - rentreeScolaire?: string; - codeRegion?: string; - codeAcademie?: string; - codeDepartement?: string; - tauxPression?: "eleve" | "faible"; - codeNiveauDiplome?: string[]; - filiere?: string[]; - orderBy?: { column: string; order: "asc" | "desc" }; -}) => { - const partition = (() => { - if (codeDepartement) return ["dataEtablissement.codeDepartement"] as const; - if (codeAcademie) return ["dataEtablissement.codeAcademie"] as const; - if (codeRegion) return ["dataEtablissement.codeRegion"] as const; - return []; - })(); - - return kdb - .selectFrom("demande") - .innerJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") - .innerJoin("dataFormation", "dataFormation.cfd", "demande.cfd") - .leftJoin("dispositif", "dispositif.codeDispositif", "demande.dispositifId") - .leftJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") - .leftJoin( - "academie", - "academie.codeAcademie", - "dataEtablissement.codeAcademie" - ) - .leftJoin( - "departement", - "departement.codeDepartement", - "dataEtablissement.codeDepartement" - ) - .select((eb) => [ - "dataFormation.libelle as libelleDiplome", - "dispositif.libelleDispositif", - "dataFormation.cfd", - "demande.dispositifId", - (eb) => - withInsertionReg({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }).as("tauxInsertion"), - withPoursuiteReg({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }).as("tauxPoursuite"), - withTauxPressionReg({ - eb, - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }).as("tauxPression"), - withTauxDevenirFavorableReg({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }).as("tauxDevenirFavorable"), - withPositionCadran({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - codeNiveauDiplomeRef: codeNiveauDiplome - ? "dataFormation.codeNiveauDiplome" - : undefined, - }).as("positionCadran"), - selectNbDemandes(eb).as("nbDemandes"), - selectNbEtablissements(eb).as("nbEtablissements"), - sql`ABS(${eb.fn.sum(selectDifferencePlaces(eb, type))})`.as( - "differencePlaces" - ), - eb.fn.sum(selectPlacesOuvertes(eb)).as("placesOuvertes"), - eb.fn.sum(selectPlacesFermees(eb)).as("placesFermees"), - eb.fn.sum(selectPlacesTransformees(eb)).as("placesTransformees"), - hasContinuum({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }).as("continuum"), - ]) - .$call((eb) => { - if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) - return eb.where( - "demande.rentreeScolaire", - "=", - parseInt(rentreeScolaire) - ); - return eb; - }) - .where((wb) => { - if (!type) return wb.val(true); - return wb((eb) => selectDifferencePlaces(eb, type), ">", 0); - }) - .having( - (eb) => - withInsertionReg({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }), - "is not", - null - ) - .having( - (eb) => - withPoursuiteReg({ - eb, - millesimeSortie: "2020_2021", - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }), - "is not", - null - ) - .having((h) => { - if (!tauxPression) return h.val(true); - return h( - (eb) => - withTauxPressionReg({ - eb, - cfdRef: "demande.cfd", - dispositifIdRef: "demande.dispositifId", - codeRegionRef: "dataEtablissement.codeRegion", - }), - tauxPression === "eleve" ? ">" : "<", - tauxPression === "eleve" ? 130 : 70 - ); - }) - .$narrowType<{ tauxPoursuite: number; tauxInsertion: number }>() - .where((w) => { - if (!codeRegion) return w.val(true); - return w("dataEtablissement.codeRegion", "=", codeRegion); - }) - .where((w) => { - if (!codeAcademie) return w.val(true); - return w("dataEtablissement.codeAcademie", "=", codeAcademie); - }) - .where((w) => { - if (!codeDepartement) return w.val(true); - return w("dataEtablissement.codeDepartement", "=", codeDepartement); - }) - .groupBy([ - "demande.cfd", - "dataFormation.cfd", - "demande.dispositifId", - "dispositif.libelleDispositif", - "dataFormation.libelle", - ...partition, - ]) - .$call((q) => { - if (!status) return q; - return q.where("demande.status", "=", status); - }) - .$call((q) => { - if (!codeNiveauDiplome?.length) return q; - return q.where( - "dataFormation.codeNiveauDiplome", - "in", - codeNiveauDiplome - ); - }) - .$call((q) => { - if (!filiere?.length) return q; - return q.where("dataFormation.libelleFiliere", "in", filiere); - }) - .$call((q) => { - if (!orderBy) return q; - return q.orderBy( - sql.ref(orderBy.column), - sql`${sql.raw(orderBy.order)} NULLS LAST` - ); - }) - .orderBy("tauxDevenirFavorable", "desc") - .execute() - .then(cleanNull); -}; diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformationStats.usecase.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformationStats.usecase.ts index a5bc38d4c..569cd79ae 100644 --- a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformationStats.usecase.ts +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformationStats.usecase.ts @@ -1,30 +1,34 @@ -import { inject } from "injecti"; +import { dependencies } from "./dependencies"; -import { getFormationsTransformationStatsQuery } from "./getFormationsStatsQuery.dep"; -import { getRegionStats } from "./getRegionStats.dep"; - -export const [getFormationsTransformationStats] = inject( - { getFormationsTransformationStatsQuery, getRegionStats }, - (deps) => - async (filters: { - status?: "draft" | "submitted"; - type?: "fermeture" | "ouverture"; - rentreeScolaire?: string; - codeRegion?: string; - codeAcademie?: string; - codeDepartement?: string; - tauxPression?: "eleve" | "faible"; - codeNiveauDiplome?: string[]; - filiere?: string[]; - orderBy?: { column: string; order: "asc" | "desc" }; - }) => { - const [stats, formations] = await Promise.all([ - deps.getRegionStats({ - ...filters, - millesimeSortie: "2020_2021", - }), - deps.getFormationsTransformationStatsQuery(filters), - ]); - return { stats, formations }; +const getFormationsTransformationStatsFactory = + ( + deps = { + getFormationsTransformationStatsQuery: + dependencies.getFormationsTransformationStatsQuery, + getRegionStats: dependencies.getRegionStats, } -); + ) => + async (filters: { + status?: "draft" | "submitted"; + type?: "fermeture" | "ouverture"; + rentreeScolaire?: string; + codeRegion?: string; + codeAcademie?: string; + codeDepartement?: string; + tauxPression?: "eleve" | "faible"; + codeNiveauDiplome?: string[]; + filiere?: string[]; + orderBy?: { column: string; order: "asc" | "desc" }; + }) => { + const [stats, formations] = await Promise.all([ + deps.getRegionStats({ + ...filters, + millesimeSortie: "2020_2021", + }), + deps.getFormationsTransformationStatsQuery(filters), + ]); + return { stats, formations }; + }; + +export const getFormationsTransformationStats = + getFormationsTransformationStatsFactory(); diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getRegionStats.dep.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getRegionStats.dep.ts deleted file mode 100644 index 7b70a4e21..000000000 --- a/server/src/modules/data/usecases/getFormationsTransformationStats/getRegionStats.dep.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { kdb } from "../../../../db/db"; -import { notHistoriqueIndicateurRegionSortie } from "../../queries/utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; - -export const getRegionStats = async ({ - codeRegion, - codeAcademie, - codeDepartement, - codeNiveauDiplome, - millesimeSortie = "2020_2021", -}: { - codeRegion?: string; - codeAcademie?: string; - codeDepartement?: string; - millesimeSortie?: string; - codeNiveauDiplome?: string[]; -}) => { - const statsSortie = await kdb - .selectFrom("indicateurRegionSortie") - .innerJoin( - "formation", - "formation.codeFormationDiplome", - "indicateurRegionSortie.cfd" - ) - .where((w) => { - if (!codeRegion) return w.val(true); - return w("indicateurRegionSortie.codeRegion", "=", codeRegion); - }) - .$call((q) => { - if (!codeDepartement && !codeAcademie) { - return q; - } - return q - .innerJoin( - "departement", - "departement.codeRegion", - "indicateurRegionSortie.codeRegion" - ) - .where((w) => { - if (!codeAcademie) return w.val(true); - return w("departement.codeAcademie", "=", codeAcademie); - }) - .where((w) => { - if (!codeDepartement) return w.val(true); - return w("departement.codeDepartement", "=", codeDepartement); - }); - }) - .$call((q) => { - if (!codeNiveauDiplome?.length) return q; - return q.where("formation.codeNiveauDiplome", "in", codeNiveauDiplome); - }) - .where("indicateurRegionSortie.millesimeSortie", "=", millesimeSortie) - .where(notHistoriqueIndicateurRegionSortie) - .select([ - selectTauxInsertion6moisAgg("indicateurRegionSortie").as("tauxInsertion"), - selectTauxPoursuiteAgg("indicateurRegionSortie").as("tauxPoursuite"), - ]) - .executeTakeFirstOrThrow(); - - return statsSortie; -}; diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts index 405a15507..57f344424 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts @@ -1,13 +1,9 @@ -import { inject } from "injecti"; import _ from "lodash"; -import { - getFiltersQuery, - getTransformationStatsQuery, -} from "./getTransformationStatsQuery.dep"; +import { dependencies } from "./dependencies"; type DataTerritoire = Awaited< - ReturnType + ReturnType >[0]["region" | "academie" | "departement"]; const formatTerritoire = (item: DataTerritoire) => ({ @@ -46,7 +42,7 @@ const formatTerritoire = (item: DataTerritoire) => ({ }); const formatResult = ( - result: Awaited>, + result: Awaited>, orderBy?: { column: string; order: "asc" | "desc" } ) => { return { @@ -164,43 +160,48 @@ const formatResult = ( }; }; -export const [getTransformationStats] = inject( - { getTransformationStatsQuery, getFiltersQuery }, - (deps) => - async (activeFilters: { - rentreeScolaire?: string; - codeNiveauDiplome?: string[]; - filiere?: string[]; - orderBy?: { column: string; order: "asc" | "desc" }; - }) => { - const resultDraft = await deps - .getTransformationStatsQuery({ - ...activeFilters, - status: "draft", - }) - .then((result) => formatResult(result, activeFilters.orderBy)); - const resultSubmitted = await deps - .getTransformationStatsQuery({ - ...activeFilters, - status: "submitted", - }) - .then((result) => formatResult(result, activeFilters.orderBy)); - const resultAll = await deps - .getTransformationStatsQuery({ - ...activeFilters, - }) - .then((result) => formatResult(result, activeFilters.orderBy)); +const getTransformationStatsFactory = + ( + deps = { + getTransformationStatsQuery: dependencies.getTransformationStatsQuery, + getFiltersQuery: dependencies.getFiltersQuery, + } + ) => + async (activeFilters: { + rentreeScolaire?: string; + codeNiveauDiplome?: string[]; + filiere?: string[]; + orderBy?: { column: string; order: "asc" | "desc" }; + }) => { + const resultDraft = await deps + .getTransformationStatsQuery({ + ...activeFilters, + status: "draft", + }) + .then((result) => formatResult(result, activeFilters.orderBy)); + const resultSubmitted = await deps + .getTransformationStatsQuery({ + ...activeFilters, + status: "submitted", + }) + .then((result) => formatResult(result, activeFilters.orderBy)); + const resultAll = await deps + .getTransformationStatsQuery({ + ...activeFilters, + }) + .then((result) => formatResult(result, activeFilters.orderBy)); - const filters = await deps.getFiltersQuery(activeFilters); + const filters = await deps.getFiltersQuery(activeFilters); - return { - submitted: resultSubmitted, - draft: resultDraft, - all: resultAll, - filters: filters, - }; - } -); + return { + submitted: resultSubmitted, + draft: resultDraft, + all: resultAll, + filters: filters, + }; + }; + +export const getTransformationStats = getTransformationStatsFactory(); const effectifsRegions: Record = { "11": 64809, diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationStatsQuery.dep.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationStatsQuery.dep.ts deleted file mode 100644 index eaa6f0593..000000000 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationStatsQuery.dep.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { AnyColumnWithTable, ExpressionBuilder, sql } from "kysely"; -import { jsonBuildObject } from "kysely/helpers/postgres"; - -import { kdb } from "../../../../db/db"; -import { DB } from "../../../../db/schema"; -import { cleanNull } from "../../../../utils/noNull"; - -const selectPlacesTransformees = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .sum( - sql`ABS( - ${eb.ref("demande.capaciteScolaire")} - -${eb.ref("demande.capaciteScolaireActuelle")}) - +GREATEST(${eb.ref("demande.capaciteApprentissage")} - -${eb.ref("demande.capaciteApprentissageActuelle")}, 0)` - ) - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -const selectDifferenceScolaire = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - sql` -${eb.fn - .sum("demande.capaciteScolaire") - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))} -- -${eb.fn - .sum("demande.capaciteScolaireActuelle") - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))}`; - -const selectDifferenceApprentissage = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - sql` -${eb.fn - .sum("demande.capaciteApprentissage") - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))} -- -${eb.fn - .sum("demande.capaciteApprentissageActuelle") - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))}`; - -const selectPlacesOuvertesScolaire = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .sum( - sql`GREATEST(${eb.ref("demande.capaciteScolaire")} - - ${eb.ref("demande.capaciteScolaireActuelle")}, 0)` - ) - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -const selectPlacesFermeesScolaire = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .sum( - sql`GREATEST(${eb.ref("demande.capaciteScolaireActuelle")} - - ${eb.ref("demande.capaciteScolaire")}, 0)` - ) - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -const selectPlacesOuvertesApprentissage = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .sum( - sql`GREATEST(${eb.ref("demande.capaciteApprentissage")} - - ${eb.ref("demande.capaciteApprentissageActuelle")}, 0)` - ) - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -const selectPlacesFermeesApprentissage = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .sum( - sql`GREATEST(${eb.ref("demande.capaciteApprentissageActuelle")} - - ${eb.ref("demande.capaciteApprentissage")}, 0)` - ) - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -const selectNbDemandes = - (eb: ExpressionBuilder) => - (partitionBy?: AnyColumnWithTable) => - eb.fn - .count("demande.id") - .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); - -export const getTransformationStatsQuery = ({ - status, - rentreeScolaire = "2024", - codeNiveauDiplome, - filiere, -}: { - status?: "draft" | "submitted"; - rentreeScolaire?: string; - codeNiveauDiplome?: string[]; - filiere?: string[]; -}) => - kdb - .selectFrom("demande") - .leftJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") - .leftJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") - .leftJoin( - "academie", - "academie.codeAcademie", - "dataEtablissement.codeAcademie" - ) - .leftJoin( - "departement", - "departement.codeDepartement", - "dataEtablissement.codeDepartement" - ) - .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") - .distinctOn(["dataEtablissement.codeDepartement"]) - .select((eb) => [ - selectNbDemandes(eb)().as("nbDemandesNational"), - selectPlacesTransformees(eb)().as("transformeNational"), - selectDifferenceScolaire(eb)().as("differenceScolaireNational"), - jsonBuildObject({ - countDemande: selectNbDemandes(eb)(), - transformes: selectPlacesTransformees(eb)(), - differenceCapaciteScolaire: selectDifferenceScolaire(eb)(), - differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)(), - placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)(), - placesFermeesScolaire: selectPlacesFermeesScolaire(eb)(), - placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)(), - placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)(), - }).as("national"), - jsonBuildObject({ - countDemande: selectNbDemandes(eb)("dataEtablissement.codeRegion"), - transforme: selectPlacesTransformees(eb)( - "dataEtablissement.codeRegion" - ), - differenceCapaciteScolaire: selectDifferenceScolaire(eb)( - "dataEtablissement.codeRegion" - ), - differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( - "dataEtablissement.codeRegion" - ), - placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( - "dataEtablissement.codeRegion" - ), - placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( - "dataEtablissement.codeRegion" - ), - placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( - "dataEtablissement.codeRegion" - ), - placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( - "dataEtablissement.codeRegion" - ), - codeRegion: eb.ref("dataEtablissement.codeRegion"), - libelle: eb.ref("region.libelleRegion"), - }).as("region"), - jsonBuildObject({ - countDemande: selectNbDemandes(eb)("dataEtablissement.codeAcademie"), - transforme: selectPlacesTransformees(eb)( - "dataEtablissement.codeAcademie" - ), - differenceCapaciteScolaire: selectDifferenceScolaire(eb)( - "dataEtablissement.codeAcademie" - ), - differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( - "dataEtablissement.codeAcademie" - ), - placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( - "dataEtablissement.codeAcademie" - ), - placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( - "dataEtablissement.codeAcademie" - ), - placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( - "dataEtablissement.codeAcademie" - ), - placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( - "dataEtablissement.codeAcademie" - ), - codeAcademie: eb.ref("academie.codeAcademie"), - libelle: eb.ref("academie.libelle"), - }).as("academie"), - jsonBuildObject({ - countDemande: selectNbDemandes(eb)("dataEtablissement.codeDepartement"), - transforme: selectPlacesTransformees(eb)( - "dataEtablissement.codeDepartement" - ), - differenceCapaciteScolaire: selectDifferenceScolaire(eb)( - "dataEtablissement.codeDepartement" - ), - differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( - "dataEtablissement.codeDepartement" - ), - placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( - "dataEtablissement.codeDepartement" - ), - placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( - "dataEtablissement.codeDepartement" - ), - placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( - "dataEtablissement.codeDepartement" - ), - placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( - "dataEtablissement.codeDepartement" - ), - codeDepartement: eb.ref("dataEtablissement.codeDepartement"), - libelle: eb.ref("departement.libelle"), - libelleAcademie: eb.ref("academie.libelle"), - libelleRegion: eb.ref("region.libelleRegion"), - }).as("departement"), - ]) - .$call((eb) => { - if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) - return eb.where( - "demande.rentreeScolaire", - "=", - parseInt(rentreeScolaire) - ); - return eb; - }) - .$call((eb) => { - if (filiere) - return eb.where("dataFormation.libelleFiliere", "in", filiere); - return eb; - }) - .$call((eb) => { - if (codeNiveauDiplome) - return eb.where( - "dataFormation.codeNiveauDiplome", - "in", - codeNiveauDiplome - ); - return eb; - }) - .$call((q) => { - if (!status) return q; - return q.where("demande.status", "=", status); - }) - .execute() - .then(cleanNull); - -export const getFiltersQuery = async ({ - status, - rentreeScolaire = "2024", - codeNiveauDiplome, - filiere, -}: { - status?: "draft" | "submitted"; - rentreeScolaire?: string; - codeNiveauDiplome?: string[]; - filiere?: string[]; -}) => { - const inStatus = (eb: ExpressionBuilder) => { - if (!status || status == undefined) return sql`true`; - return eb("demande.status", "=", status); - }; - - const inRentreeScolaire = (eb: ExpressionBuilder) => { - if (!rentreeScolaire || Number.isNaN(rentreeScolaire)) - return sql`true`; - return eb("demande.rentreeScolaire", "=", parseInt(rentreeScolaire)); - }; - - const inCodeNiveauDiplome = (eb: ExpressionBuilder) => { - if (!codeNiveauDiplome) return sql`true`; - return eb("dataFormation.codeNiveauDiplome", "in", codeNiveauDiplome); - }; - - const inFiliere = (eb: ExpressionBuilder) => { - if (!filiere) return sql`true`; - return eb("dataFormation.libelleFiliere", "in", filiere); - }; - - const base = kdb - .selectFrom("demande") - .innerJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") - .innerJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") - .innerJoin( - "academie", - "academie.codeAcademie", - "dataEtablissement.codeAcademie" - ) - .innerJoin( - "departement", - "departement.codeDepartement", - "dataEtablissement.codeDepartement" - ) - .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") - .leftJoin( - "niveauDiplome", - "niveauDiplome.codeNiveauDiplome", - "dataFormation.codeNiveauDiplome" - ) - .distinct() - .$castTo<{ label: string; value: string }>() - .orderBy("label", "asc"); - - const rentreesScolaires = await base - .select([ - "demande.rentreeScolaire as value", - "demande.rentreeScolaire as label", - ]) - .where("demande.rentreeScolaire", "is not", null) - .execute(); - - const regions = await base - .select(["region.codeRegion as value", "region.libelleRegion as label"]) - .where("region.codeRegion", "is not", null) - .execute(); - - const academies = await base - .select(["academie.codeAcademie as value", "academie.libelle as label"]) - .where("academie.codeAcademie", "is not", null) - .execute(); - - const departements = await base - .select([ - "departement.codeDepartement as value", - "departement.libelle as label", - ]) - .where("departement.codeDepartement", "is not", null) - .execute(); - - const filieres = await base - .select([ - "dataFormation.libelleFiliere as label", - "dataFormation.libelleFiliere as value", - ]) - .where("dataFormation.libelleFiliere", "is not", null) - .where((eb) => - eb.and([inStatus(eb), inRentreeScolaire(eb), inCodeNiveauDiplome(eb)]) - ) - .execute(); - - const diplomes = await base - .select([ - "niveauDiplome.libelleNiveauDiplome as label", - "niveauDiplome.codeNiveauDiplome as value", - ]) - .where("niveauDiplome.codeNiveauDiplome", "is not", null) - .where((eb) => eb.and([inStatus(eb), inRentreeScolaire(eb), inFiliere(eb)])) - .execute(); - - return { - rentreesScolaires: rentreesScolaires.map(cleanNull), - regions: regions.map(cleanNull), - academies: academies.map(cleanNull), - departements: departements.map(cleanNull), - filieres: filieres.map(cleanNull), - diplomes: diplomes.map(cleanNull), - }; -}; From 751c76fdb97f6d4052a10be5f5d4af19a71266e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Mon, 20 Nov 2023 14:27:43 +0100 Subject: [PATCH 07/20] refactor: data routes fix --- .../src/modules/data/routes/regions.routes.ts | 29 ++ .../countRestitutionIntentionsStats.route.ts | 33 ++ .../countRestitutionIntentionsStats.schema.ts | 92 +++++ .../countRestitutionIntentionsStats.ts | 36 ++ .../dependencies.ts | 260 +++++++++++++ .../dependencies.ts | 335 ++++++++++++++++ .../getFormationsTransformations.route.ts | 30 ++ .../getFormationsTransformations.schema.ts | 48 +++ .../getRestitutionIntentionStats.route.ts | 34 ++ .../getRestitutionIntentionStats.schema.ts | 110 ++++++ .../getTransformationStats/dependencies.ts | 359 ++++++++++++++++++ .../getTransformationsStats.route.ts | 31 ++ .../getTransformationsStats.schema.ts | 78 ++++ 13 files changed, 1475 insertions(+) create mode 100644 server/src/modules/data/routes/regions.routes.ts create mode 100644 server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts create mode 100644 server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.schema.ts create mode 100644 server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts create mode 100644 server/src/modules/data/usecases/countRestitutionIntentionsStats/dependencies.ts create mode 100644 server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts create mode 100644 server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.route.ts create mode 100644 server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.schema.ts create mode 100644 server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts create mode 100644 server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts create mode 100644 server/src/modules/data/usecases/getTransformationStats/dependencies.ts create mode 100644 server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts create mode 100644 server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts diff --git a/server/src/modules/data/routes/regions.routes.ts b/server/src/modules/data/routes/regions.routes.ts new file mode 100644 index 000000000..47d75db6b --- /dev/null +++ b/server/src/modules/data/routes/regions.routes.ts @@ -0,0 +1,29 @@ +import { ROUTES_CONFIG } from "shared"; + +import { Server } from "../../../server"; +import { getRegions } from "../queries/getRegions/getRegions.query"; +import { getRegionStats } from "../queries/getRegionStats/getRegionStats.query"; + +export const regionsRoutes = ({ server }: { server: Server }) => { + server.get( + "/regions", + { schema: ROUTES_CONFIG.getRegions }, + async (_, response) => { + const regions = await getRegions(); + response.status(200).send(regions); + } + ); + + server.get( + "/region/:codeRegion", + { schema: ROUTES_CONFIG.getRegionStats }, + async (request, response) => { + const regionStats = await getRegionStats({ + ...request.params, + ...request.query, + }); + if (!regionStats) return response.status(404).send(); + response.status(200).send(regionStats); + } + ); +}; diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts new file mode 100644 index 000000000..3fd22d657 --- /dev/null +++ b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts @@ -0,0 +1,33 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { countRestitutionIntentionsStats } from "./countRestitutionIntentionsStats"; +import { countRestitutionIntentionsStatsSchema } from "./countRestitutionIntentionsStats.schema"; + +export const countRestitutionIntentionsStatsRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/intentions/stats/count", { + method: "GET", + schema: countRestitutionIntentionsStatsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage-intentions/lecture"), + handler: async (request, response) => { + const { ...filters } = request.query; + if (!request.user) throw Boom.forbidden(); + + const result = await countRestitutionIntentionsStats({ + ...filters, + user: request.user, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.schema.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.schema.ts new file mode 100644 index 000000000..08a500b51 --- /dev/null +++ b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.schema.ts @@ -0,0 +1,92 @@ +import { z } from "zod"; + +const StatsDemandesItem = z.object({ + id: z.string(), + cfd: z.string().optional(), + libelleDiplome: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + niveauDiplome: z.string().optional(), + uai: z.string().optional(), + libelleEtablissement: z.string().optional(), + commune: z.string().optional(), + rentreeScolaire: z.coerce.number().optional(), + typeDemande: z.string().optional(), + motif: z.array(z.string()).optional(), + autreMotif: z.string().optional(), + coloration: z.boolean().optional(), + libelleColoration: z.string().optional(), + libelleFCIL: z.string().optional(), + amiCma: z.boolean().optional(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + libelleFiliere: z.string().optional(), + status: z.string(), + codeRegion: z.string().optional(), + libelleRegion: z.string().optional(), + codeAcademie: z.string().optional(), + codeDepartement: z.string().optional(), + libelleDepartement: z.string().optional(), + createdAt: z.string(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationUai: z.string().optional(), + differenceCapaciteScolaire: z.coerce.number().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + differenceCapaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), + insertion: z.coerce.number().optional(), + poursuite: z.coerce.number().optional(), + devenirFavorable: z.coerce.number().optional(), + pression: z.coerce.number().optional(), + nbEtablissement: z.coerce.number().optional(), + positionCadran: z.string().optional(), + tauxInsertionMoyen: z.coerce.number().optional(), + tauxPoursuiteMoyen: z.coerce.number().optional(), +}); + +const CountCapaciteStatsDemandesSchema = z.object({ + total: z.number(), + scolaire: z.number(), + apprentissage: z.number(), + coloration: z.number().optional(), +}); + +export const countRestitutionIntentionsStatsSchema = { + querystring: z.object({ + codeRegion: z.array(z.string()).optional(), + codeAcademie: z.array(z.string()).optional(), + codeDepartement: z.array(z.string()).optional(), + commune: z.array(z.string()).optional(), + uai: z.array(z.string()).optional(), + rentreeScolaire: z.string().optional(), + typeDemande: z.array(z.string()).optional(), + motif: z.array(z.string()).optional(), + status: z.enum(["draft", "submitted"]).optional(), + codeNiveauDiplome: z.array(z.string()).optional(), + cfd: z.array(z.string()).optional(), + dispositif: z.array(z.string()).optional(), + filiere: z.array(z.string()).optional(), + cfdFamille: z.array(z.string()).optional(), + coloration: z.string().optional(), + amiCMA: z.string().optional(), + secteur: z.string().optional(), + compensation: z.string().optional(), + positionCadran: z.string().optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: StatsDemandesItem.keyof().optional(), + }), + response: { + 200: z.object({ + total: CountCapaciteStatsDemandesSchema, + ouvertures: CountCapaciteStatsDemandesSchema, + fermetures: CountCapaciteStatsDemandesSchema, + amiCMAs: CountCapaciteStatsDemandesSchema, + FCILs: CountCapaciteStatsDemandesSchema, + }), + }, +}; diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts new file mode 100644 index 000000000..e76eb14ad --- /dev/null +++ b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts @@ -0,0 +1,36 @@ +import { RequestUser } from "../../../core/model/User"; +import { dependencies } from "./dependencies"; + +const countRestitutionIntentionsStatsFactory = + ({ + countRestitutionIntentionsStatsInDB = dependencies.countRestitutionIntentionsStatsInDB, + }) => + async (activeFilters: { + status?: "draft" | "submitted"; + codeRegion?: string[]; + rentreeScolaire?: string; + typeDemande?: string[]; + motif?: string[]; + cfd?: string[]; + codeNiveauDiplome?: string[]; + dispositif?: string[]; + filiere?: string[]; + coloration?: string; + amiCMA?: string; + secteur?: string; + cfdFamille?: string[]; + codeDepartement?: string[]; + codeAcademie?: string[]; + commune?: string[]; + uai?: string[]; + compensation?: string; + user: Pick; + }) => { + const countStatsDemandesPromise = + countRestitutionIntentionsStatsInDB(activeFilters); + + return await countStatsDemandesPromise; + }; + +export const countRestitutionIntentionsStats = + countRestitutionIntentionsStatsFactory({}); diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/dependencies.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/dependencies.ts new file mode 100644 index 000000000..4300e8cd0 --- /dev/null +++ b/server/src/modules/data/usecases/countRestitutionIntentionsStats/dependencies.ts @@ -0,0 +1,260 @@ +import { sql } from "kysely"; +import { jsonBuildObject } from "kysely/helpers/postgres"; + +import { kdb } from "../../../../db/db"; +import { RequestUser } from "../../../core/model/User"; +import { + countFermetures, + countFermeturesApprentissage, + countFermeturesSco, + countOuvertures, + countOuverturesApprentissage, + countOuverturesSco, +} from "../../../utils/countCapacite"; +import { isIntentionVisible } from "../../../utils/isIntentionVisible"; + +const countRestitutionIntentionsStatsInDB = async ({ + status, + codeRegion, + rentreeScolaire, + typeDemande, + motif, + cfd, + codeNiveauDiplome, + dispositif, + filiere, + coloration, + amiCMA, + secteur, + cfdFamille, + codeDepartement, + codeAcademie, + commune, + uai, + compensation, + user, +}: { + status?: "draft" | "submitted"; + codeRegion?: string[]; + rentreeScolaire?: string; + typeDemande?: string[]; + motif?: string[]; + cfd?: string[]; + codeNiveauDiplome?: string[]; + dispositif?: string[]; + filiere?: string[]; + coloration?: string; + amiCMA?: string; + secteur?: string; + cfdFamille?: string[]; + codeDepartement?: string[]; + codeAcademie?: string[]; + commune?: string[]; + uai?: string[]; + compensation?: string; + user: Pick; +}) => { + const countDemandes = await kdb + .selectFrom("demande") + .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") + .leftJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") + .leftJoin("familleMetier", "familleMetier.cfdSpecialite", "demande.cfd") + .select((eb) => + jsonBuildObject({ + total: sql`SUM(${countOuvertures(eb)} + ${countFermetures( + eb + )})`, + scolaire: sql`SUM(${countOuverturesSco( + eb + )} + ${countFermeturesSco(eb)})`, + apprentissage: sql`SUM(${countOuverturesApprentissage( + eb + )} + ${countFermeturesApprentissage(eb)})`, + }).as("total") + ) + .select((eb) => + jsonBuildObject({ + total: sql`SUM(${countOuvertures(eb)})`, + scolaire: sql`SUM(${countOuverturesSco(eb)})`, + apprentissage: sql`SUM(${countOuverturesApprentissage(eb)})`, + }).as("ouvertures") + ) + .select((eb) => + jsonBuildObject({ + total: sql`SUM(${countFermetures(eb)})`, + scolaire: sql`SUM(${countFermeturesSco(eb)})`, + apprentissage: sql`SUM(${countFermeturesApprentissage(eb)})`, + }).as("fermetures") + ) + .select((eb) => + jsonBuildObject({ + total: sql`SUM( + CASE WHEN + ${eb.ref("demande.amiCma")} = true + THEN ${countOuvertures(eb)} + ELSE 0 + END + )`, + scolaire: sql`SUM( + CASE WHEN + ${eb.ref("demande.amiCma")} = true + THEN ${countOuverturesSco(eb)} + ELSE 0 + END + )`, + apprentissage: sql`SUM( + CASE WHEN + ${eb.ref("demande.amiCma")} = true + THEN ${countOuverturesApprentissage(eb)} + ELSE 0 + END + )`, + }).as("amiCMAs") + ) + .select((eb) => + jsonBuildObject({ + total: sql`SUM( + CASE WHEN + ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') + THEN ${countOuvertures(eb)} + ELSE 0 + END + )`, + scolaire: sql`SUM( + CASE WHEN + ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') + THEN ${countOuverturesSco(eb)} + ELSE 0 + END + )`, + apprentissage: sql`SUM( + CASE WHEN + ${eb.ref("dataFormation.codeNiveauDiplome")} IN ('381', '481', '581') + THEN ${countOuverturesApprentissage(eb)} + ELSE 0 + END + )`, + }).as("FCILs") + ) + .$call((eb) => { + if (status && status != undefined) + return eb.where("demande.status", "=", status); + return eb; + }) + .$call((eb) => { + if (codeRegion) return eb.where("demande.codeRegion", "in", codeRegion); + return eb; + }) + .$call((eb) => { + if (codeDepartement) + return eb.where( + "dataEtablissement.codeDepartement", + "in", + codeDepartement + ); + return eb; + }) + .$call((eb) => { + if (codeAcademie) + return eb.where("dataEtablissement.codeAcademie", "in", codeAcademie); + return eb; + }) + .$call((eb) => { + if (commune) return eb.where("dataEtablissement.commune", "in", commune); + return eb; + }) + .$call((eb) => { + if (uai) return eb.where("dataEtablissement.uai", "in", uai); + return eb; + }) + .$call((eb) => { + if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) + return eb.where( + "demande.rentreeScolaire", + "=", + parseInt(rentreeScolaire) + ); + return eb; + }) + .$call((eb) => { + if (motif) + return eb.where((eb) => + eb.or( + motif.map( + (m) => sql`${m} = any(${eb.ref("demande.motif")})` + ) + ) + ); + return eb; + }) + .$call((eb) => { + if (typeDemande) + return eb.where("demande.typeDemande", "in", typeDemande); + return eb; + }) + .$call((eb) => { + if (cfd) return eb.where("demande.cfd", "in", cfd); + return eb; + }) + .$call((eb) => { + if (codeNiveauDiplome) + return eb.where( + "dataFormation.codeNiveauDiplome", + "in", + codeNiveauDiplome + ); + return eb; + }) + .$call((eb) => { + if (dispositif) return eb.where("demande.dispositifId", "in", dispositif); + return eb; + }) + .$call((eb) => { + if (filiere) + return eb.where("dataFormation.libelleFiliere", "in", filiere); + return eb; + }) + .$call((eb) => { + if (cfdFamille) + return eb.where("familleMetier.cfdFamille", "in", cfdFamille); + return eb; + }) + .$call((eb) => { + if (coloration) + return eb.where( + "demande.coloration", + "=", + coloration === "true" ? sql`true` : sql`false` + ); + return eb; + }) + .$call((eb) => { + if (amiCMA) + return eb.where( + "demande.amiCma", + "=", + amiCMA === "true" ? sql`true` : sql`false` + ); + return eb; + }) + .$call((eb) => { + if (compensation) + return eb.where("demande.typeDemande", "in", [ + "ouverture_compensation", + "augmentation_compensation", + ]); + return eb; + }) + .$call((eb) => { + if (secteur) return eb.where("dataEtablissement.secteur", "=", secteur); + return eb; + }) + .where(isIntentionVisible({ user })) + .executeTakeFirstOrThrow(); + + return countDemandes; +}; + +export const dependencies = { + countRestitutionIntentionsStatsInDB, +}; diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts new file mode 100644 index 000000000..575f53928 --- /dev/null +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts @@ -0,0 +1,335 @@ +import { ExpressionBuilder, sql } from "kysely"; + +import { kdb } from "../../../../db/db"; +import { DB } from "../../../../db/schema"; +import { cleanNull } from "../../../../utils/noNull"; +import { hasContinuum } from "../../queries/utils/hasContinuum"; +import { notHistoriqueIndicateurRegionSortie } from "../../queries/utils/notHistorique"; +import { withPositionCadran } from "../../queries/utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; +import { + selectTauxInsertion6moisAgg, + withInsertionReg, +} from "../../queries/utils/tauxInsertion6mois"; +import { + selectTauxPoursuiteAgg, + withPoursuiteReg, +} from "../../queries/utils/tauxPoursuite"; +import { withTauxPressionReg } from "../../queries/utils/tauxPression"; + +const selectDifferencePlaces = ( + eb: ExpressionBuilder, + type?: "fermeture" | "ouverture" +) => { + if (type === "ouverture") + return sql`GREATEST(${eb.ref("capaciteScolaire")} + - ${eb.ref("capaciteScolaireActuelle")}, 0) ++ GREATEST(${eb.ref("capaciteApprentissage")} + - ${eb.ref("capaciteApprentissageActuelle")}, 0)`; + + if (type === "fermeture") + return sql`GREATEST(${eb.ref("capaciteScolaireActuelle")} + - ${eb.ref("capaciteScolaire")}, 0) ++ GREATEST(${eb.ref("capaciteApprentissageActuelle")} + - ${eb.ref("capaciteApprentissage")}, 0)`; + + return sql`ABS(${eb.ref("capaciteScolaire")} + - ${eb.ref("capaciteScolaireActuelle")}) ++ ABS(${eb.ref("capaciteApprentissage")} + - ${eb.ref("capaciteApprentissageActuelle")})`; +}; + +const selectPlacesTransformees = (eb: ExpressionBuilder) => + sql`GREATEST(${eb.ref("capaciteScolaire")} + - ${eb.ref("capaciteScolaireActuelle")}, 0) + + GREATEST(${eb.ref("capaciteApprentissage")} + - ${eb.ref("capaciteApprentissageActuelle")}, 0) + + GREATEST(${eb.ref("capaciteScolaireActuelle")} + - ${eb.ref("capaciteScolaire")}, 0)`; + +const selectPlacesOuvertes = (eb: ExpressionBuilder) => + sql`GREATEST(${eb.ref("capaciteScolaire")} + - ${eb.ref("capaciteScolaireActuelle")}, 0) + + GREATEST(${eb.ref("capaciteApprentissage")} + - ${eb.ref("capaciteApprentissageActuelle")}, 0)`; + +const selectPlacesFermees = (eb: ExpressionBuilder) => + sql`GREATEST(${eb.ref("capaciteScolaireActuelle")} + - ${eb.ref("capaciteScolaire")}, 0) + + GREATEST(${eb.ref("capaciteApprentissageActuelle")} + - ${eb.ref("capaciteApprentissage")}, 0)`; + +const selectNbDemandes = (eb: ExpressionBuilder) => + eb.fn.count("demande.id").distinct(); + +const selectNbEtablissements = ( + eb: ExpressionBuilder +) => eb.fn.count("dataEtablissement.uai").distinct(); + +const getFormationsTransformationStatsQuery = ({ + status, + type, + rentreeScolaire = "2024", + codeRegion, + codeAcademie, + codeDepartement, + tauxPression, + codeNiveauDiplome, + filiere, + orderBy, +}: { + status?: "draft" | "submitted"; + type?: "fermeture" | "ouverture"; + rentreeScolaire?: string; + codeRegion?: string; + codeAcademie?: string; + codeDepartement?: string; + tauxPression?: "eleve" | "faible"; + codeNiveauDiplome?: string[]; + filiere?: string[]; + orderBy?: { column: string; order: "asc" | "desc" }; +}) => { + const partition = (() => { + if (codeDepartement) return ["dataEtablissement.codeDepartement"] as const; + if (codeAcademie) return ["dataEtablissement.codeAcademie"] as const; + if (codeRegion) return ["dataEtablissement.codeRegion"] as const; + return []; + })(); + + return kdb + .selectFrom("demande") + .innerJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") + .innerJoin("dataFormation", "dataFormation.cfd", "demande.cfd") + .leftJoin("dispositif", "dispositif.codeDispositif", "demande.dispositifId") + .leftJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") + .leftJoin( + "academie", + "academie.codeAcademie", + "dataEtablissement.codeAcademie" + ) + .leftJoin( + "departement", + "departement.codeDepartement", + "dataEtablissement.codeDepartement" + ) + .select((eb) => [ + "dataFormation.libelle as libelleDiplome", + "dispositif.libelleDispositif", + "dataFormation.cfd", + "demande.dispositifId", + (eb) => + withInsertionReg({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }).as("tauxInsertion"), + withPoursuiteReg({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }).as("tauxPoursuite"), + withTauxPressionReg({ + eb, + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }).as("tauxPression"), + withTauxDevenirFavorableReg({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }).as("tauxDevenirFavorable"), + withPositionCadran({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + codeNiveauDiplomeRef: codeNiveauDiplome + ? "dataFormation.codeNiveauDiplome" + : undefined, + }).as("positionCadran"), + selectNbDemandes(eb).as("nbDemandes"), + selectNbEtablissements(eb).as("nbEtablissements"), + sql`ABS(${eb.fn.sum(selectDifferencePlaces(eb, type))})`.as( + "differencePlaces" + ), + eb.fn.sum(selectPlacesOuvertes(eb)).as("placesOuvertes"), + eb.fn.sum(selectPlacesFermees(eb)).as("placesFermees"), + eb.fn.sum(selectPlacesTransformees(eb)).as("placesTransformees"), + hasContinuum({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }).as("continuum"), + ]) + .$call((eb) => { + if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) + return eb.where( + "demande.rentreeScolaire", + "=", + parseInt(rentreeScolaire) + ); + return eb; + }) + .where((wb) => { + if (!type) return wb.val(true); + return wb((eb) => selectDifferencePlaces(eb, type), ">", 0); + }) + .having( + (eb) => + withInsertionReg({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }), + "is not", + null + ) + .having( + (eb) => + withPoursuiteReg({ + eb, + millesimeSortie: "2020_2021", + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }), + "is not", + null + ) + .having((h) => { + if (!tauxPression) return h.val(true); + return h( + (eb) => + withTauxPressionReg({ + eb, + cfdRef: "demande.cfd", + dispositifIdRef: "demande.dispositifId", + codeRegionRef: "dataEtablissement.codeRegion", + }), + tauxPression === "eleve" ? ">" : "<", + tauxPression === "eleve" ? 130 : 70 + ); + }) + .$narrowType<{ tauxPoursuite: number; tauxInsertion: number }>() + .where((w) => { + if (!codeRegion) return w.val(true); + return w("dataEtablissement.codeRegion", "=", codeRegion); + }) + .where((w) => { + if (!codeAcademie) return w.val(true); + return w("dataEtablissement.codeAcademie", "=", codeAcademie); + }) + .where((w) => { + if (!codeDepartement) return w.val(true); + return w("dataEtablissement.codeDepartement", "=", codeDepartement); + }) + .groupBy([ + "demande.cfd", + "dataFormation.cfd", + "demande.dispositifId", + "dispositif.libelleDispositif", + "dataFormation.libelle", + ...partition, + ]) + .$call((q) => { + if (!status) return q; + return q.where("demande.status", "=", status); + }) + .$call((q) => { + if (!codeNiveauDiplome?.length) return q; + return q.where( + "dataFormation.codeNiveauDiplome", + "in", + codeNiveauDiplome + ); + }) + .$call((q) => { + if (!filiere?.length) return q; + return q.where("dataFormation.libelleFiliere", "in", filiere); + }) + .$call((q) => { + if (!orderBy) return q; + return q.orderBy( + sql.ref(orderBy.column), + sql`${sql.raw(orderBy.order)} NULLS LAST` + ); + }) + .orderBy("tauxDevenirFavorable", "desc") + .execute() + .then(cleanNull); +}; + +const getRegionStats = async ({ + codeRegion, + codeAcademie, + codeDepartement, + codeNiveauDiplome, + millesimeSortie = "2020_2021", +}: { + codeRegion?: string; + codeAcademie?: string; + codeDepartement?: string; + millesimeSortie?: string; + codeNiveauDiplome?: string[]; +}) => { + const statsSortie = await kdb + .selectFrom("indicateurRegionSortie") + .innerJoin( + "formation", + "formation.codeFormationDiplome", + "indicateurRegionSortie.cfd" + ) + .where((w) => { + if (!codeRegion) return w.val(true); + return w("indicateurRegionSortie.codeRegion", "=", codeRegion); + }) + .$call((q) => { + if (!codeDepartement && !codeAcademie) { + return q; + } + return q + .innerJoin( + "departement", + "departement.codeRegion", + "indicateurRegionSortie.codeRegion" + ) + .where((w) => { + if (!codeAcademie) return w.val(true); + return w("departement.codeAcademie", "=", codeAcademie); + }) + .where((w) => { + if (!codeDepartement) return w.val(true); + return w("departement.codeDepartement", "=", codeDepartement); + }); + }) + .$call((q) => { + if (!codeNiveauDiplome?.length) return q; + return q.where("formation.codeNiveauDiplome", "in", codeNiveauDiplome); + }) + .where("indicateurRegionSortie.millesimeSortie", "=", millesimeSortie) + .where(notHistoriqueIndicateurRegionSortie) + .select([ + selectTauxInsertion6moisAgg("indicateurRegionSortie").as("tauxInsertion"), + selectTauxPoursuiteAgg("indicateurRegionSortie").as("tauxPoursuite"), + ]) + .executeTakeFirstOrThrow(); + + return statsSortie; +}; + +export const dependencies = { + getFormationsTransformationStatsQuery, + getRegionStats, +}; diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.route.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.route.ts new file mode 100644 index 000000000..3e325fa74 --- /dev/null +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.route.ts @@ -0,0 +1,30 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getFormationsTransformationsSchema } from "./getFormationsTransformations.schema"; +import { getFormationsTransformationStats } from "./getFormationsTransformationStats.usecase"; + +export const getFormationsTransformationsRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/pilotage-transformation/formations", { + method: "GET", + schema: getFormationsTransformationsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage-intentions/lecture"), + handler: async (request, response) => { + const { order, orderBy, ...filters } = request.query; + const stats = await getFormationsTransformationStats({ + ...filters, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.schema.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.schema.ts new file mode 100644 index 000000000..fde4c0226 --- /dev/null +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/getFormationsTransformations.schema.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; + +const FormationTransformationStatsSchema = z.object({ + libelleDiplome: z.string().optional(), + libelleDispositif: z.string().optional(), + tauxInsertion: z.coerce.number(), + tauxPoursuite: z.coerce.number(), + tauxPression: z.coerce.number().optional(), + dispositifId: z.string().optional(), + cfd: z.string(), + nbDemandes: z.coerce.number(), + nbEtablissements: z.coerce.number(), + differencePlaces: z.coerce.number(), + placesOuvertes: z.coerce.number(), + placesFermees: z.coerce.number(), + placesTransformees: z.coerce.number(), + positionCadran: z.string().optional(), + continuum: z + .object({ + cfd: z.string(), + libelle: z.string().optional(), + }) + .optional(), +}); + +export const getFormationsTransformationsSchema = { + querystring: z.object({ + rentreeScolaire: z.string().optional(), + codeNiveauDiplome: z.array(z.string()).optional(), + filiere: z.array(z.string()).optional(), + codeRegion: z.string().optional(), + codeAcademie: z.string().optional(), + codeDepartement: z.string().optional(), + type: z.enum(["ouverture", "fermeture"]).optional(), + tauxPression: z.enum(["faible", "eleve"]).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: FormationTransformationStatsSchema.keyof().optional(), + }), + response: { + 200: z.object({ + stats: z.object({ + tauxInsertion: z.coerce.number(), + tauxPoursuite: z.coerce.number(), + }), + formations: z.array(FormationTransformationStatsSchema), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts new file mode 100644 index 000000000..d2cacb021 --- /dev/null +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts @@ -0,0 +1,34 @@ +import Boom from "@hapi/boom"; +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getRestitutionIntentionsStats } from "./getRestitutionIntentionsStats.usecase"; +import { getRestitutionIntentionsStatsSchema } from "./getRestitutionIntentionStats.schema"; + +export const getRestitutionIntentionsStatsRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/pilotage-transformation/formations", { + method: "GET", + schema: getRestitutionIntentionsStatsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage-intentions/lecture"), + handler: async (request, response) => { + const { order, orderBy, ...rest } = request.query; + if (!request.user) throw Boom.forbidden(); + + const result = await getRestitutionIntentionsStats({ + ...rest, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + user: request.user, + }); + response.status(200).send(result); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts new file mode 100644 index 000000000..28778d62f --- /dev/null +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts @@ -0,0 +1,110 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); + +const StatsDemandesItem = z.object({ + id: z.string(), + cfd: z.string().optional(), + libelleDiplome: z.string().optional(), + dispositifId: z.string().optional(), + libelleDispositif: z.string().optional(), + niveauDiplome: z.string().optional(), + uai: z.string().optional(), + libelleEtablissement: z.string().optional(), + commune: z.string().optional(), + rentreeScolaire: z.coerce.number().optional(), + typeDemande: z.string().optional(), + motif: z.array(z.string()).optional(), + autreMotif: z.string().optional(), + coloration: z.boolean().optional(), + libelleColoration: z.string().optional(), + libelleFCIL: z.string().optional(), + amiCma: z.boolean().optional(), + poursuitePedagogique: z.boolean().optional(), + commentaire: z.string().optional(), + libelleFiliere: z.string().optional(), + status: z.string(), + codeRegion: z.string().optional(), + libelleRegion: z.string().optional(), + codeAcademie: z.string().optional(), + codeDepartement: z.string().optional(), + libelleDepartement: z.string().optional(), + createdAt: z.string(), + compensationCfd: z.string().optional(), + compensationDispositifId: z.string().optional(), + compensationUai: z.string().optional(), + differenceCapaciteScolaire: z.coerce.number().optional(), + capaciteScolaireActuelle: z.coerce.number().optional(), + capaciteScolaire: z.coerce.number().optional(), + capaciteScolaireColoree: z.coerce.number().optional(), + differenceCapaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageActuelle: z.coerce.number().optional(), + capaciteApprentissage: z.coerce.number().optional(), + capaciteApprentissageColoree: z.coerce.number().optional(), + insertion: z.coerce.number().optional(), + poursuite: z.coerce.number().optional(), + devenirFavorable: z.coerce.number().optional(), + pression: z.coerce.number().optional(), + nbEtablissement: z.coerce.number().optional(), + positionCadran: z.string().optional(), + tauxInsertionMoyen: z.coerce.number().optional(), + tauxPoursuiteMoyen: z.coerce.number().optional(), +}); + +export const getRestitutionIntentionsStatsSchema = { + querystring: z.object({ + codeRegion: z.array(z.string()).optional(), + codeAcademie: z.array(z.string()).optional(), + codeDepartement: z.array(z.string()).optional(), + commune: z.array(z.string()).optional(), + uai: z.array(z.string()).optional(), + rentreeScolaire: z.string().optional(), + typeDemande: z.array(z.string()).optional(), + motif: z.array(z.string()).optional(), + status: z.enum(["draft", "submitted"]).optional(), + codeNiveauDiplome: z.array(z.string()).optional(), + cfd: z.array(z.string()).optional(), + dispositif: z.array(z.string()).optional(), + filiere: z.array(z.string()).optional(), + cfdFamille: z.array(z.string()).optional(), + coloration: z.string().optional(), + amiCMA: z.string().optional(), + secteur: z.string().optional(), + compensation: z.string().optional(), + positionCadran: z.string().optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: StatsDemandesItem.keyof().optional(), + offset: z.coerce.number().optional(), + limit: z.coerce.number().optional(), + }), + response: { + 200: z.object({ + filters: z.object({ + rentreesScolaires: z.array(OptionSchema), + statuts: z.array(OptionSchema), + regions: z.array(OptionSchema), + academies: z.array(OptionSchema), + departements: z.array(OptionSchema), + communes: z.array(OptionSchema), + etablissements: z.array(OptionSchema), + typesDemande: z.array(OptionSchema), + motifs: z.array(OptionSchema), + status: z.array(OptionSchema), + diplomes: z.array(OptionSchema), + formations: z.array(OptionSchema), + filieres: z.array(OptionSchema), + familles: z.array(OptionSchema), + dispositifs: z.array(OptionSchema), + secteurs: z.array(OptionSchema), + amiCMAs: z.array(OptionSchema), + colorations: z.array(OptionSchema), + compensations: z.array(OptionSchema), + }), + demandes: z.array(StatsDemandesItem), + count: z.coerce.number(), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getTransformationStats/dependencies.ts b/server/src/modules/data/usecases/getTransformationStats/dependencies.ts new file mode 100644 index 000000000..a51fa4f89 --- /dev/null +++ b/server/src/modules/data/usecases/getTransformationStats/dependencies.ts @@ -0,0 +1,359 @@ +import { AnyColumnWithTable, ExpressionBuilder, sql } from "kysely"; +import { jsonBuildObject } from "kysely/helpers/postgres"; + +import { kdb } from "../../../../db/db"; +import { DB } from "../../../../db/schema"; +import { cleanNull } from "../../../../utils/noNull"; + +const selectPlacesTransformees = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .sum( + sql`ABS( + ${eb.ref("demande.capaciteScolaire")} + -${eb.ref("demande.capaciteScolaireActuelle")}) + +GREATEST(${eb.ref("demande.capaciteApprentissage")} + -${eb.ref("demande.capaciteApprentissageActuelle")}, 0)` + ) + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const selectDifferenceScolaire = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + sql` +${eb.fn + .sum("demande.capaciteScolaire") + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))} +- +${eb.fn + .sum("demande.capaciteScolaireActuelle") + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))}`; + +const selectDifferenceApprentissage = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + sql` +${eb.fn + .sum("demande.capaciteApprentissage") + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))} +- +${eb.fn + .sum("demande.capaciteApprentissageActuelle") + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob))}`; + +const selectPlacesOuvertesScolaire = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .sum( + sql`GREATEST(${eb.ref("demande.capaciteScolaire")} + - ${eb.ref("demande.capaciteScolaireActuelle")}, 0)` + ) + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const selectPlacesFermeesScolaire = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .sum( + sql`GREATEST(${eb.ref("demande.capaciteScolaireActuelle")} + - ${eb.ref("demande.capaciteScolaire")}, 0)` + ) + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const selectPlacesOuvertesApprentissage = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .sum( + sql`GREATEST(${eb.ref("demande.capaciteApprentissage")} + - ${eb.ref("demande.capaciteApprentissageActuelle")}, 0)` + ) + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const selectPlacesFermeesApprentissage = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .sum( + sql`GREATEST(${eb.ref("demande.capaciteApprentissageActuelle")} + - ${eb.ref("demande.capaciteApprentissage")}, 0)` + ) + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const selectNbDemandes = + (eb: ExpressionBuilder) => + (partitionBy?: AnyColumnWithTable) => + eb.fn + .count("demande.id") + .over((ob) => (partitionBy ? ob.partitionBy(partitionBy) : ob)); + +const getTransformationStatsQuery = ({ + status, + rentreeScolaire = "2024", + codeNiveauDiplome, + filiere, +}: { + status?: "draft" | "submitted"; + rentreeScolaire?: string; + codeNiveauDiplome?: string[]; + filiere?: string[]; +}) => + kdb + .selectFrom("demande") + .leftJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") + .leftJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") + .leftJoin( + "academie", + "academie.codeAcademie", + "dataEtablissement.codeAcademie" + ) + .leftJoin( + "departement", + "departement.codeDepartement", + "dataEtablissement.codeDepartement" + ) + .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") + .distinctOn(["dataEtablissement.codeDepartement"]) + .select((eb) => [ + selectNbDemandes(eb)().as("nbDemandesNational"), + selectPlacesTransformees(eb)().as("transformeNational"), + selectDifferenceScolaire(eb)().as("differenceScolaireNational"), + jsonBuildObject({ + countDemande: selectNbDemandes(eb)(), + transformes: selectPlacesTransformees(eb)(), + differenceCapaciteScolaire: selectDifferenceScolaire(eb)(), + differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)(), + placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)(), + placesFermeesScolaire: selectPlacesFermeesScolaire(eb)(), + placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)(), + placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)(), + }).as("national"), + jsonBuildObject({ + countDemande: selectNbDemandes(eb)("dataEtablissement.codeRegion"), + transforme: selectPlacesTransformees(eb)( + "dataEtablissement.codeRegion" + ), + differenceCapaciteScolaire: selectDifferenceScolaire(eb)( + "dataEtablissement.codeRegion" + ), + differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( + "dataEtablissement.codeRegion" + ), + placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( + "dataEtablissement.codeRegion" + ), + placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( + "dataEtablissement.codeRegion" + ), + placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( + "dataEtablissement.codeRegion" + ), + placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( + "dataEtablissement.codeRegion" + ), + codeRegion: eb.ref("dataEtablissement.codeRegion"), + libelle: eb.ref("region.libelleRegion"), + }).as("region"), + jsonBuildObject({ + countDemande: selectNbDemandes(eb)("dataEtablissement.codeAcademie"), + transforme: selectPlacesTransformees(eb)( + "dataEtablissement.codeAcademie" + ), + differenceCapaciteScolaire: selectDifferenceScolaire(eb)( + "dataEtablissement.codeAcademie" + ), + differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( + "dataEtablissement.codeAcademie" + ), + placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( + "dataEtablissement.codeAcademie" + ), + placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( + "dataEtablissement.codeAcademie" + ), + placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( + "dataEtablissement.codeAcademie" + ), + placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( + "dataEtablissement.codeAcademie" + ), + codeAcademie: eb.ref("academie.codeAcademie"), + libelle: eb.ref("academie.libelle"), + }).as("academie"), + jsonBuildObject({ + countDemande: selectNbDemandes(eb)("dataEtablissement.codeDepartement"), + transforme: selectPlacesTransformees(eb)( + "dataEtablissement.codeDepartement" + ), + differenceCapaciteScolaire: selectDifferenceScolaire(eb)( + "dataEtablissement.codeDepartement" + ), + differenceCapaciteApprentissage: selectDifferenceApprentissage(eb)( + "dataEtablissement.codeDepartement" + ), + placesOuvertesScolaire: selectPlacesOuvertesScolaire(eb)( + "dataEtablissement.codeDepartement" + ), + placesFermeesScolaire: selectPlacesFermeesScolaire(eb)( + "dataEtablissement.codeDepartement" + ), + placesOuvertesApprentissage: selectPlacesOuvertesApprentissage(eb)( + "dataEtablissement.codeDepartement" + ), + placesFermeesApprentissage: selectPlacesFermeesApprentissage(eb)( + "dataEtablissement.codeDepartement" + ), + codeDepartement: eb.ref("dataEtablissement.codeDepartement"), + libelle: eb.ref("departement.libelle"), + libelleAcademie: eb.ref("academie.libelle"), + libelleRegion: eb.ref("region.libelleRegion"), + }).as("departement"), + ]) + .$call((eb) => { + if (rentreeScolaire && !Number.isNaN(rentreeScolaire)) + return eb.where( + "demande.rentreeScolaire", + "=", + parseInt(rentreeScolaire) + ); + return eb; + }) + .$call((eb) => { + if (filiere) + return eb.where("dataFormation.libelleFiliere", "in", filiere); + return eb; + }) + .$call((eb) => { + if (codeNiveauDiplome) + return eb.where( + "dataFormation.codeNiveauDiplome", + "in", + codeNiveauDiplome + ); + return eb; + }) + .$call((q) => { + if (!status) return q; + return q.where("demande.status", "=", status); + }) + .execute() + .then(cleanNull); + +const getFiltersQuery = async ({ + status, + rentreeScolaire = "2024", + codeNiveauDiplome, + filiere, +}: { + status?: "draft" | "submitted"; + rentreeScolaire?: string; + codeNiveauDiplome?: string[]; + filiere?: string[]; +}) => { + const inStatus = (eb: ExpressionBuilder) => { + if (!status || status == undefined) return sql`true`; + return eb("demande.status", "=", status); + }; + + const inRentreeScolaire = (eb: ExpressionBuilder) => { + if (!rentreeScolaire || Number.isNaN(rentreeScolaire)) + return sql`true`; + return eb("demande.rentreeScolaire", "=", parseInt(rentreeScolaire)); + }; + + const inCodeNiveauDiplome = (eb: ExpressionBuilder) => { + if (!codeNiveauDiplome) return sql`true`; + return eb("dataFormation.codeNiveauDiplome", "in", codeNiveauDiplome); + }; + + const inFiliere = (eb: ExpressionBuilder) => { + if (!filiere) return sql`true`; + return eb("dataFormation.libelleFiliere", "in", filiere); + }; + + const base = kdb + .selectFrom("demande") + .innerJoin("dataEtablissement", "dataEtablissement.uai", "demande.uai") + .innerJoin("region", "region.codeRegion", "dataEtablissement.codeRegion") + .innerJoin( + "academie", + "academie.codeAcademie", + "dataEtablissement.codeAcademie" + ) + .innerJoin( + "departement", + "departement.codeDepartement", + "dataEtablissement.codeDepartement" + ) + .leftJoin("dataFormation", "dataFormation.cfd", "demande.cfd") + .leftJoin( + "niveauDiplome", + "niveauDiplome.codeNiveauDiplome", + "dataFormation.codeNiveauDiplome" + ) + .distinct() + .$castTo<{ label: string; value: string }>() + .orderBy("label", "asc"); + + const rentreesScolaires = await base + .select([ + "demande.rentreeScolaire as value", + "demande.rentreeScolaire as label", + ]) + .where("demande.rentreeScolaire", "is not", null) + .execute(); + + const regions = await base + .select(["region.codeRegion as value", "region.libelleRegion as label"]) + .where("region.codeRegion", "is not", null) + .execute(); + + const academies = await base + .select(["academie.codeAcademie as value", "academie.libelle as label"]) + .where("academie.codeAcademie", "is not", null) + .execute(); + + const departements = await base + .select([ + "departement.codeDepartement as value", + "departement.libelle as label", + ]) + .where("departement.codeDepartement", "is not", null) + .execute(); + + const filieres = await base + .select([ + "dataFormation.libelleFiliere as label", + "dataFormation.libelleFiliere as value", + ]) + .where("dataFormation.libelleFiliere", "is not", null) + .where((eb) => + eb.and([inStatus(eb), inRentreeScolaire(eb), inCodeNiveauDiplome(eb)]) + ) + .execute(); + + const diplomes = await base + .select([ + "niveauDiplome.libelleNiveauDiplome as label", + "niveauDiplome.codeNiveauDiplome as value", + ]) + .where("niveauDiplome.codeNiveauDiplome", "is not", null) + .where((eb) => eb.and([inStatus(eb), inRentreeScolaire(eb), inFiliere(eb)])) + .execute(); + + return { + rentreesScolaires: rentreesScolaires.map(cleanNull), + regions: regions.map(cleanNull), + academies: academies.map(cleanNull), + departements: departements.map(cleanNull), + filieres: filieres.map(cleanNull), + diplomes: diplomes.map(cleanNull), + }; +}; + +export const dependencies = { + getTransformationStatsQuery, + getFiltersQuery, +}; diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts new file mode 100644 index 000000000..60e2d50fa --- /dev/null +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts @@ -0,0 +1,31 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getTransformationStatsSchema } from "./getTransformationsStats.schema"; +import { getTransformationStats } from "./getTransformationStats.usecase"; + +export const getTransformationsStatsRoutes = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/pilotage-transformation/formations", { + method: "GET", + schema: getTransformationStatsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage-intentions/lecture"), + handler: async (request, response) => { + const { order, orderBy, ...filters } = request.query; + + const stats = await getTransformationStats({ + ...filters, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts new file mode 100644 index 000000000..6fd674337 --- /dev/null +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts @@ -0,0 +1,78 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); + +const ScopedStatsTransfoSchema = z.object({ + libelle: z.string().optional(), + libelleAcademie: z.string().optional(), + libelleRegion: z.string().optional(), + countDemande: z.coerce.number(), + differenceCapaciteScolaire: z.coerce.number(), + differenceCapaciteApprentissage: z.coerce.number(), + placesTransformees: z.coerce.number(), + placesOuvertesScolaire: z.coerce.number(), + placesOuvertesApprentissage: z.coerce.number(), + placesOuvertes: z.coerce.number(), + placesFermeesScolaire: z.coerce.number(), + placesFermeesApprentissage: z.coerce.number(), + placesFermees: z.coerce.number(), + ratioOuverture: z.coerce.number(), + ratioFermeture: z.coerce.number(), + tauxTransformation: z.coerce.number(), +}); + +const StatsTransfoSchema = z.object({ + national: ScopedStatsTransfoSchema, + regions: z.record( + z.string(), + ScopedStatsTransfoSchema.partial().merge( + z.object({ + codeRegion: z.string().optional(), + }) + ) + ), + academies: z.record( + z.string(), + ScopedStatsTransfoSchema.partial().merge( + z.object({ + codeAcademie: z.string().optional(), + }) + ) + ), + departements: z.record( + z.string(), + ScopedStatsTransfoSchema.partial().merge( + z.object({ + codeDepartement: z.string().optional(), + }) + ) + ), +}); + +export const getTransformationStatsSchema = { + querystring: z.object({ + rentreeScolaire: z.string().optional(), + codeNiveauDiplome: z.array(z.string()).optional(), + filiere: z.array(z.string()).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: ScopedStatsTransfoSchema.keyof().optional(), + }), + response: { + 200: z.object({ + submitted: StatsTransfoSchema, + draft: StatsTransfoSchema, + all: StatsTransfoSchema, + filters: z.object({ + rentreesScolaires: z.array(OptionSchema), + regions: z.array(OptionSchema), + academies: z.array(OptionSchema), + departements: z.array(OptionSchema), + filieres: z.array(OptionSchema), + diplomes: z.array(OptionSchema), + }), + }), + }, +}; From 2a36c7ea2d3e3b0f147d6fd6c7f9a4d097e1cc7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Mon, 20 Nov 2023 16:12:32 +0100 Subject: [PATCH 08/20] refactor: queries -> usecase --- ...rmationsTransformationRegionStats.query.ts | 62 ------------- .../getRegionStats/getRegionStats.query.ts | 92 ------------------- .../queries/getRegions/getRegions.query.ts | 17 ---- .../routes/restitutionIntentions.routes.ts | 2 +- .../countRestitutionIntentionsStats.route.ts | 2 +- ...ountRestitutionIntentionsStats.usecase.ts} | 0 .../dependencies.ts | 18 ++-- .../getDataForPanoramaRegion/dependencies.ts | 18 ++-- .../getDepartementsStats.query.ts | 12 +-- .../getEtablissement.query.ts | 12 +-- .../getEtablissements/dependencies.ts | 20 ++-- .../usecases/getFormations/dependencies.ts | 18 ++-- .../dependencies.ts | 14 +-- .../getPilotageReformeStats/dependencies.ts} | 64 +++++++------ .../getPilotageReformeStats.route.ts | 27 ++++++ .../getPilotageReformeStats.schema.ts | 38 ++++++++ .../getPilotageReformeStats.usecase.ts | 25 +++++ .../dependencies.ts} | 22 +++-- .../getPilotageReformeStatsRegions.route.ts | 30 ++++++ .../getPilotageReformeStatsRegions.schema.ts | 30 ++++++ .../getPilotageReformeStatsRegions.usecase.ts | 26 ++++++ .../getRegion/getRegionsStats.query.ts | 12 +-- .../usecases/getRegions/getRegions.route.ts | 2 +- .../dependencies.ts | 26 +++--- .../data/{queries => }/utils/capaciteAnnee.ts | 0 .../data/{queries => }/utils/effectifAnnee.ts | 0 .../data/{queries => }/utils/hasContinuum.ts | 2 +- .../utils/nbEtablissementFormationRegion.ts | 2 +- .../data/{queries => }/utils/notHistorique.ts | 2 +- .../{queries => }/utils/positionCadran.ts | 2 +- .../{queries => }/utils/tauxDecrochage.ts | 0 .../utils/tauxDevenirFavorable.ts | 2 +- .../{queries => }/utils/tauxInsertion6mois.ts | 2 +- .../data/{queries => }/utils/tauxPoursuite.ts | 2 +- .../data/{queries => }/utils/tauxPression.ts | 2 +- .../{queries => }/utils/tauxRemplissage.ts | 0 36 files changed, 310 insertions(+), 295 deletions(-) delete mode 100644 server/src/modules/data/queries/getFormationsTransformationRegionStats/getFormationsTransformationRegionStats.query.ts delete mode 100644 server/src/modules/data/queries/getRegionStats/getRegionStats.query.ts delete mode 100644 server/src/modules/data/queries/getRegions/getRegions.query.ts rename server/src/modules/data/usecases/countRestitutionIntentionsStats/{countRestitutionIntentionsStats.ts => countRestitutionIntentionsStats.usecase.ts} (100%) rename server/src/modules/data/{queries/getPilotageReformeStats/getPilotageReformeStats.query.ts => usecases/getPilotageReformeStats/dependencies.ts} (93%) create mode 100644 server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts create mode 100644 server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts create mode 100644 server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.usecase.ts rename server/src/modules/data/{queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query.ts => usecases/getPilotageReformeStatsRegions/dependencies.ts} (86%) create mode 100644 server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.route.ts create mode 100644 server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts create mode 100644 server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.usecase.ts rename server/src/modules/data/{queries => }/utils/capaciteAnnee.ts (100%) rename server/src/modules/data/{queries => }/utils/effectifAnnee.ts (100%) rename server/src/modules/data/{queries => }/utils/hasContinuum.ts (96%) rename server/src/modules/data/{queries => }/utils/nbEtablissementFormationRegion.ts (95%) rename server/src/modules/data/{queries => }/utils/notHistorique.ts (92%) rename server/src/modules/data/{queries => }/utils/positionCadran.ts (98%) rename server/src/modules/data/{queries => }/utils/tauxDecrochage.ts (100%) rename server/src/modules/data/{queries => }/utils/tauxDevenirFavorable.ts (98%) rename server/src/modules/data/{queries => }/utils/tauxInsertion6mois.ts (97%) rename server/src/modules/data/{queries => }/utils/tauxPoursuite.ts (97%) rename server/src/modules/data/{queries => }/utils/tauxPression.ts (99%) rename server/src/modules/data/{queries => }/utils/tauxRemplissage.ts (100%) diff --git a/server/src/modules/data/queries/getFormationsTransformationRegionStats/getFormationsTransformationRegionStats.query.ts b/server/src/modules/data/queries/getFormationsTransformationRegionStats/getFormationsTransformationRegionStats.query.ts deleted file mode 100644 index 0464771ee..000000000 --- a/server/src/modules/data/queries/getFormationsTransformationRegionStats/getFormationsTransformationRegionStats.query.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { kdb } from "../../../../db/db"; -import { notHistoriqueIndicateurRegionSortie } from "../../queries/utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; - -export const getFormationsTransformationRegionStats = async ({ - codeRegion, - codeAcademie, - codeDepartement, - codeNiveauDiplome, - millesimeSortie = "2020_2021", -}: { - codeRegion?: string; - codeAcademie?: string; - codeDepartement?: string; - millesimeSortie?: string; - codeNiveauDiplome?: string[]; -}) => { - const statsSortie = await kdb - .selectFrom("indicateurRegionSortie") - .innerJoin( - "formation", - "formation.codeFormationDiplome", - "indicateurRegionSortie.cfd" - ) - .where((w) => { - if (!codeRegion) return w.val(true); - return w("indicateurRegionSortie.codeRegion", "=", codeRegion); - }) - .$call((q) => { - if (!codeDepartement && !codeAcademie) { - return q; - } - return q - .innerJoin( - "departement", - "departement.codeRegion", - "indicateurRegionSortie.codeRegion" - ) - .where((w) => { - if (!codeAcademie) return w.val(true); - return w("departement.codeAcademie", "=", codeAcademie); - }) - .where((w) => { - if (!codeDepartement) return w.val(true); - return w("departement.codeDepartement", "=", codeDepartement); - }); - }) - .$call((q) => { - if (!codeNiveauDiplome?.length) return q; - return q.where("formation.codeNiveauDiplome", "in", codeNiveauDiplome); - }) - .where("indicateurRegionSortie.millesimeSortie", "=", millesimeSortie) - .where(notHistoriqueIndicateurRegionSortie) - .select([ - selectTauxInsertion6moisAgg("indicateurRegionSortie").as("tauxInsertion"), - selectTauxPoursuiteAgg("indicateurRegionSortie").as("tauxPoursuite"), - ]) - .executeTakeFirstOrThrow(); - - return statsSortie; -}; diff --git a/server/src/modules/data/queries/getRegionStats/getRegionStats.query.ts b/server/src/modules/data/queries/getRegionStats/getRegionStats.query.ts deleted file mode 100644 index 63ce22dab..000000000 --- a/server/src/modules/data/queries/getRegionStats/getRegionStats.query.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { sql } from "kysely"; - -import { kdb } from "../../../../db/db"; -import { effectifAnnee } from "../utils/effectifAnnee"; -import { - notHistorique, - notHistoriqueIndicateurRegionSortie, -} from "../utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../utils/tauxRemplissage"; - -export const getRegionStats = async ({ - codeRegion, - codeDiplome, - rentreeScolaire = "2022", - millesimeSortie = "2020_2021", -}: { - codeRegion: string; - codeDiplome?: string[]; - rentreeScolaire?: string; - millesimeSortie?: string; -}) => { - const statsSortie = await kdb - .selectFrom("indicateurRegionSortie") - .innerJoin( - "formation", - "formation.codeFormationDiplome", - "indicateurRegionSortie.cfd" - ) - .where("indicateurRegionSortie.codeRegion", "=", codeRegion) - .$call((q) => { - if (!codeDiplome?.length) return q; - return q.where("formation.codeNiveauDiplome", "in", codeDiplome); - }) - .where("indicateurRegionSortie.millesimeSortie", "=", millesimeSortie) - .where(notHistoriqueIndicateurRegionSortie) - .select([ - selectTauxInsertion6moisAgg("indicateurRegionSortie").as( - "tauxInsertion6mois" - ), - selectTauxPoursuiteAgg("indicateurRegionSortie").as( - "tauxPoursuiteEtudes" - ), - ]) - .executeTakeFirstOrThrow(); - - const stats = await kdb - .selectFrom("formationEtablissement") - .leftJoin( - "formation", - "formation.codeFormationDiplome", - "formationEtablissement.cfd" - ) - .innerJoin("indicateurEntree", (join) => - join - .onRef( - "formationEtablissement.id", - "=", - "indicateurEntree.formationEtablissementId" - ) - .on("indicateurEntree.rentreeScolaire", "=", rentreeScolaire) - ) - .innerJoin( - "etablissement", - "formationEtablissement.UAI", - "etablissement.UAI" - ) - .where("etablissement.codeRegion", "=", codeRegion) - .innerJoin("region", "region.codeRegion", "etablissement.codeRegion") - .$call((q) => { - if (!codeDiplome?.length) return q; - return q.where("formation.codeNiveauDiplome", "in", codeDiplome); - }) - .where(notHistorique) - .select([ - "region.libelleRegion", - sql`COUNT(distinct CONCAT("formationEtablissement"."cfd", "formationEtablissement"."dispositifId"))`.as( - "nbFormations" - ), - sql`SUM(${effectifAnnee({ alias: "indicateurEntree" })}) - `.as("effectif"), - - selectTauxPressionAgg("indicateurEntree").as("tauxPression"), - selectTauxRemplissageAgg("indicateurEntree").as("tauxRemplissage"), - ]) - .groupBy("region.libelleRegion") - .executeTakeFirstOrThrow(); - - return { ...stats, ...statsSortie }; -}; diff --git a/server/src/modules/data/queries/getRegions/getRegions.query.ts b/server/src/modules/data/queries/getRegions/getRegions.query.ts deleted file mode 100644 index 2445831b1..000000000 --- a/server/src/modules/data/queries/getRegions/getRegions.query.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { kdb } from "../../../../db/db"; - -export const getRegions = () => { - return kdb - .selectFrom("region") - .innerJoin("etablissement", "etablissement.codeRegion", "region.codeRegion") - .innerJoin( - "formationEtablissement", - "formationEtablissement.UAI", - "etablissement.UAI" - ) - .select(["region.codeRegion as value", "region.libelleRegion as label"]) - .where("region.codeRegion", "is not", null) - .distinct() - .orderBy("label", "asc") - .execute(); -}; diff --git a/server/src/modules/data/routes/restitutionIntentions.routes.ts b/server/src/modules/data/routes/restitutionIntentions.routes.ts index 3a1424c7d..d336c9274 100644 --- a/server/src/modules/data/routes/restitutionIntentions.routes.ts +++ b/server/src/modules/data/routes/restitutionIntentions.routes.ts @@ -3,7 +3,7 @@ import { ROUTES_CONFIG } from "shared"; import { Server } from "../../../server"; import { hasPermissionHandler } from "../../core"; -import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats"; +import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase"; import { getRestitutionIntentionsStats } from "../usecases/getRestitutionIntentionsStats/getRestitutionIntentionsStats.usecase"; export const restitutionIntentionsRoutes = ({ server }: { server: Server }) => { diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts index 3fd22d657..a111acfd3 100644 --- a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts +++ b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route.ts @@ -3,8 +3,8 @@ import { createRoute } from "@http-wizard/core"; import { Server } from "../../../../server"; import { hasPermissionHandler } from "../../../core"; -import { countRestitutionIntentionsStats } from "./countRestitutionIntentionsStats"; import { countRestitutionIntentionsStatsSchema } from "./countRestitutionIntentionsStats.schema"; +import { countRestitutionIntentionsStats } from "./countRestitutionIntentionsStats.usecase"; export const countRestitutionIntentionsStatsRoute = ({ server, diff --git a/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts b/server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.ts rename to server/src/modules/data/usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase.ts diff --git a/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts b/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts index 6403d6df7..785b22c5f 100644 --- a/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts +++ b/server/src/modules/data/usecases/getDataForPanoramaDepartement/dependencies.ts @@ -2,15 +2,15 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { notHistorique } from "../../queries/utils/notHistorique"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; +import { effectifAnnee } from "../../utils/effectifAnnee"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { notHistorique } from "../../utils/notHistorique"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../utils/tauxDevenirFavorable"; +import { withInsertionReg } from "../../utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../utils/tauxRemplissage"; export const queryFormationsDepartement = async ({ codeDepartement, diff --git a/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts b/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts index 4737055c1..6ac837045 100644 --- a/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts +++ b/server/src/modules/data/usecases/getDataForPanoramaRegion/dependencies.ts @@ -2,15 +2,15 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { notHistorique } from "../../queries/utils/notHistorique"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; +import { effectifAnnee } from "../../utils/effectifAnnee"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { notHistorique } from "../../utils/notHistorique"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../utils/tauxDevenirFavorable"; +import { withInsertionReg } from "../../utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../utils/tauxRemplissage"; export const queryFormationsRegion = async ({ codeRegion, diff --git a/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts b/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts index 185c3ceeb..e2125a03e 100644 --- a/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts +++ b/server/src/modules/data/usecases/getDepartement/getDepartementsStats.query.ts @@ -1,12 +1,12 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; -import { notHistorique } from "../../queries/utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; +import { effectifAnnee } from "../../utils/effectifAnnee"; +import { notHistorique } from "../../utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../utils/tauxRemplissage"; export const getDepartementsStats = async ({ codeDepartement, diff --git a/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts b/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts index ebc83384c..e0348b579 100644 --- a/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts +++ b/server/src/modules/data/usecases/getEtablissement/getEtablissement.query.ts @@ -3,12 +3,12 @@ import { jsonArrayFrom } from "kysely/helpers/postgres"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { notHistorique } from "../../queries/utils/notHistorique"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPression } from "../../queries/utils/tauxPression"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { notHistorique } from "../../utils/notHistorique"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withInsertionReg } from "../../utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../utils/tauxPoursuite"; +import { selectTauxPression } from "../../utils/tauxPression"; export const getEtablissementQuery = async ({ uai, diff --git a/server/src/modules/data/usecases/getEtablissements/dependencies.ts b/server/src/modules/data/usecases/getEtablissements/dependencies.ts index 8c73f7791..439ca6c3d 100644 --- a/server/src/modules/data/usecases/getEtablissements/dependencies.ts +++ b/server/src/modules/data/usecases/getEtablissements/dependencies.ts @@ -3,16 +3,16 @@ import { ExpressionBuilder, sql } from "kysely"; import { kdb } from "../../../../db/db"; import { DB } from "../../../../db/schema"; import { cleanNull } from "../../../../utils/noNull"; -import { capaciteAnnee } from "../../queries/utils/capaciteAnnee"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { notHistorique } from "../../queries/utils/notHistorique"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPression } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissage } from "../../queries/utils/tauxRemplissage"; +import { capaciteAnnee } from "../../utils/capaciteAnnee"; +import { effectifAnnee } from "../../utils/effectifAnnee"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { notHistorique } from "../../utils/notHistorique"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../utils/tauxDevenirFavorable"; +import { withInsertionReg } from "../../utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../utils/tauxPoursuite"; +import { selectTauxPression } from "../../utils/tauxPression"; +import { selectTauxRemplissage } from "../../utils/tauxRemplissage"; const findEtablissementsInDb = async ({ offset = 0, diff --git a/server/src/modules/data/usecases/getFormations/dependencies.ts b/server/src/modules/data/usecases/getFormations/dependencies.ts index de94c11a7..d9383c1d7 100644 --- a/server/src/modules/data/usecases/getFormations/dependencies.ts +++ b/server/src/modules/data/usecases/getFormations/dependencies.ts @@ -3,15 +3,15 @@ import { ExpressionBuilder, sql } from "kysely"; import { kdb } from "../../../../db/db"; import { DB } from "../../../../db/schema"; import { cleanNull } from "../../../../utils/noNull"; -import { capaciteAnnee } from "../../queries/utils/capaciteAnnee"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; -import { withInsertionReg } from "../../queries/utils/tauxInsertion6mois"; -import { withPoursuiteReg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; +import { capaciteAnnee } from "../../utils/capaciteAnnee"; +import { effectifAnnee } from "../../utils/effectifAnnee"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../utils/tauxDevenirFavorable"; +import { withInsertionReg } from "../../utils/tauxInsertion6mois"; +import { withPoursuiteReg } from "../../utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../utils/tauxRemplissage"; const findFormationsInDb = async ({ offset = 0, diff --git a/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts b/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts index 575f53928..d934e3345 100644 --- a/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts +++ b/server/src/modules/data/usecases/getFormationsTransformationStats/dependencies.ts @@ -3,19 +3,19 @@ import { ExpressionBuilder, sql } from "kysely"; import { kdb } from "../../../../db/db"; import { DB } from "../../../../db/schema"; import { cleanNull } from "../../../../utils/noNull"; -import { hasContinuum } from "../../queries/utils/hasContinuum"; -import { notHistoriqueIndicateurRegionSortie } from "../../queries/utils/notHistorique"; -import { withPositionCadran } from "../../queries/utils/positionCadran"; -import { withTauxDevenirFavorableReg } from "../../queries/utils/tauxDevenirFavorable"; +import { hasContinuum } from "../../utils/hasContinuum"; +import { notHistoriqueIndicateurRegionSortie } from "../../utils/notHistorique"; +import { withPositionCadran } from "../../utils/positionCadran"; +import { withTauxDevenirFavorableReg } from "../../utils/tauxDevenirFavorable"; import { selectTauxInsertion6moisAgg, withInsertionReg, -} from "../../queries/utils/tauxInsertion6mois"; +} from "../../utils/tauxInsertion6mois"; import { selectTauxPoursuiteAgg, withPoursuiteReg, -} from "../../queries/utils/tauxPoursuite"; -import { withTauxPressionReg } from "../../queries/utils/tauxPression"; +} from "../../utils/tauxPoursuite"; +import { withTauxPressionReg } from "../../utils/tauxPression"; const selectDifferencePlaces = ( eb: ExpressionBuilder, diff --git a/server/src/modules/data/queries/getPilotageReformeStats/getPilotageReformeStats.query.ts b/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts similarity index 93% rename from server/src/modules/data/queries/getPilotageReformeStats/getPilotageReformeStats.query.ts rename to server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts index 9d224c8e6..b1afb5ffe 100644 --- a/server/src/modules/data/queries/getPilotageReformeStats/getPilotageReformeStats.query.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts @@ -6,15 +6,15 @@ import { getMillesimeFromRentreeScolaire, getRentreeScolaire, } from "../../services/inserJeunesApi/formatMillesime"; -import { effectifAnnee } from "../utils/effectifAnnee"; +import { effectifAnnee } from "../../utils/effectifAnnee"; import { notHistorique, notHistoriqueIndicateurRegionSortie, -} from "../utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../utils/tauxPoursuite"; +} from "../../utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../utils/tauxPoursuite"; -export const getPilotageReformeStats = async ({ +export const getStats = async ({ codeRegion, codeNiveauDiplome, }: { @@ -115,6 +115,31 @@ export const getPilotageReformeStats = async ({ ]) .executeTakeFirstOrThrow(); + return { + anneeN: { + filtered: { + ...(await selectStatsEffectif({ isFiltered: true, annee: 0 })), + ...(await selectStatsSortie({ isFiltered: true, annee: 0 })), + }, + nationale: { + ...(await selectStatsEffectif({ isFiltered: false, annee: 0 })), + ...(await selectStatsSortie({ isFiltered: false, annee: 0 })), + }, + }, + anneeNMoins1: { + filtered: { + ...(await selectStatsEffectif({ isFiltered: true, annee: -1 })), + ...(await selectStatsSortie({ isFiltered: true, annee: -1 })), + }, + nationale: { + ...(await selectStatsEffectif({ isFiltered: false, annee: -1 })), + ...(await selectStatsSortie({ isFiltered: false, annee: -1 })), + }, + }, + }; +}; + +const findFiltersInDb = async () => { const filtersBase = kdb .selectFrom("formation") .leftJoin( @@ -156,32 +181,13 @@ export const getPilotageReformeStats = async ({ .where("niveauDiplome.codeNiveauDiplome", "in", ["500", "320", "400"]) .execute(); - const filters = { + return { regions: (await regions).map(cleanNull), diplomes: (await diplomes).map(cleanNull), }; +}; - return { - filters: filters, - anneeN: { - filtered: { - ...(await selectStatsEffectif({ isFiltered: true, annee: 0 })), - ...(await selectStatsSortie({ isFiltered: true, annee: 0 })), - }, - nationale: { - ...(await selectStatsEffectif({ isFiltered: false, annee: 0 })), - ...(await selectStatsSortie({ isFiltered: false, annee: 0 })), - }, - }, - anneeNMoins1: { - filtered: { - ...(await selectStatsEffectif({ isFiltered: true, annee: -1 })), - ...(await selectStatsSortie({ isFiltered: true, annee: -1 })), - }, - nationale: { - ...(await selectStatsEffectif({ isFiltered: false, annee: -1 })), - ...(await selectStatsSortie({ isFiltered: false, annee: -1 })), - }, - }, - }; +export const dependencies = { + getStats, + findFiltersInDb, }; diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts new file mode 100644 index 000000000..5ffc3aff7 --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts @@ -0,0 +1,27 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getPilotageReformeStatsSchema } from "./getPilotageReformeStats.schema"; +import { getPilotageReformeStats } from "./getPilotageReformeStats.usecase"; + +export const getPilotageReformeStatsRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/pilotage-reforme/stats/regions", { + method: "GET", + schema: getPilotageReformeStatsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage_reforme/lecture"), + handler: async (request, response) => { + const { ...filters } = request.query; + const stats = await getPilotageReformeStats(filters); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts new file mode 100644 index 000000000..18421ae9a --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts @@ -0,0 +1,38 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); + +const StatsSchema = z.object({ + effectif: z.number().optional(), + nbFormations: z.number().optional(), + nbEtablissements: z.number().optional(), + poursuite: z.number().optional(), + insertion: z.number().optional(), +}); + +const StatsAnneeSchema = z.object({ + nationale: StatsSchema, + filtered: StatsSchema, +}); + +const FiltersSchema = z.object({ + codeNiveauDiplome: z.array(z.string()).optional(), + codeRegion: z.string().optional(), +}); + +export const getPilotageReformeStatsSchema = { + querystring: FiltersSchema, + response: { + 200: z.object({ + filters: z.object({ + regions: z.array(OptionSchema), + diplomes: z.array(OptionSchema), + }), + anneeN: StatsAnneeSchema, + anneeNMoins1: StatsAnneeSchema, + }), + }, +}; diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.usecase.ts b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.usecase.ts new file mode 100644 index 000000000..8c70fb4cf --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.usecase.ts @@ -0,0 +1,25 @@ +import { dependencies } from "./dependencies"; + +const getPilotageReformeStatsFactory = + ( + deps = { + getStats: dependencies.getStats, + findFiltersInDb: dependencies.findFiltersInDb, + } + ) => + async (activeFilters: { + codeNiveauDiplome?: string[]; + orderBy?: { order: "asc" | "desc"; column: string }; + }) => { + const [stats, filters] = await Promise.all([ + deps.getStats(activeFilters), + deps.findFiltersInDb(), + ]); + + return { + ...stats, + filters, + }; + }; + +export const getPilotageReformeStats = getPilotageReformeStatsFactory(); diff --git a/server/src/modules/data/queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts similarity index 86% rename from server/src/modules/data/queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query.ts rename to server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts index e7e207798..125669686 100644 --- a/server/src/modules/data/queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts @@ -3,11 +3,11 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; import { getMillesimeFromRentreeScolaire } from "../../services/inserJeunesApi/formatMillesime"; -import { notHistoriqueIndicateurRegionSortie } from "../utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../utils/tauxPoursuite"; +import { notHistoriqueIndicateurRegionSortie } from "../../utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../utils/tauxPoursuite"; -export const getPilotageReformeStatsRegions = async ({ +const getStatsRegions = async ({ codeNiveauDiplome, orderBy = { order: "asc", column: "libelleRegion" }, }: { @@ -63,6 +63,10 @@ export const getPilotageReformeStatsRegions = async ({ }) .execute(); + return statsRegions.map(cleanNull); +}; + +const findFiltersInDb = async () => { const filtersBase = kdb .selectFrom("formation") .leftJoin( @@ -87,12 +91,12 @@ export const getPilotageReformeStatsRegions = async ({ .where("niveauDiplome.codeNiveauDiplome", "in", ["500", "320", "400"]) .execute(); - const filters = { + return { diplomes: (await diplomes).map(cleanNull), }; +}; - return { - filters: filters, - statsRegions: statsRegions.map(cleanNull), - }; +export const dependencies = { + getStatsRegions, + findFiltersInDb, }; diff --git a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.route.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.route.ts new file mode 100644 index 000000000..63be11a62 --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.route.ts @@ -0,0 +1,30 @@ +import { createRoute } from "@http-wizard/core"; + +import { Server } from "../../../../server"; +import { hasPermissionHandler } from "../../../core"; +import { getPilotageReformeStatsRegionsSchema } from "./getPilotageReformeStatsRegions.schema"; +import { getPilotageReformeStatsRegions } from "./getPilotageReformeStatsRegions.usecase"; + +export const getPilotageReformeStatsRegionsRoute = ({ + server, +}: { + server: Server; +}) => { + return createRoute("/pilotage-reforme/stats/regions", { + method: "GET", + schema: getPilotageReformeStatsRegionsSchema, + }).handle((props) => { + server.route({ + ...props, + preHandler: hasPermissionHandler("pilotage_reforme/lecture"), + handler: async (request, response) => { + const { order, orderBy, ...rest } = request.query; + const stats = await getPilotageReformeStatsRegions({ + ...rest, + orderBy: order && orderBy ? { order, column: orderBy } : undefined, + }); + response.status(200).send(stats); + }, + }); + }); +}; diff --git a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts new file mode 100644 index 000000000..c167ae5a4 --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts @@ -0,0 +1,30 @@ +import { z } from "zod"; + +const OptionSchema = z.object({ + label: z.string(), + value: z.string(), +}); +const StatsRegionLineSchema = z.object({ + codeRegion: z.string(), + libelleRegion: z.string().optional(), + poursuite: z.coerce.number().optional(), + insertion: z.coerce.number().optional(), +}); + +const FiltersRegionsSchema = z.object({ + codeNiveauDiplome: z.array(z.string()).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: StatsRegionLineSchema.keyof().optional(), +}); + +export const getPilotageReformeStatsRegionsSchema = { + querystring: FiltersRegionsSchema, + response: { + 200: z.object({ + filters: z.object({ + diplomes: z.array(OptionSchema), + }), + statsRegions: z.array(StatsRegionLineSchema), + }), + }, +}; diff --git a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.usecase.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.usecase.ts new file mode 100644 index 000000000..cae2f6707 --- /dev/null +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.usecase.ts @@ -0,0 +1,26 @@ +import { dependencies } from "./dependencies"; + +const getPilotageReformeStatsRegionsFactory = + ( + deps = { + getStatsRegions: dependencies.getStatsRegions, + findFiltersInDb: dependencies.findFiltersInDb, + } + ) => + async (activeFilters: { + codeNiveauDiplome?: string[]; + orderBy?: { order: "asc" | "desc"; column: string }; + }) => { + const [statsRegions, filters] = await Promise.all([ + deps.getStatsRegions(activeFilters), + deps.findFiltersInDb(), + ]); + + return { + statsRegions, + filters, + }; + }; + +export const getPilotageReformeStatsRegions = + getPilotageReformeStatsRegionsFactory(); diff --git a/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts b/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts index f984c4cc9..6d59fd282 100644 --- a/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts +++ b/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts @@ -1,15 +1,15 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; -import { effectifAnnee } from "../../queries/utils/effectifAnnee"; +import { effectifAnnee } from "../../utils/effectifAnnee"; import { notHistorique, notHistoriqueIndicateurRegionSortie, -} from "../../queries/utils/notHistorique"; -import { selectTauxInsertion6moisAgg } from "../../queries/utils/tauxInsertion6mois"; -import { selectTauxPoursuiteAgg } from "../../queries/utils/tauxPoursuite"; -import { selectTauxPressionAgg } from "../../queries/utils/tauxPression"; -import { selectTauxRemplissageAgg } from "../../queries/utils/tauxRemplissage"; +} from "../../utils/notHistorique"; +import { selectTauxInsertion6moisAgg } from "../../utils/tauxInsertion6mois"; +import { selectTauxPoursuiteAgg } from "../../utils/tauxPoursuite"; +import { selectTauxPressionAgg } from "../../utils/tauxPression"; +import { selectTauxRemplissageAgg } from "../../utils/tauxRemplissage"; export const getRegionStats = async ({ codeRegion, diff --git a/server/src/modules/data/usecases/getRegions/getRegions.route.ts b/server/src/modules/data/usecases/getRegions/getRegions.route.ts index d0a16b889..5d2a1c023 100644 --- a/server/src/modules/data/usecases/getRegions/getRegions.route.ts +++ b/server/src/modules/data/usecases/getRegions/getRegions.route.ts @@ -5,7 +5,7 @@ import { getRegions } from "./getRegions.query"; import { getRegionsSchema } from "./getRegions.schema"; export const getRegionsRoute = ({ server }: { server: Server }) => { - return createRoute("/regions", { + return createRoute("/departements", { method: "GET", schema: getRegionsSchema, }).handle((props) => { diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts index c4875a7d4..614fd904c 100644 --- a/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/dependencies.ts @@ -5,17 +5,6 @@ import { kdb } from "../../../../db/db"; import { DB } from "../../../../db/schema"; import { cleanNull } from "../../../../utils/noNull"; import { RequestUser } from "../../../core/model/User"; -import { nbEtablissementFormationRegion } from "../../../data/queries/utils/nbEtablissementFormationRegion"; -import { selectTauxDevenirFavorable } from "../../../data/queries/utils/tauxDevenirFavorable"; -import { - selectTauxInsertion6mois, - selectTauxInsertion6moisAgg, -} from "../../../data/queries/utils/tauxInsertion6mois"; -import { - selectTauxPoursuite, - selectTauxPoursuiteAgg, -} from "../../../data/queries/utils/tauxPoursuite"; -import { selectTauxPressionParFormationEtParRegionDemande } from "../../../data/queries/utils/tauxPression"; import { countDifferenceCapaciteApprentissage, countDifferenceCapaciteScolaire, @@ -24,8 +13,19 @@ import { isIntentionVisible, isRegionVisible, } from "../../../utils/isIntentionVisible"; -import { notHistoriqueIndicateurRegionSortie } from "../../queries/utils/notHistorique"; -import { getPositionCadran } from "../../queries/utils/positionCadran"; +import { nbEtablissementFormationRegion } from "../../utils/nbEtablissementFormationRegion"; +import { notHistoriqueIndicateurRegionSortie } from "../../utils/notHistorique"; +import { getPositionCadran } from "../../utils/positionCadran"; +import { selectTauxDevenirFavorable } from "../../utils/tauxDevenirFavorable"; +import { + selectTauxInsertion6mois, + selectTauxInsertion6moisAgg, +} from "../../utils/tauxInsertion6mois"; +import { + selectTauxPoursuite, + selectTauxPoursuiteAgg, +} from "../../utils/tauxPoursuite"; +import { selectTauxPressionParFormationEtParRegionDemande } from "../../utils/tauxPression"; const findRestitutionIntentionsStatsInDB = async ({ status, diff --git a/server/src/modules/data/queries/utils/capaciteAnnee.ts b/server/src/modules/data/utils/capaciteAnnee.ts similarity index 100% rename from server/src/modules/data/queries/utils/capaciteAnnee.ts rename to server/src/modules/data/utils/capaciteAnnee.ts diff --git a/server/src/modules/data/queries/utils/effectifAnnee.ts b/server/src/modules/data/utils/effectifAnnee.ts similarity index 100% rename from server/src/modules/data/queries/utils/effectifAnnee.ts rename to server/src/modules/data/utils/effectifAnnee.ts diff --git a/server/src/modules/data/queries/utils/hasContinuum.ts b/server/src/modules/data/utils/hasContinuum.ts similarity index 96% rename from server/src/modules/data/queries/utils/hasContinuum.ts rename to server/src/modules/data/utils/hasContinuum.ts index ff34d0c76..9e9f1a754 100644 --- a/server/src/modules/data/queries/utils/hasContinuum.ts +++ b/server/src/modules/data/utils/hasContinuum.ts @@ -1,7 +1,7 @@ import { ExpressionBuilder, sql } from "kysely"; import { jsonObjectFrom } from "kysely/helpers/postgres"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; type EbRef> = Parameters[0]; diff --git a/server/src/modules/data/queries/utils/nbEtablissementFormationRegion.ts b/server/src/modules/data/utils/nbEtablissementFormationRegion.ts similarity index 95% rename from server/src/modules/data/queries/utils/nbEtablissementFormationRegion.ts rename to server/src/modules/data/utils/nbEtablissementFormationRegion.ts index ba2154c98..5ecd6bdc0 100644 --- a/server/src/modules/data/queries/utils/nbEtablissementFormationRegion.ts +++ b/server/src/modules/data/utils/nbEtablissementFormationRegion.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; export const nbEtablissementFormationRegion = ({ eb, diff --git a/server/src/modules/data/queries/utils/notHistorique.ts b/server/src/modules/data/utils/notHistorique.ts similarity index 92% rename from server/src/modules/data/queries/utils/notHistorique.ts rename to server/src/modules/data/utils/notHistorique.ts index bfb798807..de37db06a 100644 --- a/server/src/modules/data/queries/utils/notHistorique.ts +++ b/server/src/modules/data/utils/notHistorique.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; export const notHistorique = ( eb: ExpressionBuilder diff --git a/server/src/modules/data/queries/utils/positionCadran.ts b/server/src/modules/data/utils/positionCadran.ts similarity index 98% rename from server/src/modules/data/queries/utils/positionCadran.ts rename to server/src/modules/data/utils/positionCadran.ts index b311812b6..21a369181 100644 --- a/server/src/modules/data/queries/utils/positionCadran.ts +++ b/server/src/modules/data/utils/positionCadran.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; import { notHistoriqueIndicateurRegionSortie } from "./notHistorique"; import { selectTauxInsertion6mois, diff --git a/server/src/modules/data/queries/utils/tauxDecrochage.ts b/server/src/modules/data/utils/tauxDecrochage.ts similarity index 100% rename from server/src/modules/data/queries/utils/tauxDecrochage.ts rename to server/src/modules/data/utils/tauxDecrochage.ts diff --git a/server/src/modules/data/queries/utils/tauxDevenirFavorable.ts b/server/src/modules/data/utils/tauxDevenirFavorable.ts similarity index 98% rename from server/src/modules/data/queries/utils/tauxDevenirFavorable.ts rename to server/src/modules/data/utils/tauxDevenirFavorable.ts index f1b7100ad..8d98af78d 100644 --- a/server/src/modules/data/queries/utils/tauxDevenirFavorable.ts +++ b/server/src/modules/data/utils/tauxDevenirFavorable.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; const seuil = 20; diff --git a/server/src/modules/data/queries/utils/tauxInsertion6mois.ts b/server/src/modules/data/utils/tauxInsertion6mois.ts similarity index 97% rename from server/src/modules/data/queries/utils/tauxInsertion6mois.ts rename to server/src/modules/data/utils/tauxInsertion6mois.ts index 42f33cc54..02d22b290 100644 --- a/server/src/modules/data/queries/utils/tauxInsertion6mois.ts +++ b/server/src/modules/data/utils/tauxInsertion6mois.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; const seuil = 20; diff --git a/server/src/modules/data/queries/utils/tauxPoursuite.ts b/server/src/modules/data/utils/tauxPoursuite.ts similarity index 97% rename from server/src/modules/data/queries/utils/tauxPoursuite.ts rename to server/src/modules/data/utils/tauxPoursuite.ts index f353202ab..199cde61a 100644 --- a/server/src/modules/data/queries/utils/tauxPoursuite.ts +++ b/server/src/modules/data/utils/tauxPoursuite.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; const seuil = 20; diff --git a/server/src/modules/data/queries/utils/tauxPression.ts b/server/src/modules/data/utils/tauxPression.ts similarity index 99% rename from server/src/modules/data/queries/utils/tauxPression.ts rename to server/src/modules/data/utils/tauxPression.ts index 741e68829..82b86eb8e 100644 --- a/server/src/modules/data/queries/utils/tauxPression.ts +++ b/server/src/modules/data/utils/tauxPression.ts @@ -1,6 +1,6 @@ import { ExpressionBuilder, RawBuilder, sql } from "kysely"; -import { DB } from "../../../../db/schema"; +import { DB } from "../../../db/schema"; const capaciteAnnee = ( annee: RawBuilder, diff --git a/server/src/modules/data/queries/utils/tauxRemplissage.ts b/server/src/modules/data/utils/tauxRemplissage.ts similarity index 100% rename from server/src/modules/data/queries/utils/tauxRemplissage.ts rename to server/src/modules/data/utils/tauxRemplissage.ts From 5abea803b879a9f745b56b6d5dcaec0f247c426e Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 20 Nov 2023 18:05:42 +0100 Subject: [PATCH 09/20] wip --- .../activateUser/activateUser.schema.ts | 3 +- .../checkActivationToken.route.ts | 2 +- .../core/usecases/logout/logout.schema.ts | 2 +- shared/client/ROUTES_CONFIG.ts | 4 - shared/client/auth/auth.client.ts | 46 ---- shared/client/auth/auth.schema.ts | 76 ------ shared/client/client.ts | 4 - shared/client/intentions/intentions.client.ts | 54 ----- shared/client/intentions/intentions.schema.ts | 227 ------------------ .../activer-compte/ActivateAccountForm.tsx | 6 +- ui/app/(wrapped)/auth/activer-compte/page.tsx | 10 +- ui/app/(wrapped)/auth/login/LoginForm.tsx | 23 +- .../askResetPasswordForm.tsx | 17 +- .../auth/reset-password/ResetPasswordForm.tsx | 20 +- ui/app/(wrapped)/components/Header.tsx | 4 +- .../saisie/[intentionId]/page.client.tsx | 15 +- .../saisie/components/CfdAutocomplete.tsx | 13 +- .../saisie/components/MenuIntention.tsx | 19 +- .../saisie/components/UaiAutocomplete.tsx | 6 +- .../saisie/intentionForm/IntentionForm.tsx | 38 ++- .../CompensationSection.tsx | 25 +- .../(wrapped)/intentions/saisie/new/page.tsx | 16 +- .../intentions/saisie/page.client.tsx | 39 ++- ui/app/layout.tsx | 6 +- 24 files changed, 134 insertions(+), 541 deletions(-) delete mode 100644 shared/client/auth/auth.client.ts delete mode 100644 shared/client/auth/auth.schema.ts delete mode 100644 shared/client/intentions/intentions.client.ts delete mode 100644 shared/client/intentions/intentions.schema.ts diff --git a/server/src/modules/core/usecases/activateUser/activateUser.schema.ts b/server/src/modules/core/usecases/activateUser/activateUser.schema.ts index 69a218835..c46d8e2ae 100644 --- a/server/src/modules/core/usecases/activateUser/activateUser.schema.ts +++ b/server/src/modules/core/usecases/activateUser/activateUser.schema.ts @@ -1,6 +1,5 @@ +import { passwordRegex } from "shared"; import { z } from "zod"; - -import { passwordRegex } from "../../../../../../shared/utils/passwordRegex"; export const activateUserSchema = { body: z.object({ password: z.string().regex(new RegExp(passwordRegex)), diff --git a/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts index 80a2fc869..7f7723d4a 100644 --- a/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts +++ b/server/src/modules/core/usecases/checkActivationToken/checkActivationToken.route.ts @@ -5,7 +5,7 @@ import { checkActivationTokenSchema } from "./checkActivationToken.schema"; import { checkActivationToken } from "./checkActivationToken.usecase"; export const checkActivationTokenRoute = (server: Server) => { - return createRoute("/auth/activate", { + return createRoute("/auth/check-activation-token", { method: "GET", schema: checkActivationTokenSchema, }).handle((props) => { diff --git a/server/src/modules/core/usecases/logout/logout.schema.ts b/server/src/modules/core/usecases/logout/logout.schema.ts index ae5590cd1..ee8f8ca7c 100644 --- a/server/src/modules/core/usecases/logout/logout.schema.ts +++ b/server/src/modules/core/usecases/logout/logout.schema.ts @@ -1,6 +1,6 @@ import { z } from "zod"; export const logoutSchema = { response: { - 200: z.void, + 200: z.void(), }, }; diff --git a/shared/client/ROUTES_CONFIG.ts b/shared/client/ROUTES_CONFIG.ts index bbe23e91b..8ad979e24 100644 --- a/shared/client/ROUTES_CONFIG.ts +++ b/shared/client/ROUTES_CONFIG.ts @@ -1,7 +1,5 @@ -import { authSchemas } from "./auth/auth.schema"; import { etablissementSchemas } from "./etablissements/etablissements.schema"; import { formationSchemas } from "./formations/formation.schema"; -import { intentionsSchemas } from "./intentions/intentions.schema"; import { pilotageReformeSchemas } from "./pilotageReforme/pilotageReforme.schema"; import { pilotageTransformationSchemas } from "./pilotageTransfo/pilotageTransfo.schema"; import { restitutionIntentionsSchemas } from "./restitutionIntentions/restitutionIntentions.schema"; @@ -10,8 +8,6 @@ export const ROUTES_CONFIG = { ...formationSchemas, ...etablissementSchemas, ...pilotageReformeSchemas, - ...authSchemas, - ...intentionsSchemas, ...pilotageTransformationSchemas, ...restitutionIntentionsSchemas, }; diff --git a/shared/client/auth/auth.client.ts b/shared/client/auth/auth.client.ts deleted file mode 100644 index ff6badf51..000000000 --- a/shared/client/auth/auth.client.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createAuthClient = (instance: AxiosInstance) => ({ - login: createClientMethod({ - method: "POST", - url: "/auth/login", - instance, - }), - logout: createClientMethod({ - method: "POST", - url: "/auth/logout", - instance, - }), - whoAmI: createClientMethod({ - method: "GET", - url: "/auth/whoAmI", - instance, - }), - activateUser: createClientMethod({ - method: "POST", - url: "/auth/activate", - instance, - }), - checkActivationToken: createClientMethod< - typeof ROUTES_CONFIG.checkActivationToken - >({ - method: "GET", - url: "/auth/check-activation-token", - instance, - }), - sendResetPassword: createClientMethod( - { - method: "POST", - url: "/auth/send-reset-password", - instance, - } - ), - resetPassword: createClientMethod({ - method: "POST", - url: "/auth/reset-password", - instance, - }), -}); diff --git a/shared/client/auth/auth.schema.ts b/shared/client/auth/auth.schema.ts deleted file mode 100644 index 85b347368..000000000 --- a/shared/client/auth/auth.schema.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -import { PERMISSIONS, Role } from "../../security/permissions"; -import { passwordRegex } from "../../utils/passwordRegex"; - -export const authSchemas = { - login: { - body: Type.Object({ - email: Type.String({ format: "email" }), - password: Type.String(), - }), - response: { - 200: Type.Object({ - token: Type.String(), - }), - }, - }, - logout: { - response: { - 200: Type.Void(), - }, - }, - whoAmI: { - response: { - 200: Type.Optional( - Type.Object({ - user: Type.Object({ - id: Type.String(), - email: Type.String(), - role: Type.Optional( - Type.Union( - Object.keys(PERMISSIONS).map((item) => { - return Type.Literal(item as Role); - }) - ) - ), - codeRegion: Type.Optional(Type.String()), - }), - }) - ), - }, - }, - activateUser: { - body: Type.Object({ - password: Type.String({ - pattern: passwordRegex, - }), - repeatPassword: Type.String(), - activationToken: Type.String(), - }), - }, - checkActivationToken: { - querystring: Type.Object({ - activationToken: Type.String(), - }), - response: { - 200: Type.Object({ - valid: Type.Literal(true), - }), - }, - }, - resetPassword: { - body: Type.Object({ - password: Type.String({ - pattern: passwordRegex, - }), - repeatPassword: Type.String(), - resetPasswordToken: Type.String(), - }), - }, - sendResetPassword: { - body: Type.Object({ - email: Type.String({ format: "email" }), - }), - }, -} as const; diff --git a/shared/client/client.ts b/shared/client/client.ts index 8e6e9dd8c..07aedb3b3 100644 --- a/shared/client/client.ts +++ b/shared/client/client.ts @@ -1,9 +1,7 @@ import { AxiosInstance } from "axios"; -import { createAuthClient } from "./auth/auth.client"; import { createEtablissementClient } from "./etablissements/etablissements.client"; import { createFormationClient } from "./formations/formation.client"; -import { createIntentionsClient } from "./intentions/intentions.client"; import { createPilotageReformeClient } from "./pilotageReforme/pilotageReforme.client"; import { createPilotageTransformationClient } from "./pilotageTransfo/pilotageTransfo.client"; import { createRestitutionIntentionsClient } from "./restitutionIntentions/restitutionIntentions.client"; @@ -12,8 +10,6 @@ export const createClient = (instance: AxiosInstance) => ({ ...createFormationClient(instance), ...createEtablissementClient(instance), ...createPilotageReformeClient(instance), - ...createAuthClient(instance), - ...createIntentionsClient(instance), ...createPilotageTransformationClient(instance), ...createRestitutionIntentionsClient(instance), }); diff --git a/shared/client/intentions/intentions.client.ts b/shared/client/intentions/intentions.client.ts deleted file mode 100644 index 2ad15769a..000000000 --- a/shared/client/intentions/intentions.client.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createIntentionsClient = (instance: AxiosInstance) => ({ - searchEtab: createClientMethod({ - method: "GET", - url: ({ params }) => `/etab/search/${params.search}`, - instance, - }), - getEtab: createClientMethod({ - method: "GET", - url: ({ params }) => `/etab/${params.uai}`, - instance, - }), - searchDiplome: createClientMethod({ - method: "GET", - url: ({ params }) => `/diplome/search/${params.search}`, - instance, - }), - submitDemande: createClientMethod({ - method: "POST", - url: "/demande/submit", - instance, - }), - submitDraftDemande: createClientMethod< - typeof ROUTES_CONFIG.submitDraftDemande - >({ - method: "POST", - url: "/demande/draft", - instance, - }), - deleteDemande: createClientMethod({ - method: "DELETE", - url: ({ params: { id } }) => `/demande/${id}`, - instance, - }), - getDemande: createClientMethod({ - method: "GET", - url: ({ params: { id } }) => `/demande/${id}`, - instance, - }), - getDemandes: createClientMethod({ - method: "GET", - url: "/demandes", - instance, - }), - countDemandes: createClientMethod({ - method: "GET", - url: "/demandes/count", - instance, - }), -}); diff --git a/shared/client/intentions/intentions.schema.ts b/shared/client/intentions/intentions.schema.ts deleted file mode 100644 index 26ceaac2a..000000000 --- a/shared/client/intentions/intentions.schema.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const DemandeData = Type.Object({ - uai: Type.String(), - cfd: Type.String(), - dispositifId: Type.String(), - libelleFCIL: Type.Optional(Type.String()), - rentreeScolaire: Type.Number(), - typeDemande: Type.String(), - compensationUai: Type.Optional(Type.String()), - compensationCfd: Type.Optional(Type.String()), - compensationDispositifId: Type.Optional(Type.String()), - compensationRentreeScolaire: Type.Optional(Type.Number()), - motif: Type.Array(Type.String()), - autreMotif: Type.Optional(Type.String()), - libelleColoration: Type.Optional(Type.String()), - coloration: Type.Boolean(), - amiCma: Type.Boolean(), - poursuitePedagogique: Type.Optional(Type.Boolean()), - commentaire: Type.Optional(Type.String()), - mixte: Type.Optional(Type.Boolean()), - capaciteScolaireActuelle: Type.Optional(Type.Number()), - capaciteScolaire: Type.Optional(Type.Number()), - capaciteScolaireColoree: Type.Optional(Type.Number()), - capaciteApprentissageActuelle: Type.Optional(Type.Number()), - capaciteApprentissage: Type.Optional(Type.Number()), - capaciteApprentissageColoree: Type.Optional(Type.Number()), -}); - -const DemandeSchema = Type.Object({ - id: Type.String(), - createdAt: Type.String(), - status: Type.String(), - ...DemandeData.properties, -}); - -const EtablissementMetadataSchema = Type.Optional( - Type.Object({ - libelle: Type.Optional(Type.String()), - commune: Type.Optional(Type.String()), - }) -); - -const FormationMetadataSchema = Type.Optional( - Type.Object({ - libelle: Type.Optional(Type.String()), - isFCIL: Type.Optional(Type.Boolean()), - dispositifs: Type.Optional( - Type.Array( - Type.Object({ - codeDispositif: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - }) - ) - ), - }) -); - -const MetadataSchema = Type.Object({ - etablissement: EtablissementMetadataSchema, - formation: FormationMetadataSchema, - etablissementCompensation: EtablissementMetadataSchema, - formationCompensation: FormationMetadataSchema, -}); - -const DemandesItem = Type.Object({ - id: Type.String(), - cfd: Type.Optional(Type.String()), - libelleDiplome: Type.Optional(Type.String()), - libelleEtablissement: Type.Optional(Type.String()), - libelleDepartement: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - libelleFCIL: Type.Optional(Type.String()), - uai: Type.Optional(Type.String()), - createdAt: Type.String(), - updatedAt: Type.String(), - createurId: Type.String(), - status: Type.String(), - typeDemande: Type.Optional(Type.String()), - compensationCfd: Type.Optional(Type.String()), - compensationDispositifId: Type.Optional(Type.String()), - compensationUai: Type.Optional(Type.String()), - compensationRentreeScolaire: Type.Optional(Type.Number()), - idCompensation: Type.Optional(Type.String()), - typeCompensation: Type.Optional(Type.String()), - dispositifId: Type.Optional(Type.String()), - rentreeScolaire: Type.Optional(Type.Number()), - motif: Type.Optional(Type.Array(Type.String())), - autreMotif: Type.Optional(Type.String()), - libelleColoration: Type.Optional(Type.String()), - coloration: Type.Optional(Type.Boolean()), - amiCma: Type.Optional(Type.Boolean()), - poursuitePedagogique: Type.Optional(Type.Boolean()), - commentaire: Type.Optional(Type.String()), - mixte: Type.Optional(Type.Boolean()), - capaciteScolaireActuelle: Type.Optional(Type.Number()), - capaciteScolaire: Type.Optional(Type.Number()), - capaciteScolaireColoree: Type.Optional(Type.Number()), - capaciteApprentissageActuelle: Type.Optional(Type.Number()), - capaciteApprentissage: Type.Optional(Type.Number()), - capaciteApprentissageColoree: Type.Optional(Type.Number()), - codeRegion: Type.String(), - codeAcademie: Type.Optional(Type.String()), -}); - -const FiltersSchema = Type.Object({ - status: Type.Optional( - Type.Union([Type.Literal("draft"), Type.Literal("submitted")]) - ), - order: Type.Optional(Type.Union([Type.Literal("asc"), Type.Literal("desc")])), - orderBy: Type.Optional(Type.KeyOf(DemandesItem)), -}); - -export const intentionsSchemas = { - searchEtab: { - params: Type.Object({ - search: Type.String(), - }), - response: { - 200: Type.Array( - Type.Object({ - value: Type.String(), - label: Type.Optional(Type.String()), - commune: Type.Optional(Type.String()), - }) - ), - }, - }, - getEtab: { - params: Type.Object({ - uai: Type.String(), - }), - response: { - 200: Type.Object({ - value: Type.String(), - label: Type.Optional(Type.String()), - commune: Type.Optional(Type.String()), - }), - }, - }, - searchDiplome: { - params: Type.Object({ - search: Type.String(), - }), - response: { - 200: Type.Array( - Type.Object({ - value: Type.String(), - label: Type.String(), - isSpecialite: Type.Boolean(), - isFCIL: Type.Boolean(), - dateFermeture: Type.String(), - dispositifs: Type.Optional( - Type.Array( - Type.Object({ - codeDispositif: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - }) - ) - ), - }) - ), - }, - }, - submitDemande: { - body: Type.Object({ - demande: Type.Object({ - id: Type.Optional(Type.String()), - ...DemandeData.properties, - }), - }), - response: { - 200: Type.Object({ id: Type.String() }), - }, - }, - submitDraftDemande: { - body: Type.Object({ - demande: Type.Object({ - id: Type.Optional(Type.String()), - ...DemandeData.properties, - }), - }), - response: { - 200: Type.Object({ id: Type.String() }), - }, - }, - getDemande: { - params: Type.Object({ id: Type.String() }), - response: { - 200: Type.Object({ - ...Type.Partial(DemandeSchema).properties, - ...Type.Object({ metadata: MetadataSchema }).properties, - ...Type.Object({ canEdit: Type.Boolean() }).properties, - }), - }, - }, - deleteDemande: { - params: Type.Object({ id: Type.String() }), - response: { - 200: Type.Void(), - }, - }, - getDemandes: { - querystring: Type.Intersect([ - FiltersSchema, - Type.Object({ - offset: Type.Optional(Type.Number()), - limit: Type.Optional(Type.Number()), - }), - ]), - response: { - 200: Type.Object({ - demandes: Type.Array(DemandesItem), - count: Type.Number(), - }), - }, - }, - countDemandes: { - response: { - 200: Type.Object({ - total: Type.String(), - draft: Type.String(), - submitted: Type.String(), - }), - }, - }, -} as const; diff --git a/ui/app/(wrapped)/auth/activer-compte/ActivateAccountForm.tsx b/ui/app/(wrapped)/auth/activer-compte/ActivateAccountForm.tsx index b7e65d878..bb3509d09 100644 --- a/ui/app/(wrapped)/auth/activer-compte/ActivateAccountForm.tsx +++ b/ui/app/(wrapped)/auth/activer-compte/ActivateAccountForm.tsx @@ -16,7 +16,7 @@ import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { passwordRegex } from "../../../../../shared/utils/passwordRegex"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; export const ActivateAccountForm = ({ activationToken, @@ -40,7 +40,9 @@ export const ActivateAccountForm = ({ isLoading, } = useMutation({ mutationFn: handleSubmit(async (values) => { - await api.activateUser({ body: { ...values, activationToken } }).call(); + await client + .ref("[POST]/auth/activate") + .query({ body: { ...values, activationToken } }); router.replace("/auth/activer-compte/confirmation"); }), }); diff --git a/ui/app/(wrapped)/auth/activer-compte/page.tsx b/ui/app/(wrapped)/auth/activer-compte/page.tsx index 9110bf8bc..c3320def6 100644 --- a/ui/app/(wrapped)/auth/activer-compte/page.tsx +++ b/ui/app/(wrapped)/auth/activer-compte/page.tsx @@ -1,4 +1,4 @@ -import { serverApi } from "../../../../api.client"; +import { client } from "../../../../api.client"; import { ActivateAccountError } from "./ActivateAccountError"; import { ActivateAccountForm } from "./ActivateAccountForm"; @@ -8,11 +8,9 @@ export default async function ({ searchParams: { activationToken: string }; }) { try { - await serverApi - .checkActivationToken({ - query: { activationToken }, - }) - .call(); + await client.ref("[GET]/auth/check-activation-token").query({ + query: { activationToken }, + }); return (
diff --git a/ui/app/(wrapped)/auth/login/LoginForm.tsx b/ui/app/(wrapped)/auth/login/LoginForm.tsx index 6123bb29e..6bdd82a33 100644 --- a/ui/app/(wrapped)/auth/login/LoginForm.tsx +++ b/ui/app/(wrapped)/auth/login/LoginForm.tsx @@ -13,14 +13,13 @@ import { Link, Text, } from "@chakra-ui/react"; -import { useMutation } from "@tanstack/react-query"; import NextLink from "next/link"; import { useRouter } from "next/navigation"; import { useContext, useEffect } from "react"; import { useForm } from "react-hook-form"; import { emailRegex } from "shared"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; import { AuthContext } from "../authContext"; export const LoginForm = () => { @@ -37,14 +36,12 @@ export const LoginForm = () => { mutateAsync: login, isLoading, isError, - } = useMutation({ - mutationFn: handleSubmit( - async ({ email, password }: { email: string; password: string }) => { - await api.login({ body: { email, password } }).call(); - const { user } = await api.whoAmI({}).call(); - setAuth({ user }); - } - ), + } = client.ref("[POST]/auth/login").useMutation({ + onSuccess: async () => { + const whoAmI = await client.ref("[GET]/auth/whoAmI").query({}); + if (!whoAmI) return; + setAuth({ user: whoAmI.user }); + }, }); const { setAuth, auth } = useContext(AuthContext); @@ -58,7 +55,11 @@ export const LoginForm = () => { return ( - + login({ body: v }))} + > Connexion diff --git a/ui/app/(wrapped)/auth/mot-de-passe-oublie/askResetPasswordForm.tsx b/ui/app/(wrapped)/auth/mot-de-passe-oublie/askResetPasswordForm.tsx index 32d90d8bb..a6b94c6e5 100644 --- a/ui/app/(wrapped)/auth/mot-de-passe-oublie/askResetPasswordForm.tsx +++ b/ui/app/(wrapped)/auth/mot-de-passe-oublie/askResetPasswordForm.tsx @@ -12,13 +12,12 @@ import { Input, Text, } from "@chakra-ui/react"; -import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useContext, useEffect } from "react"; import { useForm } from "react-hook-form"; import { emailRegex } from "shared"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; import { AuthContext } from "../authContext"; export const ForgottenPasswordForm = () => { @@ -37,12 +36,12 @@ export const ForgottenPasswordForm = () => { mutateAsync: login, isLoading, isError, - } = useMutation({ - mutationFn: handleSubmit(async ({ email }: { email: string }) => { - await api.sendResetPassword({ body: { email } }).call(); + } = client.ref("[POST]/auth/send-reset-password").useMutation({ + onSuccess: () => { router.replace("/auth/mot-de-passe-oublie/confirmation"); - }), + }, }); + const { auth } = useContext(AuthContext); useEffect(() => { @@ -52,7 +51,11 @@ export const ForgottenPasswordForm = () => { return ( - + login({ body: v }))} + > Mot de passe oublié diff --git a/ui/app/(wrapped)/auth/reset-password/ResetPasswordForm.tsx b/ui/app/(wrapped)/auth/reset-password/ResetPasswordForm.tsx index 4131506aa..aab3340df 100644 --- a/ui/app/(wrapped)/auth/reset-password/ResetPasswordForm.tsx +++ b/ui/app/(wrapped)/auth/reset-password/ResetPasswordForm.tsx @@ -11,12 +11,11 @@ import { Input, Text, } from "@chakra-ui/react"; -import { useMutation } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { passwordRegex } from "../../../../../shared/utils/passwordRegex"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; export const ResetPasswordForm = ({ resetPasswordToken, @@ -38,18 +37,21 @@ export const ResetPasswordForm = ({ mutate: activateAccount, isError, isLoading, - } = useMutation({ - mutationFn: handleSubmit(async (values) => { - await api - .resetPassword({ body: { ...values, resetPasswordToken } }) - .call(); + } = client.ref("[POST]/auth/reset-password").useMutation({ + onSuccess: () => { router.replace("/auth/reset-password/confirmation"); - }), + }, }); return ( - + + activateAccount({ body: { ...v, resetPasswordToken } }) + )} + > Réinitialisation du mot de passe diff --git a/ui/app/(wrapped)/components/Header.tsx b/ui/app/(wrapped)/components/Header.tsx index 47e49a352..7c7f8c8d0 100644 --- a/ui/app/(wrapped)/components/Header.tsx +++ b/ui/app/(wrapped)/components/Header.tsx @@ -21,7 +21,7 @@ import { useQueryClient } from "@tanstack/react-query"; import NextLink from "next/link"; import { useContext } from "react"; -import { api } from "@/api.client"; +import { client } from "@/api.client"; import { AuthContext } from "@/app/(wrapped)/auth/authContext"; import { Nav } from "./Nav"; @@ -31,7 +31,7 @@ export const Header = () => { const queryClient = useQueryClient(); const logout = async () => { - await api.logout({}).call(); + await client.ref("[POST]/auth/logout").query({}); setAuth(undefined); queryClient.clear(); }; diff --git a/ui/app/(wrapped)/intentions/saisie/[intentionId]/page.client.tsx b/ui/app/(wrapped)/intentions/saisie/[intentionId]/page.client.tsx index 453fb2f29..450bdca16 100644 --- a/ui/app/(wrapped)/intentions/saisie/[intentionId]/page.client.tsx +++ b/ui/app/(wrapped)/intentions/saisie/[intentionId]/page.client.tsx @@ -1,8 +1,6 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; - -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; import { IntentionSpinner } from "../components/IntentionSpinner"; import { IntentionForm } from "../intentionForm/IntentionForm"; @@ -13,11 +11,12 @@ export default ({ intentionId: string; }; }) => { - const { data, isLoading } = useQuery({ - queryKey: [intentionId], - queryFn: api.getDemande({ params: { id: intentionId } }).call, - cacheTime: 0, - }); + const { data, isLoading } = client.ref("[GET]/demande/:id").useQuery( + { params: { id: intentionId } }, + { + cacheTime: 0, + } + ); if (isLoading) return ; return ( diff --git a/ui/app/(wrapped)/intentions/saisie/components/CfdAutocomplete.tsx b/ui/app/(wrapped)/intentions/saisie/components/CfdAutocomplete.tsx index e7acf78ca..45f502b8b 100644 --- a/ui/app/(wrapped)/intentions/saisie/components/CfdAutocomplete.tsx +++ b/ui/app/(wrapped)/intentions/saisie/components/CfdAutocomplete.tsx @@ -1,9 +1,8 @@ import { Flex, Tag } from "@chakra-ui/react"; import { CSSObjectWithLabel } from "react-select"; import AsyncSelect from "react-select/async"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; export const cfdRegex = /^[0-9]{8}$/; @@ -18,7 +17,9 @@ export const CfdAutocompleteInput = ({ defaultValue?: { value: string; label: string }; active?: boolean; inError: boolean; - onChange: (value?: ApiType[number]) => void; + onChange: ( + value?: (typeof client.infer)["[GET]/diplome/search/:search"][number] + ) => void; }) => { const selectStyle = { control: (styles: CSSObjectWithLabel) => ({ @@ -46,11 +47,13 @@ export const CfdAutocompleteInput = ({ isFCIL: false, dateFermeture: "", dispositifs: [], - } as ApiType[0]) + } as (typeof client.infer)["[GET]/diplome/search/:search"][number]) } loadOptions={(search) => { if (search.length >= 3) - return api.searchDiplome({ params: { search } }).call(); + return client + .ref("[GET]/diplome/search/:search") + .query({ params: { search } }); }} formatOptionLabel={(option) => { return ( diff --git a/ui/app/(wrapped)/intentions/saisie/components/MenuIntention.tsx b/ui/app/(wrapped)/intentions/saisie/components/MenuIntention.tsx index ed482c4ae..e5653c939 100644 --- a/ui/app/(wrapped)/intentions/saisie/components/MenuIntention.tsx +++ b/ui/app/(wrapped)/intentions/saisie/components/MenuIntention.tsx @@ -1,15 +1,13 @@ import { QuestionOutlineIcon } from "@chakra-ui/icons"; import { Button, Flex, Text, VStack } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import NextLink from "next/link"; import { useSearchParams } from "next/navigation"; import qs from "qs"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; -export type Query = Parameters[0]["query"]; +export type Query = (typeof client.inferArgs)["[GET]/demandes"]["query"]; export type Filters = Pick; -const fetchCountDemandes = async () => api.countDemandes({}).call(); export const MenuIntention = ({ isRecapView = false, @@ -30,12 +28,13 @@ export const MenuIntention = ({ ? searchParams.filters?.status[0] : ""; - const { data: countDemandes } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["countDemandes"], - queryFn: fetchCountDemandes, - }); + const { data: countDemandes } = client.ref("[GET]/demandes/count").useQuery( + {}, + { + keepPreviousData: true, + staleTime: 10000000, + } + ); return ( diff --git a/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx b/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx index d782ad8ff..b6ee7f81b 100644 --- a/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx +++ b/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx @@ -2,7 +2,7 @@ import { CSSObjectWithLabel } from "react-select"; import AsyncSelect from "react-select/async"; import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { api, client } from "../../../../../api.client"; export const UaiAutocomplete = ({ name, @@ -43,7 +43,9 @@ export const UaiAutocomplete = ({ } loadOptions={(inputValue: string) => { if (inputValue.length >= 3) - return api.searchEtab({ params: { search: inputValue } }).call(); + return client + .ref("[GET]/etab/search/:search") + .query({ params: { search: inputValue } }); }} loadingMessage={({ inputValue }) => inputValue.length >= 3 diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/IntentionForm.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/IntentionForm.tsx index b031bb45a..7ef6dff67 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/IntentionForm.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/IntentionForm.tsx @@ -2,12 +2,10 @@ import { DeleteIcon } from "@chakra-ui/icons"; import { Box, Button, Collapse, Container, Text } from "@chakra-ui/react"; -import { useMutation } from "@tanstack/react-query"; import { AxiosError } from "axios"; import { usePathname, useRouter } from "next/navigation"; import { useEffect, useRef, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; -import { ApiType } from "shared"; import { ConfirmationDelete } from "@/app/(wrapped)/intentions/saisie/components/ConfirmationDelete"; import { @@ -15,7 +13,7 @@ import { PartialIntentionForms, } from "@/app/(wrapped)/intentions/saisie/intentionForm/defaultFormValues"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; import { Breadcrumb } from "../../../../../components/Breadcrumb"; import { CfdUaiSection } from "./cfdUaiSection/CfdUaiSection"; import { InformationsBlock } from "./InformationsBlock"; @@ -29,7 +27,7 @@ export const IntentionForm = ({ disabled?: boolean; formId?: string; defaultValues: PartialIntentionForms; - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; }) => { const { push } = useRouter(); const pathname = usePathname(); @@ -47,10 +45,9 @@ export const IntentionForm = ({ isLoading: isSubmittingDemande, mutateAsync: submitDemande, isSuccess: isDemandeSuccess, - } = useMutation({ - mutationFn: (forms: IntentionForms) => - api.submitDemande({ body: { demande: { id: formId, ...forms } } }).call(), + } = client.ref("[POST]/demande/submit").useMutation({ onSuccess: () => push("/intentions/saisie"), + //@ts-ignore onError: (e: AxiosError<{ errors: Record }>) => { const errors = e.response?.data.errors; setErrors(errors); @@ -61,12 +58,9 @@ export const IntentionForm = ({ isLoading: isSubmittingDraft, mutateAsync: submitDraft, isSuccess: isDraftSuccess, - } = useMutation({ - mutationFn: (forms: IntentionForms) => - api - .submitDraftDemande({ body: { demande: { id: formId, ...forms } } }) - .call(), + } = client.ref("[POST]/demande/draft").useMutation({ onSuccess: () => push("/intentions/saisie"), + //@ts-ignore onError: (e: AxiosError<{ errors: Record }>) => { const errors = e.response?.data.errors; setErrors(errors); @@ -77,11 +71,7 @@ export const IntentionForm = ({ isLoading: isDeleting, mutateAsync: deleteDemande, isSuccess: isDeleteSuccess, - } = useMutation({ - mutationFn: async () => { - if (!formId) return; - await api.deleteDemande({ params: { id: formId } }).call(); - }, + } = client.ref("[DELETE]/demande/:id").useMutation({ onSuccess: () => push("/intentions/saisie"), }); @@ -130,7 +120,9 @@ export const IntentionForm = ({ bg="blue.faded" as="form" noValidate - onSubmit={handleSubmit((values) => submitDemande(values))} + onSubmit={handleSubmit((values) => + submitDemande({ body: { demande: { id: formId, ...values } } }) + )} > {formId && ( + deleteDemande({ params: { id: formId } }) + } Trigger={({ onClick }) => ( diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/CompensationSection.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/CompensationSection.tsx index 134d7ed90..448dffc12 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/CompensationSection.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/CompensationSection.tsx @@ -5,24 +5,24 @@ import { FormLabel, Select, } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import { useEffect, useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; import { isTypeCompensation } from "shared/demandeValidators/validators"; import { IntentionForms } from "@/app/(wrapped)/intentions/saisie/intentionForm/defaultFormValues"; -import { api } from "../../../../../../api.client"; +import { client } from "../../../../../../api.client"; import { CfdAutocompleteInput } from "../../components/CfdAutocomplete"; import { UaiAutocomplete } from "../../components/UaiAutocomplete"; +type Etablissement = (typeof client.infer)["[GET]/api/etab/:uai"]; + export const CompensationSection = ({ disabled, formMetadata, }: { disabled?: boolean; - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; }) => { const { formState: { errors }, @@ -46,17 +46,15 @@ export const CompensationSection = ({ }).unsubscribe ); - const { data, isLoading } = useQuery({ - queryKey: [getValues("uai")], - queryFn: api.getEtab({ params: { uai: getValues("uai") } }).call, - cacheTime: 0, - }); + const { data, isLoading } = client + .ref("[GET]/api/etab/:uai") + .useQuery({ params: { uai: getValues("uai") } }, { cacheTime: 0 }); - const getSameEtabDefaultValue = (): ApiType => { - return data ?? ({} as ApiType); + const getSameEtabDefaultValue = (): Etablissement => { + return data ?? ({} as Etablissement); }; - const getUaiDefaultValue = (value?: string): ApiType => { + const getUaiDefaultValue = (value?: string): Etablissement => { if (formMetadata?.etablissementCompensation?.libelle && value) return { label: formMetadata?.etablissementCompensation.libelle, @@ -72,7 +70,8 @@ export const CompensationSection = ({ }, []); const [dispositifsCompensation, setDispositifsCompensation] = useState< - ApiType[number]["dispositifs"] | undefined + | (typeof client.infer)["[GET]/diplome/search/:search"][number]["dispositifs"] + | undefined >(formMetadata?.formationCompensation?.dispositifs); return ( diff --git a/ui/app/(wrapped)/intentions/saisie/new/page.tsx b/ui/app/(wrapped)/intentions/saisie/new/page.tsx index 196e31227..c585e9805 100644 --- a/ui/app/(wrapped)/intentions/saisie/new/page.tsx +++ b/ui/app/(wrapped)/intentions/saisie/new/page.tsx @@ -1,9 +1,8 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { useSearchParams } from "next/navigation"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; import { GuardPermission } from "../../../../../utils/security/GuardPermission"; import { IntentionSpinner } from "../components/IntentionSpinner"; import { IntentionForm } from "../intentionForm/IntentionForm"; @@ -11,12 +10,13 @@ export default () => { const queryParams = useSearchParams(); const intentionId = queryParams.get("intentionId"); - const { data, isLoading } = useQuery({ - enabled: !!intentionId, - queryKey: [intentionId], - queryFn: api.getDemande({ params: { id: intentionId ?? "" } }).call, - cacheTime: 0, - }); + const { data, isLoading } = client.ref("[GET]/demande/:id").useQuery( + { params: { id: intentionId ?? "" } }, + { + enabled: !!intentionId, + cacheTime: 0, + } + ); if (isLoading && !!intentionId) return ; return ( diff --git a/ui/app/(wrapped)/intentions/saisie/page.client.tsx b/ui/app/(wrapped)/intentions/saisie/page.client.tsx index be0a41e72..fa98ac1e5 100644 --- a/ui/app/(wrapped)/intentions/saisie/page.client.tsx +++ b/ui/app/(wrapped)/intentions/saisie/page.client.tsx @@ -17,16 +17,14 @@ import { Thead, Tr, } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import NextLink from "next/link"; import { useRouter, useSearchParams } from "next/navigation"; import { usePlausible } from "next-plausible"; import qs from "qs"; -import { ApiType } from "shared"; import { usePermission } from "@/utils/security/usePermission"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; import { Breadcrumb } from "../../../../components/Breadcrumb"; import { OrderIcon } from "../../../../components/OrderIcon"; import { TableFooter } from "../../../../components/TableFooter"; @@ -68,14 +66,16 @@ const DEMANDES_COLUMNS = { capaciteApprentissageActuelle: "Capacité apprentissage actuelle", capaciteApprentissage: "Capacité apprentissage", capaciteApprentissageColoree: "Capacité apprentissage coloree", -} satisfies ExportColumns["demandes"][number]>; +} satisfies ExportColumns< + (typeof client.infer)["[GET]/demandes"]["demandes"][number] +>; -export type Query = Parameters[0]["query"]; +export type Query = (typeof client.inferArgs)["[GET]/demandes"]["query"]; export type Filters = Pick; export type Order = Pick; const PAGE_SIZE = 30; -const fetchDemandes = async (query: Query) => api.getDemandes({ query }).call(); +// const fetchDemandes = async (query: Query) => api.getDemandes({ query }).call(); export const PageClient = () => { const router = useRouter(); @@ -116,21 +116,20 @@ export const PageClient = () => { const order = searchParams.order ?? { order: "asc" }; const page = searchParams.page ? parseInt(searchParams.page) : 0; - const { data, isLoading } = useQuery({ - keepPreviousData: true, - staleTime: 0, - queryKey: ["demandes", filters, order, page], - queryFn: () => - fetchDemandes({ + const { data, isLoading } = client.ref("[GET]/demandes").useQuery( + { + query: { ...filters, ...order, offset: page * PAGE_SIZE, limit: PAGE_SIZE, - }), - }); + }, + }, + { keepPreviousData: true, staleTime: 0 } + ); const nouvelleCompensation = ( - demandeCompensee: ApiType["demandes"][0] + demandeCompensee: (typeof client.infer)["[GET]/demandes"]["demandes"][0] ) => { router.push( createParametrizedUrl(`${location.pathname}/new`, { @@ -229,7 +228,7 @@ export const PageClient = () => { {data?.demandes.map( ( - demande: ApiType["demandes"][0] + demande: (typeof client.infer)["[GET]/demandes"]["demandes"][0] ) => ( { pl="4" onExport={async () => { trackEvent("demandes:export"); - const data = await api - .getDemandes({ - query: { ...filters, ...order, limit: 10000000 }, - }) - .call(); + const data = await client.ref("[GET]/demandes").query({ + query: { ...filters, ...order, limit: 10000000 }, + }); downloadCsv( "export_demandes.csv", diff --git a/ui/app/layout.tsx b/ui/app/layout.tsx index 1a3a22a58..ff0bb69c3 100644 --- a/ui/app/layout.tsx +++ b/ui/app/layout.tsx @@ -3,7 +3,7 @@ import "./globals.css"; import { Metadata } from "next"; import { headers } from "next/headers"; -import { serverApi } from "../api.client"; +import { client } from "../api.client"; import RootLayoutClient from "./layoutClient"; export const metadata: Metadata = { @@ -15,7 +15,9 @@ export const metadata: Metadata = { const fetchAuth = async () => { const headersList = Object.fromEntries(headers().entries()); try { - return await serverApi.whoAmI({}, { headers: headersList }).call(); + return await client + .ref("[GET]/auth/whoAmI") + .query({}, { headers: headersList }); } catch (e) { return undefined; } From 14a2234ecd07a1607220590d78931bbc67807f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Mon, 20 Nov 2023 18:08:07 +0100 Subject: [PATCH 10/20] refactor: wip front --- server/src/modules/data/index.ts | 11 +-- .../data/routes/pilotageReforme.routes.ts | 70 ++++++++-------- .../routes/pilotageTransformation.routes.ts | 50 +++++------ .../src/modules/data/routes/regions.routes.ts | 52 ++++++------ .../routes/restitutionIntentions.routes.ts | 82 +++++++++---------- .../getPilotageReformeStats.route.ts | 2 +- ...gionsStats.query.ts => getRegion.query.ts} | 0 .../usecases/getRegion/getRegion.route.ts | 2 +- .../usecases/getRegions/getRegions.route.ts | 2 +- .../getRestitutionIntentionStats.route.ts | 2 +- .../getTransformationsStats.route.ts | 2 +- .../getTransformationsStats.schema.ts | 6 +- .../(wrapped)/console/etablissements/page.tsx | 16 ++-- ui/app/(wrapped)/intentions/pilotage/page.tsx | 26 +++--- .../(wrapped)/intentions/restitution/page.tsx | 58 ++++++------- 15 files changed, 190 insertions(+), 191 deletions(-) rename server/src/modules/data/usecases/getRegion/{getRegionsStats.query.ts => getRegion.query.ts} (100%) diff --git a/server/src/modules/data/index.ts b/server/src/modules/data/index.ts index a608f3c42..e0ba3f4d3 100644 --- a/server/src/modules/data/index.ts +++ b/server/src/modules/data/index.ts @@ -1,7 +1,4 @@ import { Server } from "../../server"; -import { pilotageReformeRoutes } from "./routes/pilotageReforme.routes"; -import { pilotageTransformationRoutes } from "./routes/pilotageTransformation.routes"; -import { restitutionIntentionsRoutes } from "./routes/restitutionIntentions.routes"; import { countRestitutionIntentionsStatsRoute } from "./usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.route"; import { getDataForPanoramaDepartementRoute } from "./usecases/getDataForPanoramaDepartement/getDataForPanoramaDepartement.route"; import { getDataForPanoramaRegionRoute } from "./usecases/getDataForPanoramaRegion/getDataForPanoramaRegion.route"; @@ -11,16 +8,14 @@ import { getEtablissementRoute } from "./usecases/getEtablissement/etablissement import { getEtablissementsRoutes } from "./usecases/getEtablissements/getEtablissements.routes"; import { getFormationsRoute } from "./usecases/getFormations/formations.routes"; import { getFormationsTransformationsRoute } from "./usecases/getFormationsTransformationStats/getFormationsTransformations.route"; +import { getPilotageReformeStatsRoute } from "./usecases/getPilotageReformeStats/getPilotageReformeStats.route"; +import { getPilotageReformeStatsRegionsRoute } from "./usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.route"; import { getRegionRoute } from "./usecases/getRegion/getRegion.route"; import { getRegionsRoute } from "./usecases/getRegions/getRegions.route"; import { getRestitutionIntentionsStatsRoute } from "./usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route"; import { getTransformationsStatsRoutes } from "./usecases/getTransformationStats/getTransformationsStats.route"; export const registerFormationModule = ({ server }: { server: Server }) => { - pilotageReformeRoutes({ server }); - pilotageTransformationRoutes({ server }); - restitutionIntentionsRoutes({ server }); - return { ...getFormationsRoute({ server }), ...getEtablissementsRoutes({ server }), @@ -35,5 +30,7 @@ export const registerFormationModule = ({ server }: { server: Server }) => { ...getTransformationsStatsRoutes({ server }), ...getRestitutionIntentionsStatsRoute({ server }), ...countRestitutionIntentionsStatsRoute({ server }), + ...getPilotageReformeStatsRoute({ server }), + ...getPilotageReformeStatsRegionsRoute({ server }), }; }; diff --git a/server/src/modules/data/routes/pilotageReforme.routes.ts b/server/src/modules/data/routes/pilotageReforme.routes.ts index c7f3c7eeb..9a3ef2bf7 100644 --- a/server/src/modules/data/routes/pilotageReforme.routes.ts +++ b/server/src/modules/data/routes/pilotageReforme.routes.ts @@ -1,38 +1,38 @@ -import { ROUTES_CONFIG } from "shared"; +// import { ROUTES_CONFIG } from "shared"; -import { Server } from "../../../server"; -import { hasPermissionHandler } from "../../core"; -import { getPilotageReformeStats } from "../queries/getPilotageReformeStats/getPilotageReformeStats.query"; -import { getPilotageReformeStatsRegions } from "../queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query"; +// import { Server } from "../../../server"; +// import { hasPermissionHandler } from "../../core"; +// import { getPilotageReformeStats } from "../queries/getPilotageReformeStats/getPilotageReformeStats.query"; +// import { getPilotageReformeStatsRegions } from "../queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query"; -export const pilotageReformeRoutes = ({ server }: { server: Server }) => { - server.get( - "/pilotage-reforme/stats", - { - schema: ROUTES_CONFIG.getPilotageReformeStats, - preHandler: hasPermissionHandler("pilotage_reforme/lecture"), - }, - async (request, response) => { - const stats = await getPilotageReformeStats({ - ...request.query, - }); - response.status(200).send(stats); - } - ); +// export const pilotageReformeRoutes = ({ server }: { server: Server }) => { +// server.get( +// "/pilotage-reforme/stats", +// { +// schema: ROUTES_CONFIG.getPilotageReformeStats, +// preHandler: hasPermissionHandler("pilotage_reforme/lecture"), +// }, +// async (request, response) => { +// const stats = await getPilotageReformeStats({ +// ...request.query, +// }); +// response.status(200).send(stats); +// } +// ); - server.get( - "/pilotage-reforme/stats/regions", - { - schema: ROUTES_CONFIG.getPilotageReformeStatsRegions, - preHandler: hasPermissionHandler("pilotage_reforme/lecture"), - }, - async (request, response) => { - const { order, orderBy, ...rest } = request.query; - const stats = await getPilotageReformeStatsRegions({ - ...rest, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(stats); - } - ); -}; +// server.get( +// "/pilotage-reforme/stats/regions", +// { +// schema: ROUTES_CONFIG.getPilotageReformeStatsRegions, +// preHandler: hasPermissionHandler("pilotage_reforme/lecture"), +// }, +// async (request, response) => { +// const { order, orderBy, ...rest } = request.query; +// const stats = await getPilotageReformeStatsRegions({ +// ...rest, +// orderBy: order && orderBy ? { order, column: orderBy } : undefined, +// }); +// response.status(200).send(stats); +// } +// ); +// }; diff --git a/server/src/modules/data/routes/pilotageTransformation.routes.ts b/server/src/modules/data/routes/pilotageTransformation.routes.ts index 1fc32fb02..bafc544ec 100644 --- a/server/src/modules/data/routes/pilotageTransformation.routes.ts +++ b/server/src/modules/data/routes/pilotageTransformation.routes.ts @@ -1,28 +1,28 @@ -import { ROUTES_CONFIG } from "shared"; +// import { ROUTES_CONFIG } from "shared"; -import { Server } from "../../../server"; -import { hasPermissionHandler } from "../../core"; -import { getTransformationStats } from "../usecases/getTransformationStats/getTransformationStats.usecase"; +// import { Server } from "../../../server"; +// import { hasPermissionHandler } from "../../core"; +// import { getTransformationStats } from "../usecases/getTransformationStats/getTransformationStats.usecase"; -export const pilotageTransformationRoutes = ({ - server, -}: { - server: Server; -}) => { - server.get( - "/pilotage-transformation/stats", - { - schema: ROUTES_CONFIG.getTransformationStats, - preHandler: hasPermissionHandler("pilotage-intentions/lecture"), - }, - async (request, response) => { - const { order, orderBy, ...filters } = request.query; +// export const pilotageTransformationRoutes = ({ +// server, +// }: { +// server: Server; +// }) => { +// server.get( +// "/pilotage-transformation/stats", +// { +// schema: ROUTES_CONFIG.getTransformationStats, +// preHandler: hasPermissionHandler("pilotage-intentions/lecture"), +// }, +// async (request, response) => { +// const { order, orderBy, ...filters } = request.query; - const stats = await getTransformationStats({ - ...filters, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - }); - response.status(200).send(stats); - } - ); -}; +// const stats = await getTransformationStats({ +// ...filters, +// orderBy: order && orderBy ? { order, column: orderBy } : undefined, +// }); +// response.status(200).send(stats); +// } +// ); +// }; diff --git a/server/src/modules/data/routes/regions.routes.ts b/server/src/modules/data/routes/regions.routes.ts index 47d75db6b..3ab0874c1 100644 --- a/server/src/modules/data/routes/regions.routes.ts +++ b/server/src/modules/data/routes/regions.routes.ts @@ -1,29 +1,29 @@ -import { ROUTES_CONFIG } from "shared"; +// import { ROUTES_CONFIG } from "shared"; -import { Server } from "../../../server"; -import { getRegions } from "../queries/getRegions/getRegions.query"; -import { getRegionStats } from "../queries/getRegionStats/getRegionStats.query"; +// import { Server } from "../../../server"; +// import { getRegions } from "../queries/getRegions/getRegions.query"; +// import { getRegionStats } from "../queries/getRegionStats/getRegionStats.query"; -export const regionsRoutes = ({ server }: { server: Server }) => { - server.get( - "/regions", - { schema: ROUTES_CONFIG.getRegions }, - async (_, response) => { - const regions = await getRegions(); - response.status(200).send(regions); - } - ); +// export const regionsRoutes = ({ server }: { server: Server }) => { +// server.get( +// "/regions", +// { schema: ROUTES_CONFIG.getRegions }, +// async (_, response) => { +// const regions = await getRegions(); +// response.status(200).send(regions); +// } +// ); - server.get( - "/region/:codeRegion", - { schema: ROUTES_CONFIG.getRegionStats }, - async (request, response) => { - const regionStats = await getRegionStats({ - ...request.params, - ...request.query, - }); - if (!regionStats) return response.status(404).send(); - response.status(200).send(regionStats); - } - ); -}; +// server.get( +// "/region/:codeRegion", +// { schema: ROUTES_CONFIG.getRegionStats }, +// async (request, response) => { +// const regionStats = await getRegionStats({ +// ...request.params, +// ...request.query, +// }); +// if (!regionStats) return response.status(404).send(); +// response.status(200).send(regionStats); +// } +// ); +// }; diff --git a/server/src/modules/data/routes/restitutionIntentions.routes.ts b/server/src/modules/data/routes/restitutionIntentions.routes.ts index d336c9274..c3b8bb66c 100644 --- a/server/src/modules/data/routes/restitutionIntentions.routes.ts +++ b/server/src/modules/data/routes/restitutionIntentions.routes.ts @@ -1,46 +1,46 @@ -import Boom from "@hapi/boom"; -import { ROUTES_CONFIG } from "shared"; +// import Boom from "@hapi/boom"; +// import { ROUTES_CONFIG } from "shared"; -import { Server } from "../../../server"; -import { hasPermissionHandler } from "../../core"; -import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase"; -import { getRestitutionIntentionsStats } from "../usecases/getRestitutionIntentionsStats/getRestitutionIntentionsStats.usecase"; +// import { Server } from "../../../server"; +// import { hasPermissionHandler } from "../../core"; +// import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase"; +// import { getRestitutionIntentionsStats } from "../usecases/getRestitutionIntentionsStats/getRestitutionIntentionsStats.usecase"; -export const restitutionIntentionsRoutes = ({ server }: { server: Server }) => { - server.get( - "/intentions/stats", - { - schema: ROUTES_CONFIG.getRestitutionIntentionsStats, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { order, orderBy, ...rest } = request.query; - if (!request.user) throw Boom.forbidden(); +// export const restitutionIntentionsRoutes = ({ server }: { server: Server }) => { +// server.get( +// "/intentions/stats", +// { +// schema: ROUTES_CONFIG.getRestitutionIntentionsStats, +// preHandler: hasPermissionHandler("intentions/lecture"), +// }, +// async (request, response) => { +// const { order, orderBy, ...rest } = request.query; +// if (!request.user) throw Boom.forbidden(); - const result = await getRestitutionIntentionsStats({ - ...rest, - orderBy: order && orderBy ? { order, column: orderBy } : undefined, - user: request.user, - }); - response.status(200).send(result); - } - ); +// const result = await getRestitutionIntentionsStats({ +// ...rest, +// orderBy: order && orderBy ? { order, column: orderBy } : undefined, +// user: request.user, +// }); +// response.status(200).send(result); +// } +// ); - server.get( - "/intentions/stats/count", - { - schema: ROUTES_CONFIG.countRestitutionIntentionsStats, - preHandler: hasPermissionHandler("intentions/lecture"), - }, - async (request, response) => { - const { ...filters } = request.query; - if (!request.user) throw Boom.forbidden(); +// server.get( +// "/intentions/stats/count", +// { +// schema: ROUTES_CONFIG.countRestitutionIntentionsStats, +// preHandler: hasPermissionHandler("intentions/lecture"), +// }, +// async (request, response) => { +// const { ...filters } = request.query; +// if (!request.user) throw Boom.forbidden(); - const result = await getCountRestitutionIntentionsStats({ - ...filters, - user: request.user, - }); - response.status(200).send(result); - } - ); -}; +// const result = await getCountRestitutionIntentionsStats({ +// ...filters, +// user: request.user, +// }); +// response.status(200).send(result); +// } +// ); +// }; diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts index 5ffc3aff7..745f8d8f0 100644 --- a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.route.ts @@ -10,7 +10,7 @@ export const getPilotageReformeStatsRoute = ({ }: { server: Server; }) => { - return createRoute("/pilotage-reforme/stats/regions", { + return createRoute("/pilotage-reforme/stats", { method: "GET", schema: getPilotageReformeStatsSchema, }).handle((props) => { diff --git a/server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts b/server/src/modules/data/usecases/getRegion/getRegion.query.ts similarity index 100% rename from server/src/modules/data/usecases/getRegion/getRegionsStats.query.ts rename to server/src/modules/data/usecases/getRegion/getRegion.query.ts diff --git a/server/src/modules/data/usecases/getRegion/getRegion.route.ts b/server/src/modules/data/usecases/getRegion/getRegion.route.ts index 8fae1976a..be783e384 100644 --- a/server/src/modules/data/usecases/getRegion/getRegion.route.ts +++ b/server/src/modules/data/usecases/getRegion/getRegion.route.ts @@ -1,8 +1,8 @@ import { createRoute } from "@http-wizard/core"; import { Server } from "../../../../server"; +import { getRegionStats } from "./getRegion.query"; import { getRegionSchema } from "./getRegion.schema"; -import { getRegionStats } from "./getRegionsStats.query"; export const getRegionRoute = ({ server }: { server: Server }) => { return createRoute("/region/:codeRegion", { diff --git a/server/src/modules/data/usecases/getRegions/getRegions.route.ts b/server/src/modules/data/usecases/getRegions/getRegions.route.ts index 5d2a1c023..d0a16b889 100644 --- a/server/src/modules/data/usecases/getRegions/getRegions.route.ts +++ b/server/src/modules/data/usecases/getRegions/getRegions.route.ts @@ -5,7 +5,7 @@ import { getRegions } from "./getRegions.query"; import { getRegionsSchema } from "./getRegions.schema"; export const getRegionsRoute = ({ server }: { server: Server }) => { - return createRoute("/departements", { + return createRoute("/regions", { method: "GET", schema: getRegionsSchema, }).handle((props) => { diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts index d2cacb021..a70444a36 100644 --- a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.route.ts @@ -11,7 +11,7 @@ export const getRestitutionIntentionsStatsRoute = ({ }: { server: Server; }) => { - return createRoute("/pilotage-transformation/formations", { + return createRoute("/intentions/stats", { method: "GET", schema: getRestitutionIntentionsStatsSchema, }).handle((props) => { diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts index 60e2d50fa..50adaa28b 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.route.ts @@ -10,7 +10,7 @@ export const getTransformationsStatsRoutes = ({ }: { server: Server; }) => { - return createRoute("/pilotage-transformation/formations", { + return createRoute("/pilotage-transformation/stats", { method: "GET", schema: getTransformationStatsSchema, }).handle((props) => { diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts index 6fd674337..f3709f3b1 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts @@ -28,7 +28,7 @@ const StatsTransfoSchema = z.object({ national: ScopedStatsTransfoSchema, regions: z.record( z.string(), - ScopedStatsTransfoSchema.partial().merge( + ScopedStatsTransfoSchema.merge( z.object({ codeRegion: z.string().optional(), }) @@ -36,7 +36,7 @@ const StatsTransfoSchema = z.object({ ), academies: z.record( z.string(), - ScopedStatsTransfoSchema.partial().merge( + ScopedStatsTransfoSchema.merge( z.object({ codeAcademie: z.string().optional(), }) @@ -44,7 +44,7 @@ const StatsTransfoSchema = z.object({ ), departements: z.record( z.string(), - ScopedStatsTransfoSchema.partial().merge( + ScopedStatsTransfoSchema.merge( z.object({ codeDepartement: z.string().optional(), }) diff --git a/ui/app/(wrapped)/console/etablissements/page.tsx b/ui/app/(wrapped)/console/etablissements/page.tsx index eb1e18ca9..5a546f58e 100644 --- a/ui/app/(wrapped)/console/etablissements/page.tsx +++ b/ui/app/(wrapped)/console/etablissements/page.tsx @@ -21,7 +21,7 @@ import { Fragment, useContext, useEffect, useState } from "react"; import { unstable_batchedUpdates } from "react-dom"; import { ApiType } from "shared"; -import { api } from "@/api.client"; +import { api, client } from "@/api.client"; import { OrderIcon } from "@/components/OrderIcon"; import { TableFooter } from "@/components/TableFooter"; import { createParametrizedUrl } from "@/utils/createParametrizedUrl"; @@ -149,19 +149,17 @@ export default function Etablissements() { } }, []); - const { data, isFetching } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["etablissements", page, order, filters], - queryFn: () => { - return fetchEtablissements({ + const { data, isFetching } = client.ref("[GET]/etablissements").useQuery( + { + query: { ...filters, ...order, offset: page * PAGE_SIZE, limit: PAGE_SIZE, - }); + }, }, - }); + { keepPreviousData: true, staleTime: 10000000 } + ); const trackEvent = usePlausible(); const handleOrder = (column: Exclude) => { diff --git a/ui/app/(wrapped)/intentions/pilotage/page.tsx b/ui/app/(wrapped)/intentions/pilotage/page.tsx index f869b6529..48c8517e3 100644 --- a/ui/app/(wrapped)/intentions/pilotage/page.tsx +++ b/ui/app/(wrapped)/intentions/pilotage/page.tsx @@ -1,15 +1,14 @@ "use client"; import { Box, Container, SimpleGrid } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import { useRouter, useSearchParams } from "next/navigation"; import { usePlausible } from "next-plausible"; import qs from "qs"; import { useContext, useEffect, useState } from "react"; +import { client } from "@/api.client"; import { CadranSection } from "@/app/(wrapped)/intentions/pilotage/components/CadranSection"; -import { api } from "../../../../api.client"; import { createParametrizedUrl } from "../../../../utils/createParametrizedUrl"; import { withAuth } from "../../../../utils/security/withAuth"; import { CodeRegionFilterContext } from "../../../layoutClient"; @@ -99,17 +98,20 @@ export default withAuth( }); }; - const { data, isLoading: isLoading } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["pilotageTransfo", filters, order], - queryFn: api.getTransformationStats({ - query: { - ...filters, - ...order, + const { data, isLoading: isLoading } = client + .ref("[GET]/pilotage-transformation/stats") + .useQuery( + { + query: { + ...filters, + ...order, + }, }, - }).call, - }); + { + keepPreviousData: true, + staleTime: 10000000, + } + ); useEffect(() => { if (codeRegionFilter != "") { diff --git a/ui/app/(wrapped)/intentions/restitution/page.tsx b/ui/app/(wrapped)/intentions/restitution/page.tsx index 61ceb256f..78bc938c1 100644 --- a/ui/app/(wrapped)/intentions/restitution/page.tsx +++ b/ui/app/(wrapped)/intentions/restitution/page.tsx @@ -1,7 +1,6 @@ "use client"; import { Box, Container, Flex } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import { useRouter, useSearchParams } from "next/navigation"; import { usePlausible } from "next-plausible"; import qs from "qs"; @@ -9,7 +8,7 @@ import { useContext, useEffect, useState } from "react"; import { GuardPermission } from "@/utils/security/GuardPermission"; -import { api } from "../../../../api.client"; +import { api, client } from "../../../../api.client"; import { TableFooter } from "../../../../components/TableFooter"; import { createParametrizedUrl } from "../../../../utils/createParametrizedUrl"; import { downloadCsv } from "../../../../utils/downloadCsv"; @@ -121,33 +120,36 @@ export default () => { }); }; - const { data, isLoading: isLoading } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["restitutionIntentionsStats", filters, order, page], - queryFn: api.getRestitutionIntentionsStats({ - query: { - ...filters, - ...order, - offset: page * PAGE_SIZE, - limit: PAGE_SIZE, + const { data, isLoading: isLoading } = client + .ref("[GET]/intentions/stats") + .useQuery( + { + query: { + ...filters, + ...order, + offset: page * PAGE_SIZE, + limit: PAGE_SIZE, + }, }, - }).call, - }); - - const { data: countData, isLoading: isLoadingCount } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["countRestitutionIntentionsStats", filters], - queryFn: async () => - api - .countRestitutionIntentionsStats({ - query: { - ...filters, - }, - }) - .call(), - }); + { + keepPreviousData: true, + staleTime: 10000000, + } + ); + + const { data: countData, isLoading: isLoadingCount } = client + .ref("[GET]/intentions/stats/count") + .useQuery( + { + query: { + ...filters, + }, + }, + { + keepPreviousData: true, + staleTime: 10000000, + } + ); return ( From ec4b35e6c997d227c8f06fe1ab5e77af0c2be95e Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 11:24:00 +0100 Subject: [PATCH 11/20] wip --- ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/package.json b/ui/package.json index 6f4ceb125..f920f407d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -37,7 +37,7 @@ "react-select": "^5.7.4", "server": "*", "shared": "*", - "typescript": "5.1.6", + "typescript": "5.0.2", "zod": "^3.22.4" } } From 8828da2548d3f0c013a03ae576c7c1ce4a248393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Tue, 21 Nov 2023 11:24:09 +0100 Subject: [PATCH 12/20] refactor: front --- .../getEtablissements.schema.ts | 4 +- .../getFormations/getFormation.schema.ts | 4 +- .../getPilotageReformeStats.schema.ts | 14 ++--- .../getPilotageReformeStatsRegions.schema.ts | 4 +- .../getRestitutionIntentionStats.schema.ts | 4 +- .../getTransformationsStats.schema.ts | 4 +- .../(wrapped)/console/etablissements/page.tsx | 47 +++++++-------- ui/app/(wrapped)/console/formations/page.tsx | 57 +++++++++---------- ui/app/(wrapped)/console/formations/types.ts | 9 ++- .../pilotage/components/CadranSection.tsx | 26 +++++---- ui/app/(wrapped)/intentions/pilotage/types.ts | 24 +++----- .../HeaderSection/CountersSection.tsx | 5 +- .../restitution/STATS_DEMANDES_COLUMN.ts | 5 +- .../(wrapped)/intentions/restitution/page.tsx | 10 ++-- .../(wrapped)/intentions/restitution/types.ts | 25 ++++---- .../departement/PanoramaSelection.tsx | 6 +- .../departement/[codeDepartement]/page.tsx | 3 +- .../(wrapped)/panorama/departement/page.tsx | 3 +- .../etablissement/[uai]/CadranSection.tsx | 8 +-- .../[uai]/EtablissementSection.tsx | 6 +- .../[uai]/FormationTooltipContent.tsx | 5 +- .../etablissement/[uai]/FormationsSection.tsx | 5 +- .../etablissement/[uai]/RegionSection.tsx | 5 +- .../panorama/etablissement/[uai]/page.tsx | 11 ++-- .../panorama/region/PanoramaSelection.tsx | 5 +- .../panorama/region/[codeRegion]/page.tsx | 23 ++++---- ui/app/(wrapped)/panorama/region/page.tsx | 4 +- ui/app/(wrapped)/panorama/types.ts | 32 +++++------ ui/app/(wrapped)/pilotage-reforme/page.tsx | 50 +++++++++------- ui/app/(wrapped)/pilotage-reforme/types.ts | 22 +++---- ui/app/layoutClient.tsx | 2 - 31 files changed, 198 insertions(+), 234 deletions(-) diff --git a/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts b/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts index 8e7a06f0a..dfc05b87d 100644 --- a/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts +++ b/server/src/modules/data/usecases/getEtablissements/getEtablissements.schema.ts @@ -1,8 +1,8 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); const EtablissementLineSchema = z.object({ diff --git a/server/src/modules/data/usecases/getFormations/getFormation.schema.ts b/server/src/modules/data/usecases/getFormations/getFormation.schema.ts index 93456cdc6..95c07da65 100644 --- a/server/src/modules/data/usecases/getFormations/getFormation.schema.ts +++ b/server/src/modules/data/usecases/getFormations/getFormation.schema.ts @@ -1,8 +1,8 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); export const FormationLineSchema = z.object({ diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts index 18421ae9a..3676f3775 100644 --- a/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStats/getPilotageReformeStats.schema.ts @@ -1,16 +1,16 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); const StatsSchema = z.object({ - effectif: z.number().optional(), - nbFormations: z.number().optional(), - nbEtablissements: z.number().optional(), - poursuite: z.number().optional(), - insertion: z.number().optional(), + effectif: z.coerce.number().optional(), + nbFormations: z.coerce.number().optional(), + nbEtablissements: z.coerce.number().optional(), + poursuite: z.coerce.number().optional(), + insertion: z.coerce.number().optional(), }); const StatsAnneeSchema = z.object({ diff --git a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts index c167ae5a4..27321c6b6 100644 --- a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.schema.ts @@ -1,8 +1,8 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); const StatsRegionLineSchema = z.object({ codeRegion: z.string(), diff --git a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts index 28778d62f..d09d62e9e 100644 --- a/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts +++ b/server/src/modules/data/usecases/getRestitutionIntentionsStats/getRestitutionIntentionStats.schema.ts @@ -1,8 +1,8 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); const StatsDemandesItem = z.object({ diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts index f3709f3b1..65e1b7c02 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationsStats.schema.ts @@ -1,8 +1,8 @@ import { z } from "zod"; const OptionSchema = z.object({ - label: z.string(), - value: z.string(), + label: z.coerce.string(), + value: z.coerce.string(), }); const ScopedStatsTransfoSchema = z.object({ diff --git a/ui/app/(wrapped)/console/etablissements/page.tsx b/ui/app/(wrapped)/console/etablissements/page.tsx index 5a546f58e..7c69efca5 100644 --- a/ui/app/(wrapped)/console/etablissements/page.tsx +++ b/ui/app/(wrapped)/console/etablissements/page.tsx @@ -19,9 +19,8 @@ import { usePlausible } from "next-plausible"; import qs from "qs"; import { Fragment, useContext, useEffect, useState } from "react"; import { unstable_batchedUpdates } from "react-dom"; -import { ApiType } from "shared"; -import { api, client } from "@/api.client"; +import { client } from "@/api.client"; import { OrderIcon } from "@/components/OrderIcon"; import { TableFooter } from "@/components/TableFooter"; import { createParametrizedUrl } from "@/utils/createParametrizedUrl"; @@ -70,14 +69,13 @@ const ETABLISSEMENTS_COLUMNS = { "continuum.libelle": "Diplôme historique", "continuum.cfd": "Code diplôme historique", } satisfies ExportColumns< - ApiType["etablissements"][number] + (typeof client.infer)["[GET]/etablissements"]["etablissements"][number] >; -type Query = Parameters[0]["query"]; +type Query = (typeof client.inferArgs)["[GET]/etablissements"]["query"]; -export type Line = Awaited< - ReturnType["call"]> ->["etablissements"][number]; +export type Line = + (typeof client.infer)["[GET]/etablissements"]["etablissements"][number]; type Filters = Pick< Query, @@ -107,9 +105,6 @@ type LineId = { const PAGE_SIZE = 30; -const fetchEtablissements = async (query: Query) => - api.getEtablissements({ query }).call(); - export default function Etablissements() { const router = useRouter(); const queryParams = useSearchParams(); @@ -213,17 +208,19 @@ export default function Etablissements() { queryFn: async () => { if (!historiqueId) return; return ( - await fetchEtablissements({ - ...filters, - cfd: [historiqueId?.cfd], - codeDispositif: historiqueId?.codeDispositif - ? [historiqueId?.codeDispositif] - : undefined, - uai: [historiqueId.UAI], - limit: 2, - order: "desc", - orderBy: "rentreeScolaire", - rentreeScolaire: ["2021", "2020"], + await client.ref("[GET]/etablissements").query({ + query: { + ...filters, + cfd: [historiqueId?.cfd], + codeDispositif: historiqueId?.codeDispositif + ? [historiqueId?.codeDispositif] + : undefined, + uai: [historiqueId.UAI], + limit: 2, + order: "desc", + orderBy: "rentreeScolaire", + rentreeScolaire: ["2021", "2020"], + }, }) ).etablissements; }, @@ -654,11 +651,9 @@ export default function Etablissements() { { - const data = await api - .getEtablissements({ - query: { ...filters, ...order, limit: 10000000 }, - }) - .call(); + const data = await client.ref("[GET]/etablissements").query({ + query: { ...filters, ...order, limit: 10000000 }, + }); trackEvent("etablissements:export"); downloadCsv( "etablissement_export.csv", diff --git a/ui/app/(wrapped)/console/formations/page.tsx b/ui/app/(wrapped)/console/formations/page.tsx index 2ca94ed29..f28733ddd 100644 --- a/ui/app/(wrapped)/console/formations/page.tsx +++ b/ui/app/(wrapped)/console/formations/page.tsx @@ -18,9 +18,8 @@ import { useRouter, useSearchParams } from "next/navigation"; import { usePlausible } from "next-plausible"; import qs from "qs"; import { Fragment, useContext, useEffect, useState } from "react"; -import { ApiType } from "shared"; -import { api } from "@/api.client"; +import { client } from "@/api.client"; import { TauxPressionScale } from "@/app/(wrapped)/components/TauxPressionScale"; import { TableFooter } from "@/components/TableFooter"; import { TooltipIcon } from "@/components/TooltipIcon"; @@ -35,7 +34,7 @@ import { FormationLineLoader, FormationLinePlaceholder, } from "./components/LineContent"; -import { Filters, LineId, Order, Query } from "./types"; +import { Filters, LineId, Order } from "./types"; const PAGE_SIZE = 30; @@ -63,12 +62,9 @@ const FORMATIONS_COLUMNS = { "continuum.cfd": "Code diplôme historique", positionCadran: "Position dans le cadran", } satisfies ExportColumns< - ApiType["formations"][number] + (typeof client.infer)["[GET]/formations"]["formations"][number] >; -const fetchFormations = async (query: Query) => - api.getFormations({ query }).call(); - export default function Formations() { const router = useRouter(); const queryParams = useSearchParams(); @@ -103,18 +99,17 @@ export default function Formations() { } }, []); - const { data, isFetching } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["formations", filters, order, page], - queryFn: () => - fetchFormations({ + const { data, isFetching } = client.ref("[GET]/formations").useQuery( + { + query: { ...order, offset: page * PAGE_SIZE, limit: PAGE_SIZE, ...filters, - }), - }); + }, + }, + { keepPreviousData: true, staleTime: 10000000 } + ); const trackEvent = usePlausible(); @@ -165,17 +160,19 @@ export default function Formations() { queryFn: async () => { if (!historiqueId) return; return ( - await fetchFormations({ - ...filters, - cfd: [historiqueId?.cfd], - codeDispositif: historiqueId?.codeDispositif - ? [historiqueId?.codeDispositif] - : undefined, - limit: 2, - order: "desc", - orderBy: "rentreeScolaire", - rentreeScolaire: ["2021", "2020"], - withEmptyFormations: false, + await client.ref("[GET]/formations").query({ + query: { + ...filters, + cfd: [historiqueId?.cfd], + codeDispositif: historiqueId?.codeDispositif + ? [historiqueId?.codeDispositif] + : undefined, + limit: 2, + order: "desc", + orderBy: "rentreeScolaire", + rentreeScolaire: ["2021", "2020"], + withEmptyFormations: false, + }, }) ).formations; }, @@ -550,11 +547,9 @@ export default function Formations() { { trackEvent("formations:export"); - const data = await api - .getFormations({ - query: { ...filters, ...order, limit: 1000000 }, - }) - .call(); + const data = await client + .ref("[GET]/formations") + .query({ query: { ...filters, ...order, limit: 1000000 } }); downloadCsv( "formations_export.csv", data.formations, diff --git a/ui/app/(wrapped)/console/formations/types.ts b/ui/app/(wrapped)/console/formations/types.ts index 21903503c..f9c8f4e6c 100644 --- a/ui/app/(wrapped)/console/formations/types.ts +++ b/ui/app/(wrapped)/console/formations/types.ts @@ -1,7 +1,5 @@ -import { ApiType } from "shared"; - -import { api } from "@/api.client"; -export type Query = Parameters[0]["query"]; +import { client } from "@/api.client"; +export type Query = (typeof client.inferArgs)["[GET]/formations"]["query"]; export type Filters = Pick< Query, @@ -21,7 +19,8 @@ export type Filters = Pick< export type Order = Pick; -export type Line = ApiType["formations"][number]; +export type Line = + (typeof client.infer)["[GET]/formations"]["formations"][number]; export type LineId = { codeDispositif?: string; diff --git a/ui/app/(wrapped)/intentions/pilotage/components/CadranSection.tsx b/ui/app/(wrapped)/intentions/pilotage/components/CadranSection.tsx index eb82081af..06fc89c6b 100644 --- a/ui/app/(wrapped)/intentions/pilotage/components/CadranSection.tsx +++ b/ui/app/(wrapped)/intentions/pilotage/components/CadranSection.tsx @@ -17,7 +17,6 @@ import { Stack, Text, } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import _ from "lodash"; import NextLink from "next/link"; import { usePlausible } from "next-plausible"; @@ -26,7 +25,7 @@ import { useMemo, useState } from "react"; import { GraphWrapper } from "@/components/GraphWrapper"; import { InfoBlock } from "@/components/InfoBlock"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; import { Cadran } from "../../../../../components/Cadran"; import { TableCadran } from "../../../../../components/TableCadran"; import { createParametrizedUrl } from "../../../../../utils/createParametrizedUrl"; @@ -88,17 +87,20 @@ export const CadranSection = ({ codeDepartement: scope?.type === "departements" ? scope.value : undefined, }; - const { data: { formations, stats } = {} } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["getformationsTransformationStats", mergedFilters, order], - queryFn: api.getFormationsTransformationStats({ - query: { - ...mergedFilters, - ...order, + const { data: { formations, stats } = {} } = client + .ref("[GET]/pilotage-transformation/formations") + .useQuery( + { + query: { + ...mergedFilters, + ...order, + }, }, - }).call, - }); + { + keepPreviousData: true, + staleTime: 10000000, + } + ); const formation = useMemo( () => formations?.find((item) => item.cfd === currentCfd), diff --git a/ui/app/(wrapped)/intentions/pilotage/types.ts b/ui/app/(wrapped)/intentions/pilotage/types.ts index a4c667289..4b70a7e82 100644 --- a/ui/app/(wrapped)/intentions/pilotage/types.ts +++ b/ui/app/(wrapped)/intentions/pilotage/types.ts @@ -1,14 +1,10 @@ -import { ApiType } from "shared"; +import { client } from "@/api.client"; -import { api } from "../../../../api.client"; +export type PilotageTransformationStatsQuery = + (typeof client.inferArgs)["[GET]/pilotage-transformation/stats"]["query"]; -export type PilotageTransformationStatsQuery = Parameters< - typeof api.getTransformationStats ->[0]["query"]; - -export type PilotageTransformationStats = ApiType< - typeof api.getTransformationStats ->; +export type PilotageTransformationStats = + (typeof client.infer)["[GET]/pilotage-transformation/stats"]; export type Filters = Pick< PilotageTransformationStatsQuery, "rentreeScolaire" | "filiere" | "codeNiveauDiplome" @@ -26,13 +22,11 @@ export type TerritoiresFilters = { departements?: string; }; -export type FormationsTransformationStatsQuery = Parameters< - typeof api.getFormationsTransformationStats ->[0]["query"]; +export type FormationsTransformationStatsQuery = + (typeof client.inferArgs)["[GET]/pilotage-transformation/formations"]["query"]; -export type FormationsTransformationStats = ApiType< - typeof api.getFormationsTransformationStats ->; +export type FormationsTransformationStats = + (typeof client.infer)["[GET]/pilotage-transformation/formations"]; export type OrderFormationsTransformationStats = Pick< FormationsTransformationStatsQuery, diff --git a/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx b/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx index de641c51b..9a7d31cc3 100644 --- a/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx @@ -7,9 +7,8 @@ import { Img, Text, } from "@chakra-ui/react"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; const CountCard = ({ label, @@ -83,7 +82,7 @@ const CountCard = ({ export const CountersSection = ({ countData, }: { - countData?: ApiType; + countData?: (typeof client.infer)["[GET]/intentions/stats/count"]; }) => { return ( <> diff --git a/ui/app/(wrapped)/intentions/restitution/STATS_DEMANDES_COLUMN.ts b/ui/app/(wrapped)/intentions/restitution/STATS_DEMANDES_COLUMN.ts index 89a61eb31..c313a0f88 100644 --- a/ui/app/(wrapped)/intentions/restitution/STATS_DEMANDES_COLUMN.ts +++ b/ui/app/(wrapped)/intentions/restitution/STATS_DEMANDES_COLUMN.ts @@ -1,6 +1,5 @@ -import { ApiType } from "shared"; +import { client } from "@/api.client"; -import { api } from "../../../../api.client"; import { ExportColumns } from "../../../../utils/downloadCsv"; export const STATS_DEMANDES_COLUMNS = { @@ -48,5 +47,5 @@ export const STATS_DEMANDES_COLUMNS = { pression: "Tx de pression régional", nbEtablissement: "Nb établissement", } satisfies ExportColumns< - ApiType["demandes"][number] + (typeof client.infer)["[GET]/intentions/stats"]["demandes"][number] >; diff --git a/ui/app/(wrapped)/intentions/restitution/page.tsx b/ui/app/(wrapped)/intentions/restitution/page.tsx index 78bc938c1..9da69ff82 100644 --- a/ui/app/(wrapped)/intentions/restitution/page.tsx +++ b/ui/app/(wrapped)/intentions/restitution/page.tsx @@ -6,9 +6,9 @@ import { usePlausible } from "next-plausible"; import qs from "qs"; import { useContext, useEffect, useState } from "react"; +import { client } from "@/api.client"; import { GuardPermission } from "@/utils/security/GuardPermission"; -import { api, client } from "../../../../api.client"; import { TableFooter } from "../../../../components/TableFooter"; import { createParametrizedUrl } from "../../../../utils/createParametrizedUrl"; import { downloadCsv } from "../../../../utils/downloadCsv"; @@ -174,11 +174,9 @@ export default () => { pl="4" onExport={async () => { trackEvent("restitution-demandes:export"); - const data = await api - .getRestitutionIntentionsStats({ - query: { ...filters, ...order, limit: 10000000 }, - }) - .call(); + const data = await client.ref("[GET]/intentions/stats").query({ + query: { ...filters, ...order, limit: 10000000 }, + }); downloadCsv( "demandes_stats_export.csv", data.demandes.map((demande) => ({ diff --git a/ui/app/(wrapped)/intentions/restitution/types.ts b/ui/app/(wrapped)/intentions/restitution/types.ts index 51fb147fd..ec1c1fdfd 100644 --- a/ui/app/(wrapped)/intentions/restitution/types.ts +++ b/ui/app/(wrapped)/intentions/restitution/types.ts @@ -1,13 +1,10 @@ -import { ApiType } from "shared"; +import { client } from "../../../../api.client"; -import { api } from "../../../../api.client"; - -export type StatsDemandesQuery = Parameters< - typeof api.getRestitutionIntentionsStats ->[0]["query"]; +export type StatsIntentionsQuery = + (typeof client.inferArgs)["[GET]/intentions/stats"]["query"]; export type Filters = Pick< - StatsDemandesQuery, + StatsIntentionsQuery, | "codeRegion" | "codeAcademie" | "codeDepartement" @@ -28,16 +25,14 @@ export type Filters = Pick< | "compensation" >; -export type Order = Pick; +export type Order = Pick; -export type StatsDemandes = ApiType; +export type StatsIntentions = (typeof client.infer)["[GET]/intentions/stats"]; export type IndicateurType = "insertion" | "poursuite"; -export type CountStatsDemandesQuery = Parameters< - typeof api.countRestitutionIntentionsStats ->[0]["query"]; +export type CountStatsIntentionsQuery = + (typeof client.inferArgs)["[GET]/intentions/stats/count"]["query"]; -export type CountStatsDemandes = ApiType< - typeof api.countRestitutionIntentionsStats ->; +export type CountStatsIntentions = + (typeof client.infer)["[GET]/intentions/stats/count"]; diff --git a/ui/app/(wrapped)/panorama/departement/PanoramaSelection.tsx b/ui/app/(wrapped)/panorama/departement/PanoramaSelection.tsx index b66ba121f..2ba7572b7 100644 --- a/ui/app/(wrapped)/panorama/departement/PanoramaSelection.tsx +++ b/ui/app/(wrapped)/panorama/departement/PanoramaSelection.tsx @@ -11,15 +11,15 @@ import { } from "@chakra-ui/react"; import { useRouter } from "next/navigation"; import { useContext, useEffect } from "react"; -import { ApiType } from "shared"; -import { api } from "../../../../api.client"; +import { client } from "@/api.client"; + import { CodeDepartementFilterContext } from "../../../layoutClient"; export function PanoramaSelection({ departementsOptions, }: { - departementsOptions: ApiType; + departementsOptions: (typeof client.infer)["[GET]/departements"]; }) { const router = useRouter(); const { codeDepartementFilter, setCodeDepartementFilter } = useContext( diff --git a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx index 00da8fb7a..13f6cfe71 100644 --- a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx +++ b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx @@ -4,7 +4,8 @@ import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { api, client } from "../../../../../api.client"; +import { api, client } from "@/api.client"; + import { CadranSection } from "../../components/CadranSection"; import { FiltersSection } from "../../components/FiltersSection"; import { IndicateursSection } from "../../components/IndicateursSection"; diff --git a/ui/app/(wrapped)/panorama/departement/page.tsx b/ui/app/(wrapped)/panorama/departement/page.tsx index 98c01f3c4..654a7c651 100644 --- a/ui/app/(wrapped)/panorama/departement/page.tsx +++ b/ui/app/(wrapped)/panorama/departement/page.tsx @@ -1,4 +1,5 @@ -import { client } from "../../../../api.client"; +import { client } from "@/api.client"; + import { PanoramaSelection } from "./PanoramaSelection"; export const revalidate = 0; diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/CadranSection.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/CadranSection.tsx index 7ebe4b603..c6bdae5ad 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/CadranSection.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/CadranSection.tsx @@ -11,9 +11,9 @@ import { VStack, } from "@chakra-ui/react"; import { useMemo, useState } from "react"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "@/api.client"; + import { Cadran } from "../../../../../components/Cadran"; import { TableCadran } from "../../../../../components/TableCadran"; import { FormationTooltipContent } from "./FormationTooltipContent"; @@ -34,7 +34,7 @@ export const CadranSection = ({ codeNiveauDiplome, rentreeScolaire, }: { - cadranFormations?: ApiType["formations"]; + cadranFormations?: (typeof client.infer)["[GET]/etablissement/:uai"]["formations"]; meanPoursuite?: number; meanInsertion?: number; codeNiveauDiplome?: string[]; @@ -52,7 +52,7 @@ export const CadranSection = ({ ( item ): item is RequiredFields< - ApiType["formations"][number], + (typeof client.infer)["[GET]/etablissement/:uai"]["formations"][number], "tauxInsertion6mois" | "tauxPoursuiteEtudes" > => item.tauxInsertion6mois !== undefined && diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/EtablissementSection.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/EtablissementSection.tsx index dd8d71cfe..5d2139770 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/EtablissementSection.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/EtablissementSection.tsx @@ -11,9 +11,9 @@ import { Stack, Text, } from "@chakra-ui/react"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "@/api.client"; + import { TooltipIcon } from "../../../../../components/TooltipIcon"; import { UaiForm } from "../UaiForm"; @@ -62,7 +62,7 @@ export const EtablissementSection = ({ onUaiChanged, }: { uai: string; - etablissement?: ApiType; + etablissement?: (typeof client.infer)["[GET]/etablissement/:uai"]; onUaiChanged: (codeRegion: string) => void; }) => { return ( diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/FormationTooltipContent.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/FormationTooltipContent.tsx index e56a9215d..c8439e3c9 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/FormationTooltipContent.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/FormationTooltipContent.tsx @@ -1,16 +1,15 @@ "use client"; import { Box, Text } from "@chakra-ui/react"; -import { ApiType } from "shared"; +import { client } from "@/api.client"; import { GraphWrapper } from "@/components/GraphWrapper"; -import { api } from "../../../../../api.client"; import { InfoBlock } from "../../../../../components/InfoBlock"; export const FormationTooltipContent = ({ formation, }: { - formation: ApiType["formations"][number]; + formation: (typeof client.infer)["[GET]/etablissement/:uai"]["formations"][number]; }) => ( ["formations"]; + formations?: (typeof client.infer)["[GET]/etablissement/:uai"]["formations"]; rentreeScolaire?: string; }) => { return ( diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/RegionSection.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/RegionSection.tsx index 5cddab991..2caf5c68f 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/RegionSection.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/RegionSection.tsx @@ -1,12 +1,11 @@ import { Box, Container, Heading, HStack, Text } from "@chakra-ui/react"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "@/api.client"; export const RegionSection = ({ regionsStats, }: { - regionsStats?: ApiType; + regionsStats?: (typeof client.infer)["[GET]/region/:codeRegion"]; }) => { const formatedLibelleRegion = (libelleRegion: string): string => { const voyelles = "aeiiîouAEIÎOU"; diff --git a/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx b/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx index e74e96ccd..ff7e82236 100644 --- a/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx +++ b/ui/app/(wrapped)/panorama/etablissement/[uai]/page.tsx @@ -1,10 +1,10 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useContext, useState } from "react"; -import { api, client } from "../../../../../api.client"; +import { client } from "@/api.client"; + import { UaiFilterContext } from "../../../../layoutClient"; import { InfoSection } from "../../components/InfoSection"; import { PanoramaSelection } from "../PanoramaSelection"; @@ -37,12 +37,11 @@ export default function Panorama({ { keepPreviousData: true, staleTime: 10000000, retry: false } ); - const { data: regionStats } = useQuery( - ["getRegionStats", { codeRegion: etablissement?.codeRegion }], - api.getRegionStats({ + const { data: regionStats } = client.ref("[GET]/region/:codeRegion").useQuery( + { params: { codeRegion: etablissement?.codeRegion as string }, query: {}, - }).call, + }, { keepPreviousData: true, staleTime: 10000000, enabled: !!etablissement } ); diff --git a/ui/app/(wrapped)/panorama/region/PanoramaSelection.tsx b/ui/app/(wrapped)/panorama/region/PanoramaSelection.tsx index f5f77e328..9c89f7535 100644 --- a/ui/app/(wrapped)/panorama/region/PanoramaSelection.tsx +++ b/ui/app/(wrapped)/panorama/region/PanoramaSelection.tsx @@ -11,15 +11,14 @@ import { } from "@chakra-ui/react"; import { useRouter } from "next/navigation"; import { useContext, useEffect } from "react"; -import { ApiType } from "shared"; -import { api } from "../../../../api.client"; +import { client } from "../../../../api.client"; import { CodeRegionFilterContext } from "../../../layoutClient"; export function PanoramaSelection({ regionOptions, }: { - regionOptions: ApiType; + regionOptions: (typeof client.infer)["[GET]/regions"]; }) { const router = useRouter(); const { codeRegionFilter, setCodeRegionFilter } = useContext( diff --git a/ui/app/(wrapped)/panorama/region/[codeRegion]/page.tsx b/ui/app/(wrapped)/panorama/region/[codeRegion]/page.tsx index 990a2d8ed..b643fed27 100644 --- a/ui/app/(wrapped)/panorama/region/[codeRegion]/page.tsx +++ b/ui/app/(wrapped)/panorama/region/[codeRegion]/page.tsx @@ -1,10 +1,10 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { api } from "../../../../../api.client"; +import { client } from "@/api.client"; + import { CadranSection } from "../../components/CadranSection"; import { FiltersSection } from "../../components/FiltersSection"; import { IndicateursSection } from "../../components/IndicateursSection"; @@ -26,32 +26,29 @@ export default function Panorama({ const [codeNiveauDiplome, setCodeNiveauDiplome] = useState(); const [libelleFiliere, setLibelleFiliere] = useState(); - const { data: regionOptions } = useQuery( - ["regions"], - api.getRegions({}).call, + const { data: regionOptions } = client.ref("[GET]/regions").useQuery( + {}, { keepPreviousData: true, staleTime: 10000000, } ); - const { data: stats } = useQuery( - ["region", codeRegion, codeNiveauDiplome], - api.getRegionStats({ + const { data: stats } = client.ref("[GET]/region/:codeRegion").useQuery( + { params: { codeRegion }, query: { codeDiplome: codeNiveauDiplome }, - }).call, + }, { keepPreviousData: true, staleTime: 10000000, } ); - const { data } = useQuery( - ["formationForPanorama", { codeRegion }], - api.getDataForPanoramaRegion({ + const { data } = client.ref("[GET]/panorama/stats/region").useQuery( + { query: { codeRegion }, - }).call, + }, { keepPreviousData: true, staleTime: 10000000 } ); diff --git a/ui/app/(wrapped)/panorama/region/page.tsx b/ui/app/(wrapped)/panorama/region/page.tsx index 59c28ed21..7cad4c7eb 100644 --- a/ui/app/(wrapped)/panorama/region/page.tsx +++ b/ui/app/(wrapped)/panorama/region/page.tsx @@ -1,9 +1,9 @@ -import { serverApi } from "@/api.client"; +import { client } from "@/api.client"; import { PanoramaSelection } from "@/app/(wrapped)/panorama/region/PanoramaSelection"; export const revalidate = 0; export default async function Panorama() { - const regionOptions = await serverApi.getRegions({}).call(); + const regionOptions = await client.ref("[GET]/regions").query({}); return ; } diff --git a/ui/app/(wrapped)/panorama/types.ts b/ui/app/(wrapped)/panorama/types.ts index d36015366..6deea267d 100644 --- a/ui/app/(wrapped)/panorama/types.ts +++ b/ui/app/(wrapped)/panorama/types.ts @@ -1,20 +1,14 @@ -import { ApiType } from "shared"; +import { client } from "@/api.client"; -import { api } from "@/api.client"; +export type PanoramaFormationRegion = + (typeof client.infer)["[GET]/panorama/stats/region"]["formations"][number]; +export type PanoramaFormationsRegion = + (typeof client.infer)["[GET]/panorama/stats/region"]["formations"]; -export type PanoramaFormationRegion = ApiType< - typeof api.getDataForPanoramaRegion ->["formations"][number]; -export type PanoramaFormationsRegion = ApiType< - typeof api.getDataForPanoramaRegion ->["formations"]; - -export type PanoramaFormationDepartement = ApiType< - typeof api.getDataForPanoramaDepartement ->["formations"][number]; -export type PanoramaFormationsDepartement = ApiType< - typeof api.getDataForPanoramaDepartement ->["formations"]; +export type PanoramaFormationDepartement = + (typeof client.infer)["[GET]/panorama/stats/departement"]["formations"][number]; +export type PanoramaFormationsDepartement = + (typeof client.infer)["[GET]/panorama/stats/departement"]["formations"]; export type PanoramaFormation = | PanoramaFormationRegion @@ -23,10 +17,10 @@ export type PanoramaFormations = | PanoramaFormationsRegion | PanoramaFormationsDepartement; -export type StatsFormationsRegion = ApiType; -export type StatsFormationsDepartement = ApiType< - typeof api.getDepartementStats ->; +export type StatsFormationsRegion = + (typeof client.infer)["[GET]/region/:codeRegion"]; +export type StatsFormationsDepartement = + (typeof client.infer)["[GET]/departement/:codeDepartement"]; export type StatsFormations = | StatsFormationsRegion | StatsFormationsDepartement; diff --git a/ui/app/(wrapped)/pilotage-reforme/page.tsx b/ui/app/(wrapped)/pilotage-reforme/page.tsx index deba7f412..d0faf4726 100644 --- a/ui/app/(wrapped)/pilotage-reforme/page.tsx +++ b/ui/app/(wrapped)/pilotage-reforme/page.tsx @@ -1,13 +1,13 @@ "use client"; import { Box, Container, SimpleGrid } from "@chakra-ui/react"; -import { useQuery } from "@tanstack/react-query"; import { useRouter, useSearchParams } from "next/navigation"; import { usePlausible } from "next-plausible"; import qs from "qs"; import { useState } from "react"; -import { api } from "../../../api.client"; +import { client } from "@/api.client"; + import { createParametrizedUrl } from "../../../utils/createParametrizedUrl"; import { withAuth } from "../../../utils/security/withAuth"; import { CartoSection } from "./components/CartoSection"; @@ -68,28 +68,34 @@ export default withAuth("pilotage_reforme/lecture", function PilotageReforme() { }); }; - const { data, isLoading: isLoading } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["pilotageReforme", filters], - queryFn: api.getPilotageReformeStats({ - query: { - ...filters, + const { data, isLoading: isLoading } = client + .ref("[GET]/pilotage-reforme/stats") + .useQuery( + { + query: { + ...filters, + }, }, - }).call, - }); - - const { data: dataRegions, isLoading: isLoadingRegions } = useQuery({ - keepPreviousData: true, - staleTime: 10000000, - queryKey: ["pilotageReformeRegions", filters, order], - queryFn: api.getPilotageReformeStatsRegions({ - query: { - ...filters, - ...order, + { + keepPreviousData: true, + staleTime: 10000000, + } + ); + + const { data: dataRegions, isLoading: isLoadingRegions } = client + .ref("[GET]/pilotage-reforme/stats/regions") + .useQuery( + { + query: { + ...filters, + ...order, + }, }, - }).call, - }); + { + keepPreviousData: true, + staleTime: 10000000, + } + ); const isFiltered = filters.codeRegion; diff --git a/ui/app/(wrapped)/pilotage-reforme/types.ts b/ui/app/(wrapped)/pilotage-reforme/types.ts index ef3fcca0d..ede6e1d17 100644 --- a/ui/app/(wrapped)/pilotage-reforme/types.ts +++ b/ui/app/(wrapped)/pilotage-reforme/types.ts @@ -1,14 +1,10 @@ -import { ApiType } from "shared"; +import { client } from "@/api.client"; -import { api } from "../../../api.client"; +export type PilotageReformeStatsQuery = + (typeof client.inferArgs)["[GET]/pilotage-reforme/stats"]["query"]; -export type PilotageReformeStatsQuery = Parameters< - typeof api.getPilotageReformeStats ->[0]["query"]; - -export type PilotageReformeStatsRegionsQuery = Parameters< - typeof api.getPilotageReformeStatsRegions ->[0]["query"]; +export type PilotageReformeStatsRegionsQuery = + (typeof client.inferArgs)["[GET]/pilotage-reforme/stats/regions"]["query"]; export type Filters = Pick< PilotageReformeStatsQuery, @@ -22,10 +18,10 @@ export type FiltersRegions = Pick< export type Order = Pick; -export type PilotageReformeStats = ApiType; +export type PilotageReformeStats = + (typeof client.infer)["[GET]/pilotage-reforme/stats"]; -export type PilotageReformeStatsRegion = ApiType< - typeof api.getPilotageReformeStatsRegions ->; +export type PilotageReformeStatsRegion = + (typeof client.infer)["[GET]/pilotage-reforme/stats/regions"]; export type IndicateurType = "insertion" | "poursuite"; diff --git a/ui/app/layoutClient.tsx b/ui/app/layoutClient.tsx index b17207d07..61372b778 100644 --- a/ui/app/layoutClient.tsx +++ b/ui/app/layoutClient.tsx @@ -16,7 +16,6 @@ import { useRef, useState, } from "react"; -import { z } from "zod"; import { Auth, AuthContext } from "@/app/(wrapped)/auth/authContext"; @@ -31,7 +30,6 @@ const useTracking = () => { (typeof localStorage !== "undefined" && localStorage.getItem("notracking") === "true")) ); - console.log(z); useEffect(() => { if (param === "reset") { localStorage.removeItem("notracking"); From ae74fb0fea755404a95a5e2906528ca25191b1b0 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 11:24:13 +0100 Subject: [PATCH 13/20] wip --- package.json | 2 +- server/package.json | 2 +- .../getTransformationStats.usecase.ts | 13 ++++---- shared/demandeValidators/validators.ts | 10 +++--- shared/package.json | 4 ++- yarn.lock | 33 ++++++++++++------- 6 files changed, 40 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index c562d52eb..e9839ce0b 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "prettier": "3.0.3", "semantic-release": "19.0.2", "semantic-release-slack-bot": "3.5.2", - "typescript": "5.1.6" + "typescript": "5.0.2" }, "prettier": { "printWidth": 80, diff --git a/server/package.json b/server/package.json index a1d2b4630..11ff4d0fc 100644 --- a/server/package.json +++ b/server/package.json @@ -79,6 +79,6 @@ "jest": "^29.5.0", "kysely-codegen": "^0.10.0", "ts-jest": "^29.1.0", - "typescript": "5.1.6" + "typescript": "5.0.2" } } diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts index 57f344424..91fd28999 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts @@ -173,18 +173,19 @@ const getTransformationStatsFactory = filiere?: string[]; orderBy?: { column: string; order: "asc" | "desc" }; }) => { - const resultDraft = await deps - .getTransformationStatsQuery({ - ...activeFilters, - status: "draft", - }) - .then((result) => formatResult(result, activeFilters.orderBy)); + const resultDraft = await deps.getTransformationStatsQuery({ + ...activeFilters, + status: "draft", + }); + // .then((result) => formatResult(result, activeFilters.orderBy)); + const resultSubmitted = await deps .getTransformationStatsQuery({ ...activeFilters, status: "submitted", }) .then((result) => formatResult(result, activeFilters.orderBy)); + const resultAll = await deps .getTransformationStatsQuery({ ...activeFilters, diff --git a/shared/demandeValidators/validators.ts b/shared/demandeValidators/validators.ts index dbfc05fef..0e9a683f3 100644 --- a/shared/demandeValidators/validators.ts +++ b/shared/demandeValidators/validators.ts @@ -1,8 +1,10 @@ -import { Static } from "@sinclair/typebox"; +import { Args, ZodTypeProvider } from "@http-wizard/core"; +import { Router } from "server"; -import { intentionsSchemas } from "../client/intentions/intentions.schema"; - -type Demande = Static["demande"]; +type Demande = Args< + Router["[POST]/demande/submit"]["schema"], + ZodTypeProvider +>["body"]["demande"]; export const isTypeFermeture = (typeDemande: string) => ["fermeture"].includes(typeDemande); diff --git a/shared/package.json b/shared/package.json index 8dcd0d3d2..31c5d8612 100644 --- a/shared/package.json +++ b/shared/package.json @@ -8,8 +8,10 @@ "license": "MIT", "private": true, "dependencies": { + "@http-wizard/core": "^1.3.20", "@sinclair/typebox": "^0.25.21", - "injecti": "^1.0.5" + "injecti": "^1.0.5", + "server": "*" }, "devDependencies": { "axios": "1.3.5" diff --git a/yarn.lock b/yarn.lock index 28fce50d8..6f15d90c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,6 +2456,15 @@ __metadata: languageName: node linkType: hard +"@http-wizard/core@npm:^1.3.20": + version: 1.3.20 + resolution: "@http-wizard/core@npm:1.3.20" + peerDependencies: + axios: ^1.4.0 + checksum: 771a65d829b70247b824847ac882415bf77f783ca1da1b98c899f892e3120e74fff788d557b989b044b2db1e1e44f53bf9c631de9d0f98770d7c6d68cd448c6f + languageName: node + linkType: hard + "@http-wizard/react-query@npm:^1.3.16": version: 1.3.16 resolution: "@http-wizard/react-query@npm:1.3.16" @@ -16101,7 +16110,7 @@ __metadata: ts-jest: ^29.1.0 ts-node: ^10.9.1 tsup: ^6.5.0 - typescript: 5.1.6 + typescript: 5.0.2 uglify-js: ^3.17.4 uuid: ^9.0.1 winston: ^3.10.0 @@ -16165,9 +16174,11 @@ __metadata: version: 0.0.0-use.local resolution: "shared@workspace:shared" dependencies: + "@http-wizard/core": ^1.3.20 "@sinclair/typebox": ^0.25.21 axios: 1.3.5 injecti: ^1.0.5 + server: "*" languageName: unknown linkType: soft @@ -17136,7 +17147,7 @@ __metadata: prettier: 3.0.3 semantic-release: 19.0.2 semantic-release-slack-bot: 3.5.2 - typescript: 5.1.6 + typescript: 5.0.2 languageName: unknown linkType: soft @@ -17592,13 +17603,13 @@ __metadata: languageName: node linkType: hard -"typescript@npm:5.1.6": - version: 5.1.6 - resolution: "typescript@npm:5.1.6" +"typescript@npm:5.0.2": + version: 5.0.2 + resolution: "typescript@npm:5.0.2" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: b2f2c35096035fe1f5facd1e38922ccb8558996331405eb00a5111cc948b2e733163cc22fab5db46992aba7dd520fff637f2c1df4996ff0e134e77d3249a7350 + checksum: bef1dcd166acfc6934b2ec4d72f93edb8961a5fab36b8dd2aaf6f4f4cd5c0210f2e0850aef4724f3b4913d5aef203a94a28ded731b370880c8bcff7e4ff91fc1 languageName: node linkType: hard @@ -17612,13 +17623,13 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@5.1.6#~builtin": - version: 5.1.6 - resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=ad5954" +"typescript@patch:typescript@5.0.2#~builtin": + version: 5.0.2 + resolution: "typescript@patch:typescript@npm%3A5.0.2#~builtin::version=5.0.2&hash=ad5954" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 21e88b0a0c0226f9cb9fd25b9626fb05b4c0f3fddac521844a13e1f30beb8f14e90bd409a9ac43c812c5946d714d6e0dee12d5d02dfc1c562c5aacfa1f49b606 + checksum: bdbf3d0aac0d6cf010fbe0536753dc19f278eb4aba88140dcd25487dfe1c56ca8b33abc0dcd42078790a939b08ebc4046f3e9bb961d77d3d2c3cfa9829da4d53 languageName: node linkType: hard @@ -17672,7 +17683,7 @@ __metadata: react-select: ^5.7.4 server: "*" shared: "*" - typescript: 5.1.6 + typescript: 5.0.2 zod: ^3.22.4 languageName: unknown linkType: soft From db1abfa26bb1da71c97f2da3117d87f4edffaaa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Tue, 21 Nov 2023 11:33:10 +0100 Subject: [PATCH 14/20] fix: nommage type --- .../ConsoleSection/ConsoleSection.tsx | 24 ++++++++++--------- .../ConsoleSection/LineContent.tsx | 4 ++-- .../HeaderSection/CountersSection.tsx | 4 ++-- .../HeaderSection/HeaderSection.tsx | 6 ++--- .../HeaderSection/PrimaryFiltersSection.tsx | 4 ++-- .../HeaderSection/SecondaryFiltersSection.tsx | 4 ++-- 6 files changed, 24 insertions(+), 22 deletions(-) diff --git a/ui/app/(wrapped)/intentions/restitution/ConsoleSection/ConsoleSection.tsx b/ui/app/(wrapped)/intentions/restitution/ConsoleSection/ConsoleSection.tsx index 03291f9cd..75a95e697 100644 --- a/ui/app/(wrapped)/intentions/restitution/ConsoleSection/ConsoleSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/ConsoleSection/ConsoleSection.tsx @@ -16,7 +16,7 @@ import { OrderIcon } from "../../../../../components/OrderIcon"; import { TooltipIcon } from "../../../../../components/TooltipIcon"; import { TauxPressionScale } from "../../../components/TauxPressionScale"; import { STATS_DEMANDES_COLUMNS } from "../STATS_DEMANDES_COLUMN"; -import { Order, StatsDemandes } from "../types"; +import { Order, StatsIntentions } from "../types"; import { LineContent } from "./LineContent"; const Loader = () => ( @@ -62,7 +62,7 @@ export const ConsoleSection = ({ order, handleOrder, }: { - data?: StatsDemandes; + data?: StatsIntentions; isLoading: boolean; order: Order; handleOrder: (column: Order["orderBy"]) => void; @@ -316,15 +316,17 @@ export const ConsoleSection = ({ - {data?.demandes.map((demande: StatsDemandes["demandes"][0]) => { - return ( - - - - - - ); - })} + {data?.demandes.map( + (demande: StatsIntentions["demandes"][0]) => { + return ( + + + + + + ); + } + )} diff --git a/ui/app/(wrapped)/intentions/restitution/ConsoleSection/LineContent.tsx b/ui/app/(wrapped)/intentions/restitution/ConsoleSection/LineContent.tsx index 1309abb7d..46ab95e07 100644 --- a/ui/app/(wrapped)/intentions/restitution/ConsoleSection/LineContent.tsx +++ b/ui/app/(wrapped)/intentions/restitution/ConsoleSection/LineContent.tsx @@ -5,11 +5,11 @@ import { TableBadge } from "../../../../../components/TableBadge"; import { getTauxPressionStyle } from "../../../../../utils/getBgScale"; import { getMotifLabel, MotifLabel } from "../../../utils/motifDemandeUtils"; import { getTypeDemandeLabel } from "../../../utils/typeDemandeUtils"; -import { StatsDemandes } from "../types"; +import { StatsIntentions } from "../types"; export const LineContent = ({ demande, }: { - demande: StatsDemandes["demandes"][0]; + demande: StatsIntentions["demandes"][0]; }) => { const handleMotifLabel = (motif?: string[], autreMotif?: string) => { return motif ? ( diff --git a/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx b/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx index 9a7d31cc3..f58f268b9 100644 --- a/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/HeaderSection/CountersSection.tsx @@ -8,7 +8,7 @@ import { Text, } from "@chakra-ui/react"; -import { client } from "../../../../../api.client"; +import { CountStatsIntentions } from "@/app/(wrapped)/intentions/restitution/types"; const CountCard = ({ label, @@ -82,7 +82,7 @@ const CountCard = ({ export const CountersSection = ({ countData, }: { - countData?: (typeof client.infer)["[GET]/intentions/stats/count"]; + countData?: CountStatsIntentions; }) => { return ( <> diff --git a/ui/app/(wrapped)/intentions/restitution/HeaderSection/HeaderSection.tsx b/ui/app/(wrapped)/intentions/restitution/HeaderSection/HeaderSection.tsx index b28cde9fc..dc2c9c5ec 100644 --- a/ui/app/(wrapped)/intentions/restitution/HeaderSection/HeaderSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/HeaderSection/HeaderSection.tsx @@ -1,6 +1,6 @@ import { Flex, Skeleton } from "@chakra-ui/react"; -import { CountStatsDemandes, Filters, StatsDemandes } from "../types"; +import { CountStatsIntentions, Filters, StatsIntentions } from "../types"; import { CountersSection } from "./CountersSection"; import { PrimaryFiltersSection } from "./PrimaryFiltersSection"; import { SecondaryFiltersSection } from "./SecondaryFiltersSection"; @@ -46,8 +46,8 @@ export const HeaderSection = ({ handleFilters: (type: keyof Filters, value: Filters[keyof Filters]) => void; filterTracker: (filterName: keyof Filters) => () => void; isLoading: boolean; - data?: StatsDemandes; - countData?: CountStatsDemandes; + data?: StatsIntentions; + countData?: CountStatsIntentions; }) => ( <> {isLoading ? ( diff --git a/ui/app/(wrapped)/intentions/restitution/HeaderSection/PrimaryFiltersSection.tsx b/ui/app/(wrapped)/intentions/restitution/HeaderSection/PrimaryFiltersSection.tsx index 36e5ce630..49241f8ef 100644 --- a/ui/app/(wrapped)/intentions/restitution/HeaderSection/PrimaryFiltersSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/HeaderSection/PrimaryFiltersSection.tsx @@ -8,7 +8,7 @@ import { } from "@chakra-ui/react"; import { Multiselect } from "../../../../../components/Multiselect"; -import { Filters, StatsDemandes } from "../types"; +import { Filters, StatsIntentions } from "../types"; export const PrimaryFiltersSection = ({ activeFilters, @@ -21,7 +21,7 @@ export const PrimaryFiltersSection = ({ handleFilters: (type: keyof Filters, value: Filters[keyof Filters]) => void; filterTracker: (filterName: keyof Filters) => () => void; isLoading: boolean; - data?: StatsDemandes; + data?: StatsIntentions; }) => { return ( <> diff --git a/ui/app/(wrapped)/intentions/restitution/HeaderSection/SecondaryFiltersSection.tsx b/ui/app/(wrapped)/intentions/restitution/HeaderSection/SecondaryFiltersSection.tsx index 99dea5869..1d4885464 100644 --- a/ui/app/(wrapped)/intentions/restitution/HeaderSection/SecondaryFiltersSection.tsx +++ b/ui/app/(wrapped)/intentions/restitution/HeaderSection/SecondaryFiltersSection.tsx @@ -6,7 +6,7 @@ import { getTypeDemandeLabel, TypeDemande, } from "../../../utils/typeDemandeUtils"; -import { Filters, StatsDemandes } from "../types"; +import { Filters, StatsIntentions } from "../types"; export const SecondaryFiltersSection = ({ activeFilters, @@ -17,7 +17,7 @@ export const SecondaryFiltersSection = ({ activeFilters: Filters; handleFilters: (type: keyof Filters, value: Filters[keyof Filters]) => void; filterTracker: (filterName: keyof Filters) => () => void; - data?: StatsDemandes; + data?: StatsIntentions; }) => { const handleMotifLabelFilter = (motifLabel: MotifLabel) => { if (motifLabel === "autre") return "Autre"; From b8045626ed7cd48b20e76f14670e5063c8386f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Tue, 21 Nov 2023 12:34:40 +0100 Subject: [PATCH 15/20] refactor: wip --- .../getTransformationStats.usecase.ts | 11 ++++++----- ui/app/(wrapped)/auth/authContext.server.ts | 5 ++--- ui/app/(wrapped)/auth/authContext.ts | 5 ++--- .../intentions/saisie/components/UaiAutocomplete.tsx | 9 +++++---- .../saisie/intentionForm/InformationsBlock.tsx | 6 +++--- .../saisie/intentionForm/cfdUaiSection/CfdBlock.tsx | 8 ++++---- .../intentionForm/cfdUaiSection/CfdUaiSection.tsx | 10 +++++----- .../intentionForm/cfdUaiSection/DispositifBlock.tsx | 6 +++--- .../saisie/intentionForm/cfdUaiSection/UaiBlock.tsx | 10 ++++++---- .../typeDemandeSection/TypeDemandeSection.tsx | 5 ++--- 10 files changed, 38 insertions(+), 37 deletions(-) diff --git a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts index 91fd28999..7ec595385 100644 --- a/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts +++ b/server/src/modules/data/usecases/getTransformationStats/getTransformationStats.usecase.ts @@ -173,11 +173,12 @@ const getTransformationStatsFactory = filiere?: string[]; orderBy?: { column: string; order: "asc" | "desc" }; }) => { - const resultDraft = await deps.getTransformationStatsQuery({ - ...activeFilters, - status: "draft", - }); - // .then((result) => formatResult(result, activeFilters.orderBy)); + const resultDraft = await deps + .getTransformationStatsQuery({ + ...activeFilters, + status: "draft", + }) + .then((result) => formatResult(result, activeFilters.orderBy)); const resultSubmitted = await deps .getTransformationStatsQuery({ diff --git a/ui/app/(wrapped)/auth/authContext.server.ts b/ui/app/(wrapped)/auth/authContext.server.ts index e39ebcad4..61078e1ca 100644 --- a/ui/app/(wrapped)/auth/authContext.server.ts +++ b/ui/app/(wrapped)/auth/authContext.server.ts @@ -1,10 +1,9 @@ import { createServerContext } from "react"; -import { ApiType } from "shared"; -import { api } from "@/api.client"; +import { client } from "@/api.client"; export const AuthContextServer = createServerContext<{ auth?: { - user: ApiType["user"]; + user: (typeof client.infer)["[GET]/auth/whoAmI"]["user"]; }; }>("authContext", {}); diff --git a/ui/app/(wrapped)/auth/authContext.ts b/ui/app/(wrapped)/auth/authContext.ts index 1d9ad1378..d9c27814e 100644 --- a/ui/app/(wrapped)/auth/authContext.ts +++ b/ui/app/(wrapped)/auth/authContext.ts @@ -1,9 +1,8 @@ import { createContext } from "react"; -import { ApiType } from "shared"; -import { api } from "@/api.client"; +import { client } from "@/api.client"; -export type Auth = { user: ApiType["user"] }; +export type Auth = { user: (typeof client.infer)["[GET]/auth/whoAmI"]["user"] }; export const AuthContext = createContext<{ auth?: Auth; diff --git a/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx b/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx index b6ee7f81b..c06e8f288 100644 --- a/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx +++ b/ui/app/(wrapped)/intentions/saisie/components/UaiAutocomplete.tsx @@ -1,8 +1,7 @@ import { CSSObjectWithLabel } from "react-select"; import AsyncSelect from "react-select/async"; -import { ApiType } from "shared"; -import { api, client } from "../../../../../api.client"; +import { client } from "../../../../../api.client"; export const UaiAutocomplete = ({ name, @@ -15,7 +14,9 @@ export const UaiAutocomplete = ({ defaultValue?: { value: string; label?: string; commune?: string }; active?: boolean; inError: boolean; - onChange: (value?: ApiType[number]) => void; + onChange: ( + value?: (typeof client.infer)["[GET]/etab/search/:search"][number] + ) => void; }) => { const selectStyle = { control: (styles: CSSObjectWithLabel) => ({ @@ -39,7 +40,7 @@ export const UaiAutocomplete = ({ defaultValue && ({ ...defaultValue, - } as ApiType[0]) + } as (typeof client.infer)["[GET]/etab/search/:search"][0]) } loadOptions={(inputValue: string) => { if (inputValue.length >= 3) diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/InformationsBlock.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/InformationsBlock.tsx index 41426a651..48c54a673 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/InformationsBlock.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/InformationsBlock.tsx @@ -9,9 +9,9 @@ import { UnorderedList, } from "@chakra-ui/react"; import { ReactNode } from "react"; -import { ApiType } from "shared"; -import { api } from "../../../../../api.client"; +import { client } from "@/api.client"; + import { CapaciteSection } from "./capaciteSection/CapaciteSection"; import { TypeDemandeSection } from "./typeDemandeSection/TypeDemandeSection"; @@ -23,7 +23,7 @@ export const InformationsBlock = ({ }: { disabled: boolean; errors?: Record; - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; footerActions: ReactNode; }) => { return ( diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdBlock.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdBlock.tsx index 216a59504..2c061cb03 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdBlock.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdBlock.tsx @@ -6,9 +6,9 @@ import { LightMode, } from "@chakra-ui/react"; import { Controller, useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; -import { api } from "../../../../../../api.client"; +import { client } from "@/api.client"; + import { CfdAutocompleteInput } from "../../components/CfdAutocomplete"; import { IntentionForms } from "../defaultFormValues"; @@ -19,10 +19,10 @@ export const CfdBlock = ({ active, }: { setDispositifs: ( - info?: ApiType[number]["dispositifs"] + info?: (typeof client.infer)["[GET]/diplome/search/:search"][number]["dispositifs"] ) => void; setIsFCIL: (isFcil: boolean) => void; - formMetaData?: ApiType["metadata"]; + formMetaData?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; active: boolean; }) => { const { diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdUaiSection.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdUaiSection.tsx index b937658e9..fade3c260 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdUaiSection.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/CfdUaiSection.tsx @@ -11,9 +11,8 @@ import { } from "@chakra-ui/react"; import { useEffect, useState } from "react"; import { useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; -import { api } from "@/api.client"; +import { client } from "@/api.client"; import { IntentionForms, PartialIntentionForms } from "../defaultFormValues"; import { CfdBlock } from "./CfdBlock"; @@ -36,7 +35,7 @@ export const CfdUaiSection = ({ active: boolean; disabled?: boolean; defaultValues: PartialIntentionForms; - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; onEditUaiCfdSection: () => void; isFCIL: boolean; setIsFCIL: (isFcil: boolean) => void; @@ -46,13 +45,14 @@ export const CfdUaiSection = ({ const { watch, getValues } = useFormContext(); const [dispositifs, setDispositifs] = useState< - ApiType[number]["dispositifs"] | undefined + | (typeof client.infer)["[GET]/diplome/search/:search"][number]["dispositifs"] + | undefined >(formMetadata?.formation?.dispositifs); const uai = watch("uai"); const [uaiInfo, setUaiInfo] = useState< - ApiType[number] | undefined + (typeof client.infer)["[GET]/etab/search/:search"][number] | undefined >( formMetadata?.etablissement?.libelle && uai ? { diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/DispositifBlock.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/DispositifBlock.tsx index 518e0bbdb..7814fc100 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/DispositifBlock.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/DispositifBlock.tsx @@ -7,9 +7,9 @@ import { } from "@chakra-ui/react"; import { useEffect } from "react"; import { Controller, useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; -import { api } from "../../../../../../api.client"; +import { client } from "@/api.client"; + import { IntentionForms } from "../defaultFormValues"; export const DispositifBlock = ({ @@ -17,7 +17,7 @@ export const DispositifBlock = ({ options, }: { active: boolean; - options?: ApiType[number]["dispositifs"]; + options?: (typeof client.infer)["[GET]/diplome/search/:search"][number]["dispositifs"]; }) => { const { formState: { errors }, diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/UaiBlock.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/UaiBlock.tsx index 530f3d722..a32026566 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/UaiBlock.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/cfdUaiSection/UaiBlock.tsx @@ -1,8 +1,8 @@ import { Box, FormControl, FormLabel, LightMode } from "@chakra-ui/react"; import { Controller, useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; -import { api } from "../../../../../../api.client"; +import { client } from "@/api.client"; + import { UaiAutocomplete } from "../../components/UaiAutocomplete"; import { IntentionForms } from "../defaultFormValues"; @@ -12,9 +12,11 @@ export const UaiBlock = ({ setUaiInfo, }: { active: boolean; - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; setUaiInfo: ( - uaiInfo: ApiType[number] | undefined + uaiInfo: + | (typeof client.infer)["[GET]/etab/search/:search"][number] + | undefined ) => void; }) => { const { diff --git a/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/TypeDemandeSection.tsx b/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/TypeDemandeSection.tsx index 5422eb566..e44ad7556 100644 --- a/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/TypeDemandeSection.tsx +++ b/ui/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/TypeDemandeSection.tsx @@ -1,10 +1,9 @@ import { Box, Divider, Fade, Flex, Heading } from "@chakra-ui/react"; import { useFormContext } from "react-hook-form"; -import { ApiType } from "shared"; +import { client } from "@/api.client"; import { MotifField } from "@/app/(wrapped)/intentions/saisie/intentionForm/typeDemandeSection/MotifField"; -import { api } from "../../../../../../api.client"; import { getTypeDemandeExemple, getTypeDemandeLabel, @@ -20,7 +19,7 @@ export const TypeDemandeSection = ({ formMetadata, disabled, }: { - formMetadata?: ApiType["metadata"]; + formMetadata?: (typeof client.infer)["[GET]/demande/:id"]["metadata"]; disabled?: boolean; }) => { const { watch } = useFormContext(); From ed45e8cd306f0a903a577d826d09d9333f637c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20D=C3=A9tr=C3=A9?= Date: Tue, 21 Nov 2023 12:47:04 +0100 Subject: [PATCH 16/20] refactor: module import --- server/src/cli.ts | 20 +++++++++---------- .../{inserJeunesApi => }/formatMillesime.ts | 0 .../getPilotageReformeStats/dependencies.ts | 2 +- .../dependencies.ts | 2 +- .../src/modules/{data => import}/data.di.ts | 0 .../domain/regionAcademiqueMapping.ts | 0 .../fileTypes/Attractivite_capacite.ts | 0 ...bre_division_effectifs_par_etab_mefst11.ts | 0 .../fileTypes/Decrochage_academique.ts | 0 .../fileTypes/Decrochage_regional.ts | 0 .../Departements_academies_regions.ts | 0 .../fileTypes/DiplomesProfessionnels.ts | 0 .../fileTypes/FamilleMetiers.ts | 0 .../fileTypes/LyceesACCELine.ts | 0 .../fileTypes/NDispositifFormation.ts | 0 .../fileTypes/NFormationDiplome.ts | 0 .../{data => import}/fileTypes/NMef.ts | 0 .../fileTypes/NNiveauFormationDiplome.ts | 0 .../fileTypes/Regroupement.ts | 0 .../repositories/rawData.repository.ts | 0 .../inserJeunesApi/formatRegionData.ts | 0 .../services/inserJeunesApi/formatUaiData.ts | 0 .../inserJeunesApi/inserJeunes.api.ts | 0 .../getCfdRentrees/findConstatRentrees.dep.ts | 0 .../usecases/getCfdRentrees/findNMefs.dep.ts | 0 .../getCfdRentrees/getCfdDispositifs.dep.ts | 0 .../getCfdRentrees/getCfdRentrees.usecase.ts | 0 .../createDataEtablissement.dep.ts | 0 .../findDepartement.dep.ts | 0 .../importDataEtablissements.usecase.ts | 0 .../importDataFormations/OVERRIDES.csv | 0 .../createDataFormation.dep.ts | 0 .../findDiplomeProfessionnel.dep.ts | 0 .../findFamilleMetier.dep.ts | 0 .../findNFormationDiplome.dep.ts | 0 .../findRegroupements.dep.ts | 0 .../importDataFormations.usecase.ts | 0 .../importDataFormations/overrides.ts | 0 .../importDispositifs.dependencies.ts | 0 .../importDispositifs.usecase.ts | 0 .../importFamillesMetiers.deps.ts | 0 .../importFamillesMetiers.usecase.ts | 0 .../domain/millesimes.ts | 0 .../findDiplomesProfessionnels.dep.ts | 0 .../importFormationEtablissements.usecase.ts | 0 .../importLogger.ts | 0 .../steps/fetchIJ/cacheIJ.dep.ts | 0 .../steps/fetchIJ/fetchIJ.step.ts | 0 .../steps/fetchIjReg/cacheIjReg.dep.ts | 0 .../steps/fetchIjReg/fetchIjReg.step.ts | 0 .../getIndicateurAffelnet.step.ts | 0 .../createEtablissement.dep.ts | 0 .../findDepartement.dep.ts | 0 .../importEtablissement/findLyceeAcce.dep.ts | 0 .../importEtablissement.step.ts | 0 .../importFormation/createFormation.dep.ts | 0 .../importFormation/findDataFormation.dep.ts | 0 .../importFormation/importFormation.step.ts | 0 .../createFormationHistorique.dep.ts | 0 .../findDataFormation.dep.ts | 0 .../importFormationsHistoriques.step.ts | 0 .../createFormationEtablissement.dep.ts | 0 .../getUaiData.dep.ts | 0 .../importIndicateurEtablissement.step.ts | 0 .../upsertIndicateurEtablissement.dep.ts | 0 .../createIndicateurEntree.dep.ts | 0 .../findFamilleMetier.ts | 0 .../importIndicateursEntree.step.ts | 0 .../createIndicateurSortie.dep.ts | 0 .../importIndicateursSortie/getUaiData.dep.ts | 0 .../importIndicateursSortie.step.ts | 0 .../createIndicateurRegionSortie.dep.ts | 0 .../findAnciennesFormation.dep.ts | 0 .../findIndicateurRegionSortie.dep.ts | 0 .../findNouvellesFormation.dep.ts | 0 .../importIndicateursSortieRegionaux.step.ts | 0 .../types/Logs.ts | 0 .../findAcademiesQuery.dep.ts | 0 .../importIndicateursAcademie.usecase.ts | 0 .../upsertIndicateurAcademieQuery.dep.ts | 0 .../findRegionsQuery.dep.ts | 0 .../importIndicateursRegion.usecase.ts | 0 .../upsertIndicateurRegionQuery.dep.ts | 0 .../importNiveauxDiplome/dependencies.ts | 0 .../importNiveauxDiplome.usecase.ts | 0 .../importRawFile/createRawDatas.dep.ts | 0 .../importRawFile/deleteRawData.dep.ts | 0 .../importRawFile/importRawFile.usecase.ts | 0 .../importLieuxGeographiques.deps.ts | 0 .../importLieuxGeographiques.usecase.ts | 0 .../modules/{data => import}/utils/parse.ts | 0 .../{data => import}/utils/streamIt.ts | 0 92 files changed, 12 insertions(+), 12 deletions(-) rename server/src/modules/data/services/{inserJeunesApi => }/formatMillesime.ts (100%) rename server/src/modules/{data => import}/data.di.ts (100%) rename server/src/modules/{data => import}/domain/regionAcademiqueMapping.ts (100%) rename server/src/modules/{data => import}/fileTypes/Attractivite_capacite.ts (100%) rename server/src/modules/{data => import}/fileTypes/Cab-nbre_division_effectifs_par_etab_mefst11.ts (100%) rename server/src/modules/{data => import}/fileTypes/Decrochage_academique.ts (100%) rename server/src/modules/{data => import}/fileTypes/Decrochage_regional.ts (100%) rename server/src/modules/{data => import}/fileTypes/Departements_academies_regions.ts (100%) rename server/src/modules/{data => import}/fileTypes/DiplomesProfessionnels.ts (100%) rename server/src/modules/{data => import}/fileTypes/FamilleMetiers.ts (100%) rename server/src/modules/{data => import}/fileTypes/LyceesACCELine.ts (100%) rename server/src/modules/{data => import}/fileTypes/NDispositifFormation.ts (100%) rename server/src/modules/{data => import}/fileTypes/NFormationDiplome.ts (100%) rename server/src/modules/{data => import}/fileTypes/NMef.ts (100%) rename server/src/modules/{data => import}/fileTypes/NNiveauFormationDiplome.ts (100%) rename server/src/modules/{data => import}/fileTypes/Regroupement.ts (100%) rename server/src/modules/{data => import}/repositories/rawData.repository.ts (100%) rename server/src/modules/{data => import}/services/inserJeunesApi/formatRegionData.ts (100%) rename server/src/modules/{data => import}/services/inserJeunesApi/formatUaiData.ts (100%) rename server/src/modules/{data => import}/services/inserJeunesApi/inserJeunes.api.ts (100%) rename server/src/modules/{data => import}/usecases/getCfdRentrees/findConstatRentrees.dep.ts (100%) rename server/src/modules/{data => import}/usecases/getCfdRentrees/findNMefs.dep.ts (100%) rename server/src/modules/{data => import}/usecases/getCfdRentrees/getCfdDispositifs.dep.ts (100%) rename server/src/modules/{data => import}/usecases/getCfdRentrees/getCfdRentrees.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importDataEtablissements/createDataEtablissement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataEtablissements/findDepartement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataEtablissements/importDataEtablissements.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/OVERRIDES.csv (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/createDataFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/findDiplomeProfessionnel.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/findFamilleMetier.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/findNFormationDiplome.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/findRegroupements.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/importDataFormations.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importDataFormations/overrides.ts (100%) rename server/src/modules/{data => import}/usecases/importDispositifs/importDispositifs.dependencies.ts (100%) rename server/src/modules/{data => import}/usecases/importDispositifs/importDispositifs.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importFamillesMetiers/importFamillesMetiers.deps.ts (100%) rename server/src/modules/{data => import}/usecases/importFamillesMetiers/importFamillesMetiers.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/domain/millesimes.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/findDiplomesProfessionnels.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/importFormationEtablissements.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/importLogger.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/fetchIJ/cacheIJ.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/fetchIJ/fetchIJ.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/fetchIjReg/cacheIjReg.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/fetchIjReg/fetchIjReg.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/getIndicateurAffelnet/getIndicateurAffelnet.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importEtablissement/createEtablissement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importEtablissement/findDepartement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importEtablissement/findLyceeAcce.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importEtablissement/importEtablissement.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormation/createFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormation/findDataFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormation/importFormation.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormationsHistoriques/createFormationHistorique.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormationsHistoriques/findDataFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importFormationsHistoriques/importFormationsHistoriques.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/createFormationEtablissement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/getUaiData.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/importIndicateurEtablissement.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/upsertIndicateurEtablissement.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursEntree/createIndicateurEntree.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursEntree/findFamilleMetier.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursEntree/importIndicateursEntree.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortie/createIndicateurSortie.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortie/getUaiData.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortie/importIndicateursSortie.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/createIndicateurRegionSortie.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findAnciennesFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findIndicateurRegionSortie.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findNouvellesFormation.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/importIndicateursSortieRegionaux.step.ts (100%) rename server/src/modules/{data => import}/usecases/importFormationEtablissement/types/Logs.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursAcademie/findAcademiesQuery.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursAcademie/upsertIndicateurAcademieQuery.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursRegion/findRegionsQuery.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursRegion/importIndicateursRegion.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importIndicateursRegion/upsertIndicateurRegionQuery.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importNiveauxDiplome/dependencies.ts (100%) rename server/src/modules/{data => import}/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importRawFile/createRawDatas.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importRawFile/deleteRawData.dep.ts (100%) rename server/src/modules/{data => import}/usecases/importRawFile/importRawFile.usecase.ts (100%) rename server/src/modules/{data => import}/usecases/importRegions/importLieuxGeographiques.deps.ts (100%) rename server/src/modules/{data => import}/usecases/importRegions/importLieuxGeographiques.usecase.ts (100%) rename server/src/modules/{data => import}/utils/parse.ts (100%) rename server/src/modules/{data => import}/utils/streamIt.ts (100%) diff --git a/server/src/cli.ts b/server/src/cli.ts index 557e9851c..4c8877eb8 100644 --- a/server/src/cli.ts +++ b/server/src/cli.ts @@ -7,16 +7,16 @@ import { z } from "zod"; import { basepath } from "./basepath"; import { migrateDownDB, migrateToLatest } from "./migrations/migrate"; import { createUser } from "./modules/core/usecases/createUser/createUser.usecase"; -import { importDataEtablissements } from "./modules/data/usecases/importDataEtablissements/importDataEtablissements.usecase"; -import { importDataFormations } from "./modules/data/usecases/importDataFormations/importDataFormations.usecase"; -import { importDispositifs } from "./modules/data/usecases/importDispositifs/importDispositifs.usecase"; -import { importFamillesMetiers } from "./modules/data/usecases/importFamillesMetiers/importFamillesMetiers.usecase"; -import { importFormations } from "./modules/data/usecases/importFormationEtablissement/importFormationEtablissements.usecase"; -import { importIndicateursAcademie } from "./modules/data/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase"; -import { importIndicateursRegion } from "./modules/data/usecases/importIndicateursRegion/importIndicateursRegion.usecase"; -import { importNiveauxDiplome } from "./modules/data/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase"; -import { importRawFile } from "./modules/data/usecases/importRawFile/importRawFile.usecase"; -import { importLieuxGeographiques } from "./modules/data/usecases/importRegions/importLieuxGeographiques.usecase"; +import { importDataEtablissements } from "./modules/import/usecases/importDataEtablissements/importDataEtablissements.usecase"; +import { importDataFormations } from "./modules/import/usecases/importDataFormations/importDataFormations.usecase"; +import { importDispositifs } from "./modules/import/usecases/importDispositifs/importDispositifs.usecase"; +import { importFamillesMetiers } from "./modules/import/usecases/importFamillesMetiers/importFamillesMetiers.usecase"; +import { importFormations } from "./modules/import/usecases/importFormationEtablissement/importFormationEtablissements.usecase"; +import { importIndicateursAcademie } from "./modules/import/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase"; +import { importIndicateursRegion } from "./modules/import/usecases/importIndicateursRegion/importIndicateursRegion.usecase"; +import { importNiveauxDiplome } from "./modules/import/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase"; +import { importRawFile } from "./modules/import/usecases/importRawFile/importRawFile.usecase"; +import { importLieuxGeographiques } from "./modules/import/usecases/importRegions/importLieuxGeographiques.usecase"; cli.command("migrateDB").action(async () => { await migrateToLatest(); diff --git a/server/src/modules/data/services/inserJeunesApi/formatMillesime.ts b/server/src/modules/data/services/formatMillesime.ts similarity index 100% rename from server/src/modules/data/services/inserJeunesApi/formatMillesime.ts rename to server/src/modules/data/services/formatMillesime.ts diff --git a/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts b/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts index b1afb5ffe..a841c0c8b 100644 --- a/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStats/dependencies.ts @@ -5,7 +5,7 @@ import { cleanNull } from "../../../../utils/noNull"; import { getMillesimeFromRentreeScolaire, getRentreeScolaire, -} from "../../services/inserJeunesApi/formatMillesime"; +} from "../../services/formatMillesime"; import { effectifAnnee } from "../../utils/effectifAnnee"; import { notHistorique, diff --git a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts index 125669686..ac6dfd515 100644 --- a/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts +++ b/server/src/modules/data/usecases/getPilotageReformeStatsRegions/dependencies.ts @@ -2,7 +2,7 @@ import { sql } from "kysely"; import { kdb } from "../../../../db/db"; import { cleanNull } from "../../../../utils/noNull"; -import { getMillesimeFromRentreeScolaire } from "../../services/inserJeunesApi/formatMillesime"; +import { getMillesimeFromRentreeScolaire } from "../../services/formatMillesime"; import { notHistoriqueIndicateurRegionSortie } from "../../utils/notHistorique"; import { selectTauxInsertion6moisAgg } from "../../utils/tauxInsertion6mois"; import { selectTauxPoursuiteAgg } from "../../utils/tauxPoursuite"; diff --git a/server/src/modules/data/data.di.ts b/server/src/modules/import/data.di.ts similarity index 100% rename from server/src/modules/data/data.di.ts rename to server/src/modules/import/data.di.ts diff --git a/server/src/modules/data/domain/regionAcademiqueMapping.ts b/server/src/modules/import/domain/regionAcademiqueMapping.ts similarity index 100% rename from server/src/modules/data/domain/regionAcademiqueMapping.ts rename to server/src/modules/import/domain/regionAcademiqueMapping.ts diff --git a/server/src/modules/data/fileTypes/Attractivite_capacite.ts b/server/src/modules/import/fileTypes/Attractivite_capacite.ts similarity index 100% rename from server/src/modules/data/fileTypes/Attractivite_capacite.ts rename to server/src/modules/import/fileTypes/Attractivite_capacite.ts diff --git a/server/src/modules/data/fileTypes/Cab-nbre_division_effectifs_par_etab_mefst11.ts b/server/src/modules/import/fileTypes/Cab-nbre_division_effectifs_par_etab_mefst11.ts similarity index 100% rename from server/src/modules/data/fileTypes/Cab-nbre_division_effectifs_par_etab_mefst11.ts rename to server/src/modules/import/fileTypes/Cab-nbre_division_effectifs_par_etab_mefst11.ts diff --git a/server/src/modules/data/fileTypes/Decrochage_academique.ts b/server/src/modules/import/fileTypes/Decrochage_academique.ts similarity index 100% rename from server/src/modules/data/fileTypes/Decrochage_academique.ts rename to server/src/modules/import/fileTypes/Decrochage_academique.ts diff --git a/server/src/modules/data/fileTypes/Decrochage_regional.ts b/server/src/modules/import/fileTypes/Decrochage_regional.ts similarity index 100% rename from server/src/modules/data/fileTypes/Decrochage_regional.ts rename to server/src/modules/import/fileTypes/Decrochage_regional.ts diff --git a/server/src/modules/data/fileTypes/Departements_academies_regions.ts b/server/src/modules/import/fileTypes/Departements_academies_regions.ts similarity index 100% rename from server/src/modules/data/fileTypes/Departements_academies_regions.ts rename to server/src/modules/import/fileTypes/Departements_academies_regions.ts diff --git a/server/src/modules/data/fileTypes/DiplomesProfessionnels.ts b/server/src/modules/import/fileTypes/DiplomesProfessionnels.ts similarity index 100% rename from server/src/modules/data/fileTypes/DiplomesProfessionnels.ts rename to server/src/modules/import/fileTypes/DiplomesProfessionnels.ts diff --git a/server/src/modules/data/fileTypes/FamilleMetiers.ts b/server/src/modules/import/fileTypes/FamilleMetiers.ts similarity index 100% rename from server/src/modules/data/fileTypes/FamilleMetiers.ts rename to server/src/modules/import/fileTypes/FamilleMetiers.ts diff --git a/server/src/modules/data/fileTypes/LyceesACCELine.ts b/server/src/modules/import/fileTypes/LyceesACCELine.ts similarity index 100% rename from server/src/modules/data/fileTypes/LyceesACCELine.ts rename to server/src/modules/import/fileTypes/LyceesACCELine.ts diff --git a/server/src/modules/data/fileTypes/NDispositifFormation.ts b/server/src/modules/import/fileTypes/NDispositifFormation.ts similarity index 100% rename from server/src/modules/data/fileTypes/NDispositifFormation.ts rename to server/src/modules/import/fileTypes/NDispositifFormation.ts diff --git a/server/src/modules/data/fileTypes/NFormationDiplome.ts b/server/src/modules/import/fileTypes/NFormationDiplome.ts similarity index 100% rename from server/src/modules/data/fileTypes/NFormationDiplome.ts rename to server/src/modules/import/fileTypes/NFormationDiplome.ts diff --git a/server/src/modules/data/fileTypes/NMef.ts b/server/src/modules/import/fileTypes/NMef.ts similarity index 100% rename from server/src/modules/data/fileTypes/NMef.ts rename to server/src/modules/import/fileTypes/NMef.ts diff --git a/server/src/modules/data/fileTypes/NNiveauFormationDiplome.ts b/server/src/modules/import/fileTypes/NNiveauFormationDiplome.ts similarity index 100% rename from server/src/modules/data/fileTypes/NNiveauFormationDiplome.ts rename to server/src/modules/import/fileTypes/NNiveauFormationDiplome.ts diff --git a/server/src/modules/data/fileTypes/Regroupement.ts b/server/src/modules/import/fileTypes/Regroupement.ts similarity index 100% rename from server/src/modules/data/fileTypes/Regroupement.ts rename to server/src/modules/import/fileTypes/Regroupement.ts diff --git a/server/src/modules/data/repositories/rawData.repository.ts b/server/src/modules/import/repositories/rawData.repository.ts similarity index 100% rename from server/src/modules/data/repositories/rawData.repository.ts rename to server/src/modules/import/repositories/rawData.repository.ts diff --git a/server/src/modules/data/services/inserJeunesApi/formatRegionData.ts b/server/src/modules/import/services/inserJeunesApi/formatRegionData.ts similarity index 100% rename from server/src/modules/data/services/inserJeunesApi/formatRegionData.ts rename to server/src/modules/import/services/inserJeunesApi/formatRegionData.ts diff --git a/server/src/modules/data/services/inserJeunesApi/formatUaiData.ts b/server/src/modules/import/services/inserJeunesApi/formatUaiData.ts similarity index 100% rename from server/src/modules/data/services/inserJeunesApi/formatUaiData.ts rename to server/src/modules/import/services/inserJeunesApi/formatUaiData.ts diff --git a/server/src/modules/data/services/inserJeunesApi/inserJeunes.api.ts b/server/src/modules/import/services/inserJeunesApi/inserJeunes.api.ts similarity index 100% rename from server/src/modules/data/services/inserJeunesApi/inserJeunes.api.ts rename to server/src/modules/import/services/inserJeunesApi/inserJeunes.api.ts diff --git a/server/src/modules/data/usecases/getCfdRentrees/findConstatRentrees.dep.ts b/server/src/modules/import/usecases/getCfdRentrees/findConstatRentrees.dep.ts similarity index 100% rename from server/src/modules/data/usecases/getCfdRentrees/findConstatRentrees.dep.ts rename to server/src/modules/import/usecases/getCfdRentrees/findConstatRentrees.dep.ts diff --git a/server/src/modules/data/usecases/getCfdRentrees/findNMefs.dep.ts b/server/src/modules/import/usecases/getCfdRentrees/findNMefs.dep.ts similarity index 100% rename from server/src/modules/data/usecases/getCfdRentrees/findNMefs.dep.ts rename to server/src/modules/import/usecases/getCfdRentrees/findNMefs.dep.ts diff --git a/server/src/modules/data/usecases/getCfdRentrees/getCfdDispositifs.dep.ts b/server/src/modules/import/usecases/getCfdRentrees/getCfdDispositifs.dep.ts similarity index 100% rename from server/src/modules/data/usecases/getCfdRentrees/getCfdDispositifs.dep.ts rename to server/src/modules/import/usecases/getCfdRentrees/getCfdDispositifs.dep.ts diff --git a/server/src/modules/data/usecases/getCfdRentrees/getCfdRentrees.usecase.ts b/server/src/modules/import/usecases/getCfdRentrees/getCfdRentrees.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/getCfdRentrees/getCfdRentrees.usecase.ts rename to server/src/modules/import/usecases/getCfdRentrees/getCfdRentrees.usecase.ts diff --git a/server/src/modules/data/usecases/importDataEtablissements/createDataEtablissement.dep.ts b/server/src/modules/import/usecases/importDataEtablissements/createDataEtablissement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataEtablissements/createDataEtablissement.dep.ts rename to server/src/modules/import/usecases/importDataEtablissements/createDataEtablissement.dep.ts diff --git a/server/src/modules/data/usecases/importDataEtablissements/findDepartement.dep.ts b/server/src/modules/import/usecases/importDataEtablissements/findDepartement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataEtablissements/findDepartement.dep.ts rename to server/src/modules/import/usecases/importDataEtablissements/findDepartement.dep.ts diff --git a/server/src/modules/data/usecases/importDataEtablissements/importDataEtablissements.usecase.ts b/server/src/modules/import/usecases/importDataEtablissements/importDataEtablissements.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importDataEtablissements/importDataEtablissements.usecase.ts rename to server/src/modules/import/usecases/importDataEtablissements/importDataEtablissements.usecase.ts diff --git a/server/src/modules/data/usecases/importDataFormations/OVERRIDES.csv b/server/src/modules/import/usecases/importDataFormations/OVERRIDES.csv similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/OVERRIDES.csv rename to server/src/modules/import/usecases/importDataFormations/OVERRIDES.csv diff --git a/server/src/modules/data/usecases/importDataFormations/createDataFormation.dep.ts b/server/src/modules/import/usecases/importDataFormations/createDataFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/createDataFormation.dep.ts rename to server/src/modules/import/usecases/importDataFormations/createDataFormation.dep.ts diff --git a/server/src/modules/data/usecases/importDataFormations/findDiplomeProfessionnel.dep.ts b/server/src/modules/import/usecases/importDataFormations/findDiplomeProfessionnel.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/findDiplomeProfessionnel.dep.ts rename to server/src/modules/import/usecases/importDataFormations/findDiplomeProfessionnel.dep.ts diff --git a/server/src/modules/data/usecases/importDataFormations/findFamilleMetier.dep.ts b/server/src/modules/import/usecases/importDataFormations/findFamilleMetier.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/findFamilleMetier.dep.ts rename to server/src/modules/import/usecases/importDataFormations/findFamilleMetier.dep.ts diff --git a/server/src/modules/data/usecases/importDataFormations/findNFormationDiplome.dep.ts b/server/src/modules/import/usecases/importDataFormations/findNFormationDiplome.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/findNFormationDiplome.dep.ts rename to server/src/modules/import/usecases/importDataFormations/findNFormationDiplome.dep.ts diff --git a/server/src/modules/data/usecases/importDataFormations/findRegroupements.dep.ts b/server/src/modules/import/usecases/importDataFormations/findRegroupements.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/findRegroupements.dep.ts rename to server/src/modules/import/usecases/importDataFormations/findRegroupements.dep.ts diff --git a/server/src/modules/data/usecases/importDataFormations/importDataFormations.usecase.ts b/server/src/modules/import/usecases/importDataFormations/importDataFormations.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/importDataFormations.usecase.ts rename to server/src/modules/import/usecases/importDataFormations/importDataFormations.usecase.ts diff --git a/server/src/modules/data/usecases/importDataFormations/overrides.ts b/server/src/modules/import/usecases/importDataFormations/overrides.ts similarity index 100% rename from server/src/modules/data/usecases/importDataFormations/overrides.ts rename to server/src/modules/import/usecases/importDataFormations/overrides.ts diff --git a/server/src/modules/data/usecases/importDispositifs/importDispositifs.dependencies.ts b/server/src/modules/import/usecases/importDispositifs/importDispositifs.dependencies.ts similarity index 100% rename from server/src/modules/data/usecases/importDispositifs/importDispositifs.dependencies.ts rename to server/src/modules/import/usecases/importDispositifs/importDispositifs.dependencies.ts diff --git a/server/src/modules/data/usecases/importDispositifs/importDispositifs.usecase.ts b/server/src/modules/import/usecases/importDispositifs/importDispositifs.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importDispositifs/importDispositifs.usecase.ts rename to server/src/modules/import/usecases/importDispositifs/importDispositifs.usecase.ts diff --git a/server/src/modules/data/usecases/importFamillesMetiers/importFamillesMetiers.deps.ts b/server/src/modules/import/usecases/importFamillesMetiers/importFamillesMetiers.deps.ts similarity index 100% rename from server/src/modules/data/usecases/importFamillesMetiers/importFamillesMetiers.deps.ts rename to server/src/modules/import/usecases/importFamillesMetiers/importFamillesMetiers.deps.ts diff --git a/server/src/modules/data/usecases/importFamillesMetiers/importFamillesMetiers.usecase.ts b/server/src/modules/import/usecases/importFamillesMetiers/importFamillesMetiers.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importFamillesMetiers/importFamillesMetiers.usecase.ts rename to server/src/modules/import/usecases/importFamillesMetiers/importFamillesMetiers.usecase.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/domain/millesimes.ts b/server/src/modules/import/usecases/importFormationEtablissement/domain/millesimes.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/domain/millesimes.ts rename to server/src/modules/import/usecases/importFormationEtablissement/domain/millesimes.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/findDiplomesProfessionnels.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/findDiplomesProfessionnels.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/findDiplomesProfessionnels.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/findDiplomesProfessionnels.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/importFormationEtablissements.usecase.ts b/server/src/modules/import/usecases/importFormationEtablissement/importFormationEtablissements.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/importFormationEtablissements.usecase.ts rename to server/src/modules/import/usecases/importFormationEtablissement/importFormationEtablissements.usecase.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/importLogger.ts b/server/src/modules/import/usecases/importFormationEtablissement/importLogger.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/importLogger.ts rename to server/src/modules/import/usecases/importFormationEtablissement/importLogger.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIJ/cacheIJ.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIJ/cacheIJ.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIJ/cacheIJ.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIJ/cacheIJ.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIJ/fetchIJ.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIJ/fetchIJ.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIJ/fetchIJ.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIJ/fetchIJ.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIjReg/cacheIjReg.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIjReg/cacheIjReg.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIjReg/cacheIjReg.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIjReg/cacheIjReg.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIjReg/fetchIjReg.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIjReg/fetchIjReg.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/fetchIjReg/fetchIjReg.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/fetchIjReg/fetchIjReg.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/getIndicateurAffelnet/getIndicateurAffelnet.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/getIndicateurAffelnet/getIndicateurAffelnet.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/getIndicateurAffelnet/getIndicateurAffelnet.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/getIndicateurAffelnet/getIndicateurAffelnet.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/createEtablissement.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/createEtablissement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/createEtablissement.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/createEtablissement.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/findDepartement.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/findDepartement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/findDepartement.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/findDepartement.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/findLyceeAcce.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/findLyceeAcce.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/findLyceeAcce.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/findLyceeAcce.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/importEtablissement.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/importEtablissement.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importEtablissement/importEtablissement.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importEtablissement/importEtablissement.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/createFormation.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/createFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/createFormation.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/createFormation.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/findDataFormation.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/findDataFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/findDataFormation.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/findDataFormation.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/importFormation.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/importFormation.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormation/importFormation.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormation/importFormation.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/createFormationHistorique.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/createFormationHistorique.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/createFormationHistorique.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/createFormationHistorique.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/findDataFormation.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/findDataFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/findDataFormation.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/findDataFormation.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/importFormationsHistoriques.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/importFormationsHistoriques.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importFormationsHistoriques/importFormationsHistoriques.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importFormationsHistoriques/importFormationsHistoriques.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/createFormationEtablissement.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/createFormationEtablissement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/createFormationEtablissement.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/createFormationEtablissement.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/getUaiData.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/getUaiData.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/getUaiData.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/getUaiData.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/importIndicateurEtablissement.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/importIndicateurEtablissement.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/importIndicateurEtablissement.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/importIndicateurEtablissement.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/upsertIndicateurEtablissement.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/upsertIndicateurEtablissement.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/upsertIndicateurEtablissement.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateurEtablissement/upsertIndicateurEtablissement.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/createIndicateurEntree.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/createIndicateurEntree.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/createIndicateurEntree.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/createIndicateurEntree.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/findFamilleMetier.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/findFamilleMetier.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/findFamilleMetier.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/findFamilleMetier.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/importIndicateursEntree.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/importIndicateursEntree.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursEntree/importIndicateursEntree.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursEntree/importIndicateursEntree.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/createIndicateurSortie.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/createIndicateurSortie.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/createIndicateurSortie.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/createIndicateurSortie.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/getUaiData.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/getUaiData.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/getUaiData.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/getUaiData.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/importIndicateursSortie.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/importIndicateursSortie.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortie/importIndicateursSortie.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortie/importIndicateursSortie.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/createIndicateurRegionSortie.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/createIndicateurRegionSortie.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/createIndicateurRegionSortie.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/createIndicateurRegionSortie.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findAnciennesFormation.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findAnciennesFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findAnciennesFormation.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findAnciennesFormation.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findIndicateurRegionSortie.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findIndicateurRegionSortie.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findIndicateurRegionSortie.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findIndicateurRegionSortie.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findNouvellesFormation.dep.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findNouvellesFormation.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findNouvellesFormation.dep.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/findNouvellesFormation.dep.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/importIndicateursSortieRegionaux.step.ts b/server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/importIndicateursSortieRegionaux.step.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/importIndicateursSortieRegionaux.step.ts rename to server/src/modules/import/usecases/importFormationEtablissement/steps/importIndicateursSortieRegionaux/importIndicateursSortieRegionaux.step.ts diff --git a/server/src/modules/data/usecases/importFormationEtablissement/types/Logs.ts b/server/src/modules/import/usecases/importFormationEtablissement/types/Logs.ts similarity index 100% rename from server/src/modules/data/usecases/importFormationEtablissement/types/Logs.ts rename to server/src/modules/import/usecases/importFormationEtablissement/types/Logs.ts diff --git a/server/src/modules/data/usecases/importIndicateursAcademie/findAcademiesQuery.dep.ts b/server/src/modules/import/usecases/importIndicateursAcademie/findAcademiesQuery.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursAcademie/findAcademiesQuery.dep.ts rename to server/src/modules/import/usecases/importIndicateursAcademie/findAcademiesQuery.dep.ts diff --git a/server/src/modules/data/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase.ts b/server/src/modules/import/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase.ts rename to server/src/modules/import/usecases/importIndicateursAcademie/importIndicateursAcademie.usecase.ts diff --git a/server/src/modules/data/usecases/importIndicateursAcademie/upsertIndicateurAcademieQuery.dep.ts b/server/src/modules/import/usecases/importIndicateursAcademie/upsertIndicateurAcademieQuery.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursAcademie/upsertIndicateurAcademieQuery.dep.ts rename to server/src/modules/import/usecases/importIndicateursAcademie/upsertIndicateurAcademieQuery.dep.ts diff --git a/server/src/modules/data/usecases/importIndicateursRegion/findRegionsQuery.dep.ts b/server/src/modules/import/usecases/importIndicateursRegion/findRegionsQuery.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursRegion/findRegionsQuery.dep.ts rename to server/src/modules/import/usecases/importIndicateursRegion/findRegionsQuery.dep.ts diff --git a/server/src/modules/data/usecases/importIndicateursRegion/importIndicateursRegion.usecase.ts b/server/src/modules/import/usecases/importIndicateursRegion/importIndicateursRegion.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursRegion/importIndicateursRegion.usecase.ts rename to server/src/modules/import/usecases/importIndicateursRegion/importIndicateursRegion.usecase.ts diff --git a/server/src/modules/data/usecases/importIndicateursRegion/upsertIndicateurRegionQuery.dep.ts b/server/src/modules/import/usecases/importIndicateursRegion/upsertIndicateurRegionQuery.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importIndicateursRegion/upsertIndicateurRegionQuery.dep.ts rename to server/src/modules/import/usecases/importIndicateursRegion/upsertIndicateurRegionQuery.dep.ts diff --git a/server/src/modules/data/usecases/importNiveauxDiplome/dependencies.ts b/server/src/modules/import/usecases/importNiveauxDiplome/dependencies.ts similarity index 100% rename from server/src/modules/data/usecases/importNiveauxDiplome/dependencies.ts rename to server/src/modules/import/usecases/importNiveauxDiplome/dependencies.ts diff --git a/server/src/modules/data/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase.ts b/server/src/modules/import/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase.ts rename to server/src/modules/import/usecases/importNiveauxDiplome/importNiveauxDiplome.usecase.ts diff --git a/server/src/modules/data/usecases/importRawFile/createRawDatas.dep.ts b/server/src/modules/import/usecases/importRawFile/createRawDatas.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importRawFile/createRawDatas.dep.ts rename to server/src/modules/import/usecases/importRawFile/createRawDatas.dep.ts diff --git a/server/src/modules/data/usecases/importRawFile/deleteRawData.dep.ts b/server/src/modules/import/usecases/importRawFile/deleteRawData.dep.ts similarity index 100% rename from server/src/modules/data/usecases/importRawFile/deleteRawData.dep.ts rename to server/src/modules/import/usecases/importRawFile/deleteRawData.dep.ts diff --git a/server/src/modules/data/usecases/importRawFile/importRawFile.usecase.ts b/server/src/modules/import/usecases/importRawFile/importRawFile.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importRawFile/importRawFile.usecase.ts rename to server/src/modules/import/usecases/importRawFile/importRawFile.usecase.ts diff --git a/server/src/modules/data/usecases/importRegions/importLieuxGeographiques.deps.ts b/server/src/modules/import/usecases/importRegions/importLieuxGeographiques.deps.ts similarity index 100% rename from server/src/modules/data/usecases/importRegions/importLieuxGeographiques.deps.ts rename to server/src/modules/import/usecases/importRegions/importLieuxGeographiques.deps.ts diff --git a/server/src/modules/data/usecases/importRegions/importLieuxGeographiques.usecase.ts b/server/src/modules/import/usecases/importRegions/importLieuxGeographiques.usecase.ts similarity index 100% rename from server/src/modules/data/usecases/importRegions/importLieuxGeographiques.usecase.ts rename to server/src/modules/import/usecases/importRegions/importLieuxGeographiques.usecase.ts diff --git a/server/src/modules/data/utils/parse.ts b/server/src/modules/import/utils/parse.ts similarity index 100% rename from server/src/modules/data/utils/parse.ts rename to server/src/modules/import/utils/parse.ts diff --git a/server/src/modules/data/utils/streamIt.ts b/server/src/modules/import/utils/streamIt.ts similarity index 100% rename from server/src/modules/data/utils/streamIt.ts rename to server/src/modules/import/utils/streamIt.ts From 583e28a7a6d26639e199c1beceae904013f35a87 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 14:16:23 +0100 Subject: [PATCH 17/20] wip --- ui/app/(wrapped)/auth/authContext.server.ts | 9 --------- ui/app/(wrapped)/auth/authContext.ts | 4 +++- 2 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 ui/app/(wrapped)/auth/authContext.server.ts diff --git a/ui/app/(wrapped)/auth/authContext.server.ts b/ui/app/(wrapped)/auth/authContext.server.ts deleted file mode 100644 index 61078e1ca..000000000 --- a/ui/app/(wrapped)/auth/authContext.server.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createServerContext } from "react"; - -import { client } from "@/api.client"; - -export const AuthContextServer = createServerContext<{ - auth?: { - user: (typeof client.infer)["[GET]/auth/whoAmI"]["user"]; - }; -}>("authContext", {}); diff --git a/ui/app/(wrapped)/auth/authContext.ts b/ui/app/(wrapped)/auth/authContext.ts index d9c27814e..9c3f603e4 100644 --- a/ui/app/(wrapped)/auth/authContext.ts +++ b/ui/app/(wrapped)/auth/authContext.ts @@ -2,7 +2,9 @@ import { createContext } from "react"; import { client } from "@/api.client"; -export type Auth = { user: (typeof client.infer)["[GET]/auth/whoAmI"]["user"] }; +export type Auth = { + user: Exclude<(typeof client.infer)["[GET]/auth/whoAmI"], undefined>["user"]; +}; export const AuthContext = createContext<{ auth?: Auth; From 3b8d30d103e345dfef3b52ddf042d6be2994194f Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 14:22:38 +0100 Subject: [PATCH 18/20] wip --- ui/api.client.ts | 9 --------- .../departement/[codeDepartement]/page.tsx | 16 +++++++--------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/ui/api.client.ts b/ui/api.client.ts index 4a606ef10..438173be1 100644 --- a/ui/api.client.ts +++ b/ui/api.client.ts @@ -2,15 +2,6 @@ import { ZodTypeProvider } from "@http-wizard/core"; import { createQueryClient } from "@http-wizard/react-query"; import axios from "axios"; import { Router } from "server"; -import { createClient } from "shared"; - -export const api = createClient( - axios.create({ baseURL: `${process.env.NEXT_PUBLIC_SERVER_URL}` }) -); - -export const serverApi = createClient( - axios.create({ baseURL: process.env.NEXT_PUBLIC_APP_CONTAINER_URL }) -); export const client = createQueryClient({ instance: axios.create({ diff --git a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx index 13f6cfe71..ece846c9a 100644 --- a/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx +++ b/ui/app/(wrapped)/panorama/departement/[codeDepartement]/page.tsx @@ -1,10 +1,9 @@ "use client"; -import { useQuery } from "@tanstack/react-query"; import { useRouter } from "next/navigation"; import { useState } from "react"; -import { api, client } from "@/api.client"; +import { client } from "@/api.client"; import { CadranSection } from "../../components/CadranSection"; import { FiltersSection } from "../../components/FiltersSection"; @@ -50,13 +49,12 @@ export default function Panorama({ } ); - const { data } = useQuery( - ["formationForPanorama", { codeDepartement }], - api.getDataForPanoramaDepartement({ - query: { codeDepartement }, - }).call, - { keepPreviousData: true, staleTime: 10000000 } - ); + const { data } = client + .ref("[GET]/panorama/stats/departement") + .useQuery( + { query: { codeDepartement } }, + { keepPreviousData: true, staleTime: 10000000 } + ); return ( <> From 3525b1180a5a6a463af44eb9377a06016d2b58bb Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 14:23:08 +0100 Subject: [PATCH 19/20] wip --- .../data/routes/pilotageReforme.routes.ts | 38 ---- .../routes/pilotageTransformation.routes.ts | 28 --- .../src/modules/data/routes/regions.routes.ts | 29 --- .../routes/restitutionIntentions.routes.ts | 46 ----- shared/client/ROUTES_CONFIG.ts | 13 -- shared/client/client.ts | 15 -- shared/client/clientFactory.ts | 113 ----------- .../etablissements/etablissements.client.ts | 31 ---- .../etablissements/etablissements.schema.ts | 175 ------------------ shared/client/formations/formation.client.ts | 36 ---- shared/client/formations/formation.schema.ts | 150 --------------- .../pilotageReforme/pilotageReforme.client.ts | 21 --- .../pilotageReforme/pilotageReforme.schema.ts | 64 ------- .../pilotageTransfo/pilotageTransfo.client.ts | 23 --- .../pilotageTransfo/pilotageTransfo.schema.ts | 152 --------------- .../restitutionIntentions.client.ts | 21 --- .../restitutionIntentions.schema.ts | 143 -------------- shared/client/utils.ts | 21 --- shared/index.ts | 3 - 19 files changed, 1122 deletions(-) delete mode 100644 server/src/modules/data/routes/pilotageReforme.routes.ts delete mode 100644 server/src/modules/data/routes/pilotageTransformation.routes.ts delete mode 100644 server/src/modules/data/routes/regions.routes.ts delete mode 100644 server/src/modules/data/routes/restitutionIntentions.routes.ts delete mode 100644 shared/client/ROUTES_CONFIG.ts delete mode 100644 shared/client/client.ts delete mode 100644 shared/client/clientFactory.ts delete mode 100644 shared/client/etablissements/etablissements.client.ts delete mode 100644 shared/client/etablissements/etablissements.schema.ts delete mode 100644 shared/client/formations/formation.client.ts delete mode 100644 shared/client/formations/formation.schema.ts delete mode 100644 shared/client/pilotageReforme/pilotageReforme.client.ts delete mode 100644 shared/client/pilotageReforme/pilotageReforme.schema.ts delete mode 100644 shared/client/pilotageTransfo/pilotageTransfo.client.ts delete mode 100644 shared/client/pilotageTransfo/pilotageTransfo.schema.ts delete mode 100644 shared/client/restitutionIntentions/restitutionIntentions.client.ts delete mode 100644 shared/client/restitutionIntentions/restitutionIntentions.schema.ts delete mode 100644 shared/client/utils.ts diff --git a/server/src/modules/data/routes/pilotageReforme.routes.ts b/server/src/modules/data/routes/pilotageReforme.routes.ts deleted file mode 100644 index 9a3ef2bf7..000000000 --- a/server/src/modules/data/routes/pilotageReforme.routes.ts +++ /dev/null @@ -1,38 +0,0 @@ -// import { ROUTES_CONFIG } from "shared"; - -// import { Server } from "../../../server"; -// import { hasPermissionHandler } from "../../core"; -// import { getPilotageReformeStats } from "../queries/getPilotageReformeStats/getPilotageReformeStats.query"; -// import { getPilotageReformeStatsRegions } from "../queries/getPilotageReformeStatsRegions/getPilotageReformeStatsRegions.query"; - -// export const pilotageReformeRoutes = ({ server }: { server: Server }) => { -// server.get( -// "/pilotage-reforme/stats", -// { -// schema: ROUTES_CONFIG.getPilotageReformeStats, -// preHandler: hasPermissionHandler("pilotage_reforme/lecture"), -// }, -// async (request, response) => { -// const stats = await getPilotageReformeStats({ -// ...request.query, -// }); -// response.status(200).send(stats); -// } -// ); - -// server.get( -// "/pilotage-reforme/stats/regions", -// { -// schema: ROUTES_CONFIG.getPilotageReformeStatsRegions, -// preHandler: hasPermissionHandler("pilotage_reforme/lecture"), -// }, -// async (request, response) => { -// const { order, orderBy, ...rest } = request.query; -// const stats = await getPilotageReformeStatsRegions({ -// ...rest, -// orderBy: order && orderBy ? { order, column: orderBy } : undefined, -// }); -// response.status(200).send(stats); -// } -// ); -// }; diff --git a/server/src/modules/data/routes/pilotageTransformation.routes.ts b/server/src/modules/data/routes/pilotageTransformation.routes.ts deleted file mode 100644 index bafc544ec..000000000 --- a/server/src/modules/data/routes/pilotageTransformation.routes.ts +++ /dev/null @@ -1,28 +0,0 @@ -// import { ROUTES_CONFIG } from "shared"; - -// import { Server } from "../../../server"; -// import { hasPermissionHandler } from "../../core"; -// import { getTransformationStats } from "../usecases/getTransformationStats/getTransformationStats.usecase"; - -// export const pilotageTransformationRoutes = ({ -// server, -// }: { -// server: Server; -// }) => { -// server.get( -// "/pilotage-transformation/stats", -// { -// schema: ROUTES_CONFIG.getTransformationStats, -// preHandler: hasPermissionHandler("pilotage-intentions/lecture"), -// }, -// async (request, response) => { -// const { order, orderBy, ...filters } = request.query; - -// const stats = await getTransformationStats({ -// ...filters, -// orderBy: order && orderBy ? { order, column: orderBy } : undefined, -// }); -// response.status(200).send(stats); -// } -// ); -// }; diff --git a/server/src/modules/data/routes/regions.routes.ts b/server/src/modules/data/routes/regions.routes.ts deleted file mode 100644 index 3ab0874c1..000000000 --- a/server/src/modules/data/routes/regions.routes.ts +++ /dev/null @@ -1,29 +0,0 @@ -// import { ROUTES_CONFIG } from "shared"; - -// import { Server } from "../../../server"; -// import { getRegions } from "../queries/getRegions/getRegions.query"; -// import { getRegionStats } from "../queries/getRegionStats/getRegionStats.query"; - -// export const regionsRoutes = ({ server }: { server: Server }) => { -// server.get( -// "/regions", -// { schema: ROUTES_CONFIG.getRegions }, -// async (_, response) => { -// const regions = await getRegions(); -// response.status(200).send(regions); -// } -// ); - -// server.get( -// "/region/:codeRegion", -// { schema: ROUTES_CONFIG.getRegionStats }, -// async (request, response) => { -// const regionStats = await getRegionStats({ -// ...request.params, -// ...request.query, -// }); -// if (!regionStats) return response.status(404).send(); -// response.status(200).send(regionStats); -// } -// ); -// }; diff --git a/server/src/modules/data/routes/restitutionIntentions.routes.ts b/server/src/modules/data/routes/restitutionIntentions.routes.ts deleted file mode 100644 index c3b8bb66c..000000000 --- a/server/src/modules/data/routes/restitutionIntentions.routes.ts +++ /dev/null @@ -1,46 +0,0 @@ -// import Boom from "@hapi/boom"; -// import { ROUTES_CONFIG } from "shared"; - -// import { Server } from "../../../server"; -// import { hasPermissionHandler } from "../../core"; -// import { getCountRestitutionIntentionsStats } from "../usecases/countRestitutionIntentionsStats/countRestitutionIntentionsStats.usecase"; -// import { getRestitutionIntentionsStats } from "../usecases/getRestitutionIntentionsStats/getRestitutionIntentionsStats.usecase"; - -// export const restitutionIntentionsRoutes = ({ server }: { server: Server }) => { -// server.get( -// "/intentions/stats", -// { -// schema: ROUTES_CONFIG.getRestitutionIntentionsStats, -// preHandler: hasPermissionHandler("intentions/lecture"), -// }, -// async (request, response) => { -// const { order, orderBy, ...rest } = request.query; -// if (!request.user) throw Boom.forbidden(); - -// const result = await getRestitutionIntentionsStats({ -// ...rest, -// orderBy: order && orderBy ? { order, column: orderBy } : undefined, -// user: request.user, -// }); -// response.status(200).send(result); -// } -// ); - -// server.get( -// "/intentions/stats/count", -// { -// schema: ROUTES_CONFIG.countRestitutionIntentionsStats, -// preHandler: hasPermissionHandler("intentions/lecture"), -// }, -// async (request, response) => { -// const { ...filters } = request.query; -// if (!request.user) throw Boom.forbidden(); - -// const result = await getCountRestitutionIntentionsStats({ -// ...filters, -// user: request.user, -// }); -// response.status(200).send(result); -// } -// ); -// }; diff --git a/shared/client/ROUTES_CONFIG.ts b/shared/client/ROUTES_CONFIG.ts deleted file mode 100644 index 8ad979e24..000000000 --- a/shared/client/ROUTES_CONFIG.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { etablissementSchemas } from "./etablissements/etablissements.schema"; -import { formationSchemas } from "./formations/formation.schema"; -import { pilotageReformeSchemas } from "./pilotageReforme/pilotageReforme.schema"; -import { pilotageTransformationSchemas } from "./pilotageTransfo/pilotageTransfo.schema"; -import { restitutionIntentionsSchemas } from "./restitutionIntentions/restitutionIntentions.schema"; - -export const ROUTES_CONFIG = { - ...formationSchemas, - ...etablissementSchemas, - ...pilotageReformeSchemas, - ...pilotageTransformationSchemas, - ...restitutionIntentionsSchemas, -}; diff --git a/shared/client/client.ts b/shared/client/client.ts deleted file mode 100644 index 07aedb3b3..000000000 --- a/shared/client/client.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createEtablissementClient } from "./etablissements/etablissements.client"; -import { createFormationClient } from "./formations/formation.client"; -import { createPilotageReformeClient } from "./pilotageReforme/pilotageReforme.client"; -import { createPilotageTransformationClient } from "./pilotageTransfo/pilotageTransfo.client"; -import { createRestitutionIntentionsClient } from "./restitutionIntentions/restitutionIntentions.client"; - -export const createClient = (instance: AxiosInstance) => ({ - ...createFormationClient(instance), - ...createEtablissementClient(instance), - ...createPilotageReformeClient(instance), - ...createPilotageTransformationClient(instance), - ...createRestitutionIntentionsClient(instance), -}); diff --git a/shared/client/clientFactory.ts b/shared/client/clientFactory.ts deleted file mode 100644 index a25fb2da4..000000000 --- a/shared/client/clientFactory.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Static, TSchema } from "@sinclair/typebox"; -import { AxiosInstance, AxiosRequestConfig } from "axios"; - -import { ROUTES_CONFIG } from "./ROUTES_CONFIG"; - -export type ParamsFromSchema = S extends { - params: TSchema; -} - ? Static - : undefined; - -export type Params< - S extends (typeof ROUTES_CONFIG)[keyof typeof ROUTES_CONFIG], -> = ParamsFromSchema; - -export type QueryFromSchema = S extends { - querystring: TSchema; -} - ? Static - : undefined; -export type Query< - S extends (typeof ROUTES_CONFIG)[keyof typeof ROUTES_CONFIG], -> = QueryFromSchema; - -export type BodyFromSchema = S extends { - body: TSchema; -} - ? Static - : undefined; - -export type Body = - BodyFromSchema; - -export type Args = - Record & - (Body extends undefined ? { body?: undefined } : { body: Body }) & - (Query extends undefined ? { query?: undefined } : { query: Query }) & - (Params extends undefined - ? { params?: undefined } - : { params: Params }); - -export type ResponseFromSchema = S extends { - response: { 200: TSchema }; -} - ? Static - : undefined; - -export type Response< - S extends (typeof ROUTES_CONFIG)[keyof typeof ROUTES_CONFIG], -> = ResponseFromSchema; - -export const callApi = async < - S extends (typeof ROUTES_CONFIG)[keyof typeof ROUTES_CONFIG], ->({ - config, - method, - url, - instance, - ...props -}: { - method: AxiosRequestConfig["method"]; - url: string; - config?: AxiosRequestConfig; - instance: AxiosInstance; -} & Args) => { - const { data } = await instance.request({ - method, - url, - ...config, - params: props.query, - data: props.body, - }); - return data as Response; -}; - -export const createClientMethod = - ({ - method, - url, - instance, - }: { - method: AxiosRequestConfig["method"]; - url: string | (({ params }: { params: Args["params"] }) => string); - instance: AxiosInstance; - }) => - (args: Args, config?: AxiosRequestConfig) => { - const processedUrl = - typeof url === "string" - ? url - : url({ params: args.params as Args["params"] }); - return { - call: () => - callApi({ - method, - url: processedUrl, - config, - instance, - ...args, - }), - url: instance.getUri({ - method, - url: processedUrl, - params: args.query, - data: args.body, - ...config, - }), - }; - }; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type ApiType any> = Awaited< - ReturnType["call"]> ->; diff --git a/shared/client/etablissements/etablissements.client.ts b/shared/client/etablissements/etablissements.client.ts deleted file mode 100644 index 4bcad3e19..000000000 --- a/shared/client/etablissements/etablissements.client.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createEtablissementClient = (instance: AxiosInstance) => ({ - getEtablissements: createClientMethod( - { - method: "GET", - url: "/etablissements", - instance, - } - ), - getEtablissement: createClientMethod({ - method: "GET", - url: ({ params }) => `/etablissement/${params.uai}`, - instance, - }), - getRegionStats: createClientMethod({ - method: "GET", - url: ({ params }) => `/region/${params.codeRegion}`, - instance, - }), - getDepartementStats: createClientMethod< - typeof ROUTES_CONFIG.getDepartementStats - >({ - method: "GET", - url: ({ params }) => `/departement/${params.codeDepartement}`, - instance, - }), -}); diff --git a/shared/client/etablissements/etablissements.schema.ts b/shared/client/etablissements/etablissements.schema.ts deleted file mode 100644 index ceb9edb66..000000000 --- a/shared/client/etablissements/etablissements.schema.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const OptionSchema = Type.Object({ - label: Type.String(), - value: Type.String(), -}); - -const EtablissementLineSchema = Type.Object({ - libelleEtablissement: Type.Optional(Type.String()), - UAI: Type.String(), - rentreeScolaire: Type.Optional(Type.String()), - secteur: Type.Optional(Type.String()), - commune: Type.Optional(Type.String()), - departement: Type.Optional(Type.String()), - codeFormationDiplome: Type.String(), - libelleDiplome: Type.String(), - codeNiveauDiplome: Type.String(), - libelleOfficielFamille: Type.Optional(Type.String()), - dispositifId: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - libelleNiveauDiplome: Type.Optional(Type.String()), - anneeDebut: Type.Optional(Type.Number()), - capacite: Type.Optional(Type.Number()), - effectif: Type.Optional(Type.Number()), - effectif1: Type.Optional(Type.Number()), - effectif2: Type.Optional(Type.Number()), - effectif3: Type.Optional(Type.Number()), - tauxPression: Type.Optional(Type.Number()), - tauxRemplissage: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Optional(Type.Number()), - tauxDevenirFavorable: Type.Optional(Type.Number()), - valeurAjoutee: Type.Optional(Type.Number()), - CPC: Type.Optional(Type.String()), - CPCSecteur: Type.Optional(Type.String()), - CPCSousSecteur: Type.Optional(Type.String()), - libelleFiliere: Type.Optional(Type.String()), - continuum: Type.Optional( - Type.Object({ - cfd: Type.String(), - libelle: Type.Optional(Type.String()), - }) - ), -}); - -const FiltersSchema = Type.Object({ - cfd: Type.Optional(Type.Array(Type.String())), - codeRegion: Type.Optional(Type.Array(Type.String())), - codeAcademie: Type.Optional(Type.Array(Type.String())), - codeDepartement: Type.Optional(Type.Array(Type.String())), - commune: Type.Optional(Type.Array(Type.String())), - codeDiplome: Type.Optional(Type.Array(Type.String())), - codeDispositif: Type.Optional(Type.Array(Type.String())), - cfdFamille: Type.Optional(Type.Array(Type.String())), - rentreeScolaire: Type.Optional(Type.Array(Type.String())), - secteur: Type.Optional(Type.Array(Type.String())), - uai: Type.Optional(Type.Array(Type.String())), - CPC: Type.Optional(Type.Array(Type.String())), - CPCSecteur: Type.Optional(Type.Array(Type.String())), - CPCSousSecteur: Type.Optional(Type.Array(Type.String())), - libelleFiliere: Type.Optional(Type.Array(Type.String())), - order: Type.Optional(Type.Union([Type.Literal("asc"), Type.Literal("desc")])), - orderBy: Type.Optional(Type.KeyOf(Type.Omit(EtablissementLineSchema, []))), -}); - -export const etablissementSchemas = { - getEtablissements: { - querystring: Type.Intersect([ - FiltersSchema, - Type.Object({ - offset: Type.Optional(Type.Number()), - limit: Type.Optional(Type.Number()), - }), - ]), - response: { - 200: Type.Object({ - count: Type.Number(), - filters: Type.Object({ - regions: Type.Array(OptionSchema), - academies: Type.Array(OptionSchema), - departements: Type.Array(OptionSchema), - communes: Type.Array(OptionSchema), - diplomes: Type.Array(OptionSchema), - dispositifs: Type.Array(OptionSchema), - familles: Type.Array(OptionSchema), - formations: Type.Array(OptionSchema), - etablissements: Type.Array(OptionSchema), - CPCs: Type.Array(OptionSchema), - CPCSecteurs: Type.Array(OptionSchema), - CPCSousSecteurs: Type.Array(OptionSchema), - libelleFilieres: Type.Array(OptionSchema), - }), - etablissements: Type.Array(EtablissementLineSchema), - }), - }, - }, - getEtablissement: { - params: Type.Object({ uai: Type.String() }), - response: { - 200: Type.Object({ - uai: Type.String(), - rentreeScolaire: Type.String(), - libelleEtablissement: Type.Optional(Type.String()), - valeurAjoutee: Type.Optional(Type.Number()), - codeRegion: Type.Optional(Type.String()), - libelleRegion: Type.Optional(Type.String()), - formations: Type.Array( - Type.Object({ - cfd: Type.String(), - codeNiveauDiplome: Type.String(), - libelleDiplome: Type.String(), - dispositifId: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - libelleNiveauDiplome: Type.Optional(Type.String()), - effectif: Type.Optional(Type.Number()), - tauxPression: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Optional(Type.Number()), - tauxDevenirFavorable: Type.Optional(Type.Number()), - positionCadran: Type.Optional(Type.String()), - CPC: Type.Optional(Type.String()), - CPCSecteur: Type.Optional(Type.String()), - CPCSousSecteur: Type.Optional(Type.String()), - libelleFiliere: Type.Optional(Type.String()), - continuum: Type.Optional( - Type.Object({ - cfd: Type.String(), - libelle: Type.Optional(Type.String()), - }) - ), - }) - ), - }), - }, - }, - getRegionStats: { - params: Type.Object({ - codeRegion: Type.String(), - }), - querystring: Type.Object({ - codeDiplome: Type.Optional(Type.Array(Type.String())), - }), - response: { - 200: Type.Object({ - libelleRegion: Type.String(), - effectif: Type.Number(), - nbFormations: Type.Number(), - tauxPression: Type.Optional(Type.Number()), - tauxRemplissage: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Optional(Type.Number()), - }), - }, - }, - getDepartementStats: { - params: Type.Object({ - codeDepartement: Type.String(), - }), - querystring: Type.Object({ - codeDiplome: Type.Optional(Type.Array(Type.String())), - }), - response: { - 200: Type.Object({ - codeRegion: Type.String(), - libelleDepartement: Type.String(), - effectif: Type.Number(), - nbFormations: Type.Number(), - tauxPression: Type.Optional(Type.Number()), - tauxRemplissage: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Optional(Type.Number()), - }), - }, - }, -} as const; diff --git a/shared/client/formations/formation.client.ts b/shared/client/formations/formation.client.ts deleted file mode 100644 index 6de4b73c6..000000000 --- a/shared/client/formations/formation.client.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createFormationClient = (instance: AxiosInstance) => ({ - getFormations: createClientMethod({ - method: "GET", - url: "/formations", - instance, - }), - getDataForPanoramaRegion: createClientMethod< - typeof ROUTES_CONFIG.getDataForPanoramaRegion - >({ - method: "GET", - url: "/panorama/stats/region", - instance, - }), - getDataForPanoramaDepartement: createClientMethod< - typeof ROUTES_CONFIG.getDataForPanoramaDepartement - >({ - method: "GET", - url: "/panorama/stats/departement", - instance, - }), - getRegions: createClientMethod({ - method: "GET", - url: "/regions", - instance, - }), - getDepartements: createClientMethod({ - method: "GET", - url: "/departements", - instance, - }), -}); diff --git a/shared/client/formations/formation.schema.ts b/shared/client/formations/formation.schema.ts deleted file mode 100644 index 252ffaf76..000000000 --- a/shared/client/formations/formation.schema.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const OptionSchema = Type.Object({ - label: Type.String(), - value: Type.String(), -}); - -const FormationLineSchema = Type.Object({ - codeFormationDiplome: Type.String(), - libelleDiplome: Type.String(), - rentreeScolaire: Type.Optional(Type.String()), - codeNiveauDiplome: Type.String(), - libelleOfficielFamille: Type.Optional(Type.String()), - dispositifId: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - libelleNiveauDiplome: Type.Optional(Type.String()), - nbEtablissement: Type.Optional(Type.Number()), - anneeDebut: Type.Optional(Type.Number()), - effectif: Type.Optional(Type.Number()), - effectif1: Type.Optional(Type.Number()), - effectif2: Type.Optional(Type.Number()), - effectif3: Type.Optional(Type.Number()), - tauxRemplissage: Type.Optional(Type.Number()), - tauxPression: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Optional(Type.Number()), - tauxDevenirFavorable: Type.Optional(Type.Number()), - CPC: Type.Optional(Type.String()), - CPCSecteur: Type.Optional(Type.String()), - CPCSousSecteur: Type.Optional(Type.String()), - libelleFiliere: Type.Optional(Type.String()), - continuum: Type.Optional( - Type.Object({ - cfd: Type.String(), - libelle: Type.Optional(Type.String()), - }) - ), - positionCadran: Type.Optional(Type.String()), -}); - -const FiltersSchema = Type.Object({ - cfd: Type.Optional(Type.Array(Type.String())), - codeRegion: Type.Optional(Type.Array(Type.String())), - codeAcademie: Type.Optional(Type.Array(Type.String())), - codeDepartement: Type.Optional(Type.Array(Type.String())), - commune: Type.Optional(Type.Array(Type.String())), - codeDiplome: Type.Optional(Type.Array(Type.String())), - codeDispositif: Type.Optional(Type.Array(Type.String())), - cfdFamille: Type.Optional(Type.Array(Type.String())), - rentreeScolaire: Type.Optional(Type.Array(Type.String())), - CPC: Type.Optional(Type.Array(Type.String())), - CPCSecteur: Type.Optional(Type.Array(Type.String())), - CPCSousSecteur: Type.Optional(Type.Array(Type.String())), - libelleFiliere: Type.Optional(Type.Array(Type.String())), - order: Type.Optional(Type.Union([Type.Literal("asc"), Type.Literal("desc")])), - orderBy: Type.Optional(Type.KeyOf(FormationLineSchema)), - withEmptyFormations: Type.Optional(Type.Boolean()), -}); - -const FormationSchema = Type.Object({ - codeFormationDiplome: Type.String(), - libelleDiplome: Type.String(), - codeNiveauDiplome: Type.String(), - libelleNiveauDiplome: Type.Optional(Type.String()), - dispositifId: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - nbEtablissement: Type.Number(), - effectif: Type.Optional(Type.Number()), - effectifPrecedent: Type.Optional(Type.Number()), - tauxRemplissage: Type.Optional(Type.Number()), - tauxPression: Type.Optional(Type.Number()), - tauxInsertion6mois: Type.Number(), - tauxInsertion6moisPrecedent: Type.Optional(Type.Number()), - tauxPoursuiteEtudes: Type.Number(), - tauxPoursuiteEtudesPrecedent: Type.Optional(Type.Number()), - tauxDevenirFavorable: Type.Number(), - positionCadran: Type.Optional(Type.String()), - CPC: Type.Optional(Type.String()), - CPCSecteur: Type.Optional(Type.String()), - CPCSousSecteur: Type.Optional(Type.String()), - libelleFiliere: Type.Optional(Type.String()), - continuum: Type.Optional( - Type.Object({ - cfd: Type.String(), - libelle: Type.Optional(Type.String()), - }) - ), -}); - -export const formationSchemas = { - getFormations: { - querystring: Type.Intersect([ - FiltersSchema, - Type.Object({ - offset: Type.Optional(Type.Number()), - limit: Type.Optional(Type.Number()), - }), - ]), - response: { - 200: Type.Object({ - count: Type.Number(), - filters: Type.Object({ - regions: Type.Array(OptionSchema), - academies: Type.Array(OptionSchema), - departements: Type.Array(OptionSchema), - communes: Type.Array(OptionSchema), - diplomes: Type.Array(OptionSchema), - dispositifs: Type.Array(OptionSchema), - familles: Type.Array(OptionSchema), - formations: Type.Array(OptionSchema), - CPCs: Type.Array(OptionSchema), - CPCSecteurs: Type.Array(OptionSchema), - CPCSousSecteurs: Type.Array(OptionSchema), - libelleFilieres: Type.Array(OptionSchema), - }), - formations: Type.Array(FormationLineSchema), - }), - }, - }, - getDataForPanoramaRegion: { - querystring: Type.Object({ - codeRegion: Type.String(), - }), - response: { - 200: Type.Object({ - formations: Type.Array(FormationSchema), - }), - }, - }, - getDataForPanoramaDepartement: { - querystring: Type.Object({ - codeDepartement: Type.String(), - }), - response: { - 200: Type.Object({ - formations: Type.Array(FormationSchema), - }), - }, - }, - getRegions: { - response: { - 200: Type.Array(OptionSchema), - }, - }, - getDepartements: { - response: { - 200: Type.Array(OptionSchema), - }, - }, -} as const; diff --git a/shared/client/pilotageReforme/pilotageReforme.client.ts b/shared/client/pilotageReforme/pilotageReforme.client.ts deleted file mode 100644 index 84d475e62..000000000 --- a/shared/client/pilotageReforme/pilotageReforme.client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createPilotageReformeClient = (instance: AxiosInstance) => ({ - getPilotageReformeStats: createClientMethod< - typeof ROUTES_CONFIG.getPilotageReformeStats - >({ - method: "GET", - url: "/pilotage-reforme/stats", - instance, - }), - getPilotageReformeStatsRegions: createClientMethod< - typeof ROUTES_CONFIG.getPilotageReformeStatsRegions - >({ - method: "GET", - url: "/pilotage-reforme/stats/regions", - instance, - }), -}); diff --git a/shared/client/pilotageReforme/pilotageReforme.schema.ts b/shared/client/pilotageReforme/pilotageReforme.schema.ts deleted file mode 100644 index 92508c536..000000000 --- a/shared/client/pilotageReforme/pilotageReforme.schema.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const OptionSchema = Type.Object({ - label: Type.String(), - value: Type.String(), -}); - -const StatsSchema = Type.Object({ - effectif: Type.Optional(Type.Number()), - nbFormations: Type.Optional(Type.Number()), - nbEtablissements: Type.Optional(Type.Number()), - poursuite: Type.Optional(Type.Number()), - insertion: Type.Optional(Type.Number()), -}); - -const StatsAnneeSchema = Type.Object({ - nationale: StatsSchema, - filtered: StatsSchema, -}); - -const StatsRegionLineSchema = Type.Object({ - codeRegion: Type.String(), - libelleRegion: Type.Optional(Type.String()), - poursuite: Type.Optional(Type.Number()), - insertion: Type.Optional(Type.Number()), -}); - -const FiltersSchema = Type.Object({ - codeNiveauDiplome: Type.Optional(Type.Array(Type.String())), - codeRegion: Type.Optional(Type.String()), -}); - -const FiltersRegionsSchema = Type.Object({ - codeNiveauDiplome: Type.Optional(Type.Array(Type.String())), - order: Type.Optional(Type.Union([Type.Literal("asc"), Type.Literal("desc")])), - orderBy: Type.Optional(Type.KeyOf(Type.Omit(StatsRegionLineSchema, []))), -}); - -export const pilotageReformeSchemas = { - getPilotageReformeStats: { - querystring: FiltersSchema, - response: { - 200: Type.Object({ - filters: Type.Object({ - regions: Type.Array(OptionSchema), - diplomes: Type.Array(OptionSchema), - }), - anneeN: StatsAnneeSchema, - anneeNMoins1: StatsAnneeSchema, - }), - }, - }, - getPilotageReformeStatsRegions: { - querystring: FiltersRegionsSchema, - response: { - 200: Type.Object({ - filters: Type.Object({ - diplomes: Type.Array(OptionSchema), - }), - statsRegions: Type.Array(StatsRegionLineSchema), - }), - }, - }, -} as const; diff --git a/shared/client/pilotageTransfo/pilotageTransfo.client.ts b/shared/client/pilotageTransfo/pilotageTransfo.client.ts deleted file mode 100644 index 9e9a10257..000000000 --- a/shared/client/pilotageTransfo/pilotageTransfo.client.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createPilotageTransformationClient = ( - instance: AxiosInstance -) => ({ - getTransformationStats: createClientMethod< - typeof ROUTES_CONFIG.getTransformationStats - >({ - method: "GET", - url: "/pilotage-transformation/stats", - instance, - }), - getFormationsTransformationStats: createClientMethod< - typeof ROUTES_CONFIG.getFormationsTransformationStats - >({ - method: "GET", - url: "/pilotage-transformation/formations", - instance, - }), -}); diff --git a/shared/client/pilotageTransfo/pilotageTransfo.schema.ts b/shared/client/pilotageTransfo/pilotageTransfo.schema.ts deleted file mode 100644 index afe5be353..000000000 --- a/shared/client/pilotageTransfo/pilotageTransfo.schema.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const OptionSchema = Type.Object({ - label: Type.String(), - value: Type.String(), -}); - -const ScopedStatsTransfoSchema = Type.Object({ - libelle: Type.Optional(Type.String()), - libelleAcademie: Type.Optional(Type.String()), - libelleRegion: Type.Optional(Type.String()), - countDemande: Type.Number(), - differenceCapaciteScolaire: Type.Number(), - differenceCapaciteApprentissage: Type.Number(), - placesTransformees: Type.Number(), - placesOuvertesScolaire: Type.Number(), - placesOuvertesApprentissage: Type.Number(), - placesOuvertes: Type.Number(), - placesFermeesScolaire: Type.Number(), - placesFermeesApprentissage: Type.Number(), - placesFermees: Type.Number(), - ratioOuverture: Type.Number(), - ratioFermeture: Type.Number(), - tauxTransformation: Type.Number(), -}); - -const StatsTransfoSchema = Type.Object({ - national: Type.Object(ScopedStatsTransfoSchema.properties), - regions: Type.Record( - Type.String(), - Type.Object({ - ...ScopedStatsTransfoSchema.properties, - codeRegion: Type.Optional(Type.String()), - }) - ), - academies: Type.Record( - Type.String(), - Type.Object({ - ...ScopedStatsTransfoSchema.properties, - codeAcademie: Type.Optional(Type.String()), - }) - ), - departements: Type.Record( - Type.String(), - Type.Object({ - ...ScopedStatsTransfoSchema.properties, - codeDepartement: Type.Optional(Type.String()), - }) - ), -}); - -const StatsFiltersSchema = Type.Object({ - rentreeScolaire: Type.Optional(Type.String()), - codeNiveauDiplome: Type.Optional(Type.Array(Type.String())), - filiere: Type.Optional(Type.Array(Type.String())), -}); - -const FormationTransformationStatsSchema = Type.Object({ - libelleDiplome: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - tauxInsertion: Type.Number(), - tauxPoursuite: Type.Number(), - tauxPression: Type.Optional(Type.Number()), - dispositifId: Type.Optional(Type.String()), - cfd: Type.String(), - nbDemandes: Type.Number(), - nbEtablissements: Type.Number(), - differencePlaces: Type.Number(), - placesOuvertes: Type.Number(), - placesFermees: Type.Number(), - placesTransformees: Type.Number(), - positionCadran: Type.Optional(Type.String()), - continuum: Type.Optional( - Type.Object({ - cfd: Type.String(), - libelle: Type.Optional(Type.String()), - }) - ), -}); - -const FormationsTransformationStatsFiltersSchema = Type.Object({ - rentreeScolaire: Type.Optional(Type.String()), - codeNiveauDiplome: Type.Optional(Type.Array(Type.String())), - filiere: Type.Optional(Type.Array(Type.String())), -}); - -export const pilotageTransformationSchemas = { - getTransformationStats: { - querystring: Type.Intersect([ - StatsFiltersSchema, - Type.Object({ - order: Type.Optional( - Type.Union([Type.Literal("asc"), Type.Literal("desc")]) - ), - orderBy: Type.Optional( - Type.KeyOf(Type.Omit(ScopedStatsTransfoSchema, [])) - ), - }), - ]), - response: { - 200: Type.Object({ - submitted: StatsTransfoSchema, - draft: StatsTransfoSchema, - all: StatsTransfoSchema, - filters: Type.Object({ - rentreesScolaires: Type.Array(OptionSchema), - regions: Type.Array(OptionSchema), - academies: Type.Array(OptionSchema), - departements: Type.Array(OptionSchema), - filieres: Type.Array(OptionSchema), - diplomes: Type.Array(OptionSchema), - }), - }), - }, - }, - getFormationsTransformationStats: { - querystring: Type.Intersect([ - FormationsTransformationStatsFiltersSchema, - Type.Object({ - status: Type.Optional( - Type.Union([Type.Literal("draft"), Type.Literal("submitted")]) - ), - codeRegion: Type.Optional(Type.String()), - codeAcademie: Type.Optional(Type.String()), - codeDepartement: Type.Optional(Type.String()), - type: Type.Optional( - Type.Union([Type.Literal("ouverture"), Type.Literal("fermeture")]) - ), - tauxPression: Type.Optional( - Type.Union([Type.Literal("eleve"), Type.Literal("faible")]) - ), - }), - Type.Object({ - order: Type.Optional( - Type.Union([Type.Literal("asc"), Type.Literal("desc")]) - ), - orderBy: Type.Optional( - Type.KeyOf(Type.Omit(FormationTransformationStatsSchema, [])) - ), - }), - ]), - response: { - 200: Type.Object({ - stats: Type.Object({ - tauxInsertion: Type.Number(), - tauxPoursuite: Type.Number(), - }), - formations: Type.Array(FormationTransformationStatsSchema), - }), - }, - }, -} as const; diff --git a/shared/client/restitutionIntentions/restitutionIntentions.client.ts b/shared/client/restitutionIntentions/restitutionIntentions.client.ts deleted file mode 100644 index f07afef9a..000000000 --- a/shared/client/restitutionIntentions/restitutionIntentions.client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { AxiosInstance } from "axios"; - -import { createClientMethod } from "../clientFactory"; -import { ROUTES_CONFIG } from "../ROUTES_CONFIG"; - -export const createRestitutionIntentionsClient = (instance: AxiosInstance) => ({ - getRestitutionIntentionsStats: createClientMethod< - typeof ROUTES_CONFIG.getRestitutionIntentionsStats - >({ - method: "GET", - url: "/intentions/stats", - instance, - }), - countRestitutionIntentionsStats: createClientMethod< - typeof ROUTES_CONFIG.countRestitutionIntentionsStats - >({ - method: "GET", - url: "/intentions/stats/count", - instance, - }), -}); diff --git a/shared/client/restitutionIntentions/restitutionIntentions.schema.ts b/shared/client/restitutionIntentions/restitutionIntentions.schema.ts deleted file mode 100644 index 5732f09a5..000000000 --- a/shared/client/restitutionIntentions/restitutionIntentions.schema.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { Type } from "@sinclair/typebox"; - -const OptionSchema = Type.Object({ - label: Type.String(), - value: Type.String(), -}); - -const StatsDemandesItem = Type.Object({ - id: Type.String(), - cfd: Type.Optional(Type.String()), - libelleDiplome: Type.Optional(Type.String()), - dispositifId: Type.Optional(Type.String()), - libelleDispositif: Type.Optional(Type.String()), - niveauDiplome: Type.Optional(Type.String()), - uai: Type.Optional(Type.String()), - libelleEtablissement: Type.Optional(Type.String()), - commune: Type.Optional(Type.String()), - rentreeScolaire: Type.Optional(Type.Number()), - typeDemande: Type.Optional(Type.String()), - motif: Type.Optional(Type.Array(Type.String())), - autreMotif: Type.Optional(Type.String()), - coloration: Type.Optional(Type.Boolean()), - libelleColoration: Type.Optional(Type.String()), - libelleFCIL: Type.Optional(Type.String()), - amiCma: Type.Optional(Type.Boolean()), - poursuitePedagogique: Type.Optional(Type.Boolean()), - commentaire: Type.Optional(Type.String()), - libelleFiliere: Type.Optional(Type.String()), - status: Type.String(), - codeRegion: Type.Optional(Type.String()), - libelleRegion: Type.Optional(Type.String()), - codeAcademie: Type.Optional(Type.String()), - codeDepartement: Type.Optional(Type.String()), - libelleDepartement: Type.Optional(Type.String()), - createdAt: Type.String(), - compensationCfd: Type.Optional(Type.String()), - compensationDispositifId: Type.Optional(Type.String()), - compensationUai: Type.Optional(Type.String()), - differenceCapaciteScolaire: Type.Optional(Type.Number()), - capaciteScolaireActuelle: Type.Optional(Type.Number()), - capaciteScolaire: Type.Optional(Type.Number()), - capaciteScolaireColoree: Type.Optional(Type.Number()), - differenceCapaciteApprentissage: Type.Optional(Type.Number()), - capaciteApprentissageActuelle: Type.Optional(Type.Number()), - capaciteApprentissage: Type.Optional(Type.Number()), - capaciteApprentissageColoree: Type.Optional(Type.Number()), - insertion: Type.Optional(Type.Number()), - poursuite: Type.Optional(Type.Number()), - devenirFavorable: Type.Optional(Type.Number()), - pression: Type.Optional(Type.Number()), - nbEtablissement: Type.Optional(Type.Number()), - positionCadran: Type.Optional(Type.String()), - tauxInsertionMoyen: Type.Optional(Type.Number()), - tauxPoursuiteMoyen: Type.Optional(Type.Number()), -}); - -const StatsFiltersSchema = Type.Object({ - codeRegion: Type.Optional(Type.Array(Type.String())), - codeAcademie: Type.Optional(Type.Array(Type.String())), - codeDepartement: Type.Optional(Type.Array(Type.String())), - commune: Type.Optional(Type.Array(Type.String())), - uai: Type.Optional(Type.Array(Type.String())), - rentreeScolaire: Type.Optional(Type.String()), - typeDemande: Type.Optional(Type.Array(Type.String())), - motif: Type.Optional(Type.Array(Type.String())), - status: Type.Optional( - Type.Union([ - Type.Literal("draft"), - Type.Literal("submitted"), - Type.Undefined(), - ]) - ), - codeNiveauDiplome: Type.Optional(Type.Array(Type.String())), - cfd: Type.Optional(Type.Array(Type.String())), - dispositif: Type.Optional(Type.Array(Type.String())), - filiere: Type.Optional(Type.Array(Type.String())), - cfdFamille: Type.Optional(Type.Array(Type.String())), - coloration: Type.Optional(Type.String()), - amiCMA: Type.Optional(Type.String()), - secteur: Type.Optional(Type.String()), - compensation: Type.Optional(Type.String()), - positionCadran: Type.Optional(Type.String()), - order: Type.Optional(Type.Union([Type.Literal("asc"), Type.Literal("desc")])), - orderBy: Type.Optional(Type.KeyOf(Type.Omit(StatsDemandesItem, []))), -}); - -const CountCapaciteStatsDemandesSchema = Type.Object({ - total: Type.Number(), - scolaire: Type.Number(), - apprentissage: Type.Number(), - coloration: Type.Optional(Type.Number()), -}); - -export const restitutionIntentionsSchemas = { - getRestitutionIntentionsStats: { - querystring: Type.Intersect([ - StatsFiltersSchema, - Type.Object({ - offset: Type.Optional(Type.Number()), - limit: Type.Optional(Type.Number()), - }), - ]), - response: { - 200: Type.Object({ - filters: Type.Object({ - rentreesScolaires: Type.Array(OptionSchema), - statuts: Type.Array(OptionSchema), - regions: Type.Array(OptionSchema), - academies: Type.Array(OptionSchema), - departements: Type.Array(OptionSchema), - communes: Type.Array(OptionSchema), - etablissements: Type.Array(OptionSchema), - typesDemande: Type.Array(OptionSchema), - motifs: Type.Array(OptionSchema), - status: Type.Array(OptionSchema), - diplomes: Type.Array(OptionSchema), - formations: Type.Array(OptionSchema), - filieres: Type.Array(OptionSchema), - familles: Type.Array(OptionSchema), - dispositifs: Type.Array(OptionSchema), - secteurs: Type.Array(OptionSchema), - amiCMAs: Type.Array(OptionSchema), - colorations: Type.Array(OptionSchema), - compensations: Type.Array(OptionSchema), - }), - demandes: Type.Array(StatsDemandesItem), - count: Type.Number(), - }), - }, - }, - countRestitutionIntentionsStats: { - querystring: StatsFiltersSchema, - response: { - 200: Type.Object({ - total: CountCapaciteStatsDemandesSchema, - ouvertures: CountCapaciteStatsDemandesSchema, - fermetures: CountCapaciteStatsDemandesSchema, - amiCMAs: CountCapaciteStatsDemandesSchema, - FCILs: CountCapaciteStatsDemandesSchema, - }), - }, - }, -} as const; diff --git a/shared/client/utils.ts b/shared/client/utils.ts deleted file mode 100644 index c945d0bd4..000000000 --- a/shared/client/utils.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ObjectPropertyKeys, TObject, Type } from "@sinclair/typebox"; - -export const Partial = >( - schema: S, - keys: K[] -) => { - return Type.Intersect([ - Type.Omit(schema, keys), - Type.Partial(Type.Pick(schema, keys)), - ]); -}; - -export const Required = >( - schema: S, - keys: K[] -) => { - return Type.Intersect([ - Type.Omit(schema, keys), - Type.Required(Type.Pick(schema, keys)), - ]); -}; diff --git a/shared/index.ts b/shared/index.ts index 544f961f7..d7c027df9 100644 --- a/shared/index.ts +++ b/shared/index.ts @@ -2,9 +2,6 @@ export { demandeValidators } from "./demandeValidators/validators"; export * from "./security/securityUtils"; export * from "./security/permissions"; -export { ROUTES_CONFIG } from "./client/ROUTES_CONFIG"; -export { createClient } from "./client/client"; -export type { ApiType } from "./client/clientFactory"; export const salut = "salut"; export { emailRegex } from "./utils/emailRegex"; export { passwordRegex } from "./utils/passwordRegex"; From 4131d2fd70a802045e194c49e6489d79a7e90d13 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 21 Nov 2023 15:01:01 +0100 Subject: [PATCH 20/20] wip --- server/src/index.ts | 12 ++++++++++++ .../findManyInDataFormationQuery.dep.ts | 6 ++++-- .../usecases/searchDiplome/searchDiplome.schema.ts | 4 ++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/src/index.ts b/server/src/index.ts index aa813522d..3840dc68d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -2,6 +2,7 @@ import fastifyCors from "@fastify/cors"; import fastifySwagger from "@fastify/swagger"; import fastifySwaggerUi from "@fastify/swagger-ui"; import Boom from "@hapi/boom"; +import { ZodError } from "zod"; import { config } from "../config/config"; import { logger, loggerContextPlugin } from "./logger"; @@ -32,6 +33,15 @@ server.register(fastifySwaggerUi, { }); server.setErrorHandler((error, _, reply) => { + if ("details" in error && error.details instanceof ZodError) { + logger.error(error.message, { + error, + details: error.details.errors, + }); + reply.status(500).send({ error: "internal error", statusCode: 500 }); + return; + } + if (Boom.isBoom(error)) { reply .status(error.output.statusCode) @@ -44,6 +54,8 @@ server.setErrorHandler((error, _, reply) => { if (!error.statusCode || error.statusCode >= 500) { logger.error(error.message, { error }); + reply.status(500).send({ error: "internal error", statusCode: 500 }); + return; } if (error.statusCode && error.statusCode < 500) { diff --git a/server/src/modules/intentions/usecases/searchDiplome/findManyInDataFormationQuery.dep.ts b/server/src/modules/intentions/usecases/searchDiplome/findManyInDataFormationQuery.dep.ts index e5c088aeb..02eb14b9a 100644 --- a/server/src/modules/intentions/usecases/searchDiplome/findManyInDataFormationQuery.dep.ts +++ b/server/src/modules/intentions/usecases/searchDiplome/findManyInDataFormationQuery.dep.ts @@ -2,6 +2,7 @@ import { sql } from "kysely"; import { jsonArrayFrom } from "kysely/helpers/postgres"; import { kdb } from "../../../../db/db"; +import { cleanNull } from "../../../../utils/noNull"; export const findManyInDataFormationQuery = async ({ search, @@ -91,7 +92,7 @@ export const findManyInDataFormationQuery = async ({ "481", "581", ])}`.as("isFCIL"), - sql` + sql` case when ${eb.ref("dataFormation.dateFermeture")} is not null then to_char(${eb.ref("dataFormation.dateFermeture")}, 'dd/mm/yyyy') else null @@ -100,7 +101,8 @@ export const findManyInDataFormationQuery = async ({ ]) .distinctOn("dataFormation.cfd") .limit(20) - .execute(); + .execute() + .then(cleanNull); return formations; }; diff --git a/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts index 7549f6a14..3823e694a 100644 --- a/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts +++ b/server/src/modules/intentions/usecases/searchDiplome/searchDiplome.schema.ts @@ -9,9 +9,9 @@ export const searchDiplomeSchema = { z.object({ value: z.string(), label: z.string(), - isSpecialite: z.boolean(), + isSpecialite: z.coerce.boolean(), isFCIL: z.boolean(), - dateFermeture: z.string(), + dateFermeture: z.string().optional(), dispositifs: z .array( z.object({