From d9ad829db303ad13627741df1bb54b0b7a3c5488 Mon Sep 17 00:00:00 2001 From: Moroine Bentefrit Date: Mon, 22 Jul 2024 16:26:18 +0200 Subject: [PATCH] feat: show user mailing-lists --- .../src/modules/server/admin/upload.routes.ts | 15 ++ .../generateOpenapi.test.ts.snap | 18 --- shared/models/bouncer.domain.model.ts | 17 +-- shared/models/bouncer.email.model.ts | 32 ++-- shared/models/catalogue.api.model.ts | 141 ++++++++---------- shared/models/catalogueEmailSiret.model.ts | 16 +- shared/models/deca.model/deca.model.ts | 64 ++++---- shared/models/deca.model/decaHistory.model.ts | 18 +-- .../deca.model/decaImportJobResult.model.ts | 24 ++- .../deca.model/parts/deca.apprenant.part.ts | 42 +++--- .../deca.model/parts/deca.employeur.part.ts | 32 ++-- .../parts/deca.etablissementFormation.part.ts | 8 +- .../deca.model/parts/deca.formation.part.ts | 18 +-- .../parts/deca.organismeFormation.part.ts | 10 +- shared/models/document.model.ts | 80 +++++----- shared/models/documentContent.model.ts | 18 +-- shared/models/emailDenied.model.ts | 16 +- shared/models/events/bal_emails.event.ts | 37 ++--- shared/models/mailingList.model.ts | 72 +++++---- shared/models/organisation.model.ts | 54 ++++--- shared/models/person.model.ts | 38 +++-- shared/models/session.model.ts | 16 +- shared/models/user.model.ts | 48 +++--- shared/routes/auth.routes.ts | 18 +-- shared/routes/common.routes.ts | 86 +++++------ shared/routes/core.routes.ts | 3 +- shared/routes/document.routes.ts | 16 +- shared/routes/emails.routes.ts | 26 ++-- shared/routes/mailingList.routes.ts | 12 +- shared/routes/upload.routes.ts | 25 +++- shared/routes/upload.support.routes.ts | 34 ++--- shared/routes/user.routes.ts | 13 +- shared/routes/v1/deca.routes.ts | 21 ++- shared/routes/v1/organisation.routes.ts | 5 +- ui/app/admin/fichier/import/page.tsx | 16 +- ui/app/admin/fichier/page.tsx | 4 +- .../utilisateurs/[id]/components/UserView.tsx | 11 ++ .../[id]/liste-diffusion/page.tsx | 33 ++++ ui/app/components/breadcrumb/Breadcrumb.tsx | 4 + 39 files changed, 555 insertions(+), 606 deletions(-) create mode 100644 ui/app/admin/utilisateurs/[id]/liste-diffusion/page.tsx diff --git a/server/src/modules/server/admin/upload.routes.ts b/server/src/modules/server/admin/upload.routes.ts index 8e43678e9..8c0ad8071 100644 --- a/server/src/modules/server/admin/upload.routes.ts +++ b/server/src/modules/server/admin/upload.routes.ts @@ -17,6 +17,7 @@ import { updateDocument, uploadFile, } from "../../actions/documents.actions"; +import { findMailingListWithDocument } from "../../actions/mailingLists.actions"; import { Server } from "../server"; const validateFile = (file: MultipartFile) => { @@ -130,6 +131,20 @@ export const uploadAdminRoutes = ({ server }: { server: Server }) => { return response.status(200).send(documents.map(toPublicDocument)); } ); + server.get( + "/admin/mailing-list/:user_id", + { + schema: zRoutes.get["/admin/mailing-list/:user_id"], + onRequest: [server.auth(zRoutes.get["/admin/mailing-list/:user_id"])], + }, + async (request, response) => { + const mailingLists = await findMailingListWithDocument({ + added_by: request.params.user_id.toString(), + }); + + return response.status(200).send(mailingLists); + } + ); server.delete( "/admin/document/:id", diff --git a/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap b/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap index 096d01876..56573b50e 100644 --- a/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap +++ b/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap @@ -78,7 +78,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` }, "schemas": { "Error": { - "additionalProperties": false, "properties": { "code": { "type": [ @@ -158,11 +157,9 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "schema": { "anyOf": [ { - "additionalProperties": false, "description": "Organisation deca Response body", "properties": { "contrats": { - "additionalProperties": false, "properties": { "appr": { "type": "number", @@ -182,7 +179,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, "dernier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -197,7 +193,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` ], }, "premier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -218,7 +213,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, { - "additionalProperties": false, "properties": { "code": { "type": [ @@ -324,7 +318,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, "description": "Organisation deca Request body", "properties": { "siret": { @@ -350,11 +343,9 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "schema": { "anyOf": [ { - "additionalProperties": false, "description": "Organisation deca Response body", "properties": { "contrats": { - "additionalProperties": false, "properties": { "appr": { "type": "number", @@ -374,7 +365,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, "dernier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -389,7 +379,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` ], }, "premier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -410,7 +399,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, { - "additionalProperties": false, "properties": { "code": { "type": [ @@ -520,7 +508,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, "description": "Organisation validation Request body", "properties": { "email": { @@ -551,11 +538,9 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "schema": { "anyOf": [ { - "additionalProperties": false, "description": "Organisation deca Response body", "properties": { "contrats": { - "additionalProperties": false, "properties": { "appr": { "type": "number", @@ -575,7 +560,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, "dernier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -590,7 +574,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` ], }, "premier_contrat": { - "additionalProperties": false, "properties": { "date_debut_contrat": { "type": "string", @@ -611,7 +594,6 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` "type": "object", }, { - "additionalProperties": false, "properties": { "code": { "type": [ diff --git a/shared/models/bouncer.domain.model.ts b/shared/models/bouncer.domain.model.ts index e77babe4f..da1929ac7 100644 --- a/shared/models/bouncer.domain.model.ts +++ b/shared/models/bouncer.domain.model.ts @@ -9,15 +9,14 @@ const collectionName = "bouncer.domain" as const; const indexes: IModelDescriptor["indexes"] = [[{ domain: 1, smtp: 1 }, { unique: true }]]; -export const zBouncerDomain = z - .object({ - _id: zObjectId, - domain: z.string(), - smtp: z.string(), - ping: zBouncerPingResult.nullable(), - updated_at: z.date(), - created_at: z.date(), - }) +export const zBouncerDomain = z.object({ + _id: zObjectId, + domain: z.string(), + smtp: z.string(), + ping: zBouncerPingResult.nullable(), + updated_at: z.date(), + created_at: z.date(), +}); export type BouncerDomain = z.output; diff --git a/shared/models/bouncer.email.model.ts b/shared/models/bouncer.email.model.ts index 7935143b6..5fcaccc42 100644 --- a/shared/models/bouncer.email.model.ts +++ b/shared/models/bouncer.email.model.ts @@ -12,27 +12,23 @@ const zEmailStatus = z.enum(["valid", "invalid", "not_supported", "error"]); export type EmailStatus = z.output; -export const zBouncerPingResult = z - .object({ - status: zEmailStatus, - message: z.string(), - responseCode: z.string().nullable(), - responseMessage: z.string().nullable(), - }) - ; +export const zBouncerPingResult = z.object({ + status: zEmailStatus, + message: z.string(), + responseCode: z.string().nullable(), + responseMessage: z.string().nullable(), +}); export type BouncerPingResult = z.output; -export const zBouncerEmail = z - .object({ - _id: zObjectId, - email: z.string(), - domain: z.string().nullable(), - smtp: z.string().nullable(), - ping: zBouncerPingResult, - created_at: z.date(), - }) - ; +export const zBouncerEmail = z.object({ + _id: zObjectId, + email: z.string(), + domain: z.string().nullable(), + smtp: z.string().nullable(), + ping: zBouncerPingResult, + created_at: z.date(), +}); export type BouncerEmail = z.output; diff --git a/shared/models/catalogue.api.model.ts b/shared/models/catalogue.api.model.ts index e8ec7b811..7396ba33e 100644 --- a/shared/models/catalogue.api.model.ts +++ b/shared/models/catalogue.api.model.ts @@ -1,79 +1,70 @@ import { z } from "zod"; -const etablissementFormateurSchema = z - .object({ - etablissement_formateur_id: z.string().nullish(), - etablissement_formateur_siret: z.string().nullish(), - etablissement_formateur_enseigne: z.string().nullish(), - etablissement_formateur_uai: z.string().nullish(), - etablissement_formateur_type: z.string().nullish(), - etablissement_formateur_conventionne: z.string().nullish(), - etablissement_formateur_declare_prefecture: z.string().nullish(), - etablissement_formateur_datadock: z.string().nullish(), - etablissement_formateur_adresse: z.string().nullish(), - etablissement_formateur_code_postal: z.string().nullish(), - etablissement_formateur_code_commune_insee: z.string().nullish(), - etablissement_formateur_localite: z.string().nullish(), - etablissement_formateur_complement_adresse: z.string().nullish(), - etablissement_formateur_cedex: z.string().nullish(), - etablissement_formateur_entreprise_raison_sociale: z.string().nullish(), - geo_coordonnees_etablissement_formateur: z.string().nullish(), - etablissement_formateur_region: z.string().nullish(), - etablissement_formateur_num_departement: z.string().nullish(), - etablissement_formateur_nom_departement: z.string().nullish(), - etablissement_formateur_nom_academie: z.string().nullish(), - etablissement_formateur_num_academie: z.string().nullish(), - etablissement_formateur_siren: z.string().nullish(), - etablissement_formateur_courriel: z.string().nullish(), - etablissement_formateur_published: z.boolean().nullish(), - etablissement_formateur_catalogue_published: z.boolean().nullish(), - rncp_etablissement_formateur_habilite: z.boolean().nullish(), - etablissement_formateur_date_creation: z.string().nullish(), - }) - ; - -const etablissementGestionnaireSchema = z - .object({ - etablissement_gestionnaire_id: z.string().nullish(), - etablissement_gestionnaire_siret: z.string().nullish(), - etablissement_gestionnaire_enseigne: z.string().nullish(), - etablissement_gestionnaire_uai: z.string().nullish(), - etablissement_gestionnaire_type: z.string().nullish(), - etablissement_gestionnaire_conventionne: z.string().nullish(), - etablissement_gestionnaire_declare_prefecture: z.string().nullish(), - etablissement_gestionnaire_datadock: z.string().nullish(), - etablissement_gestionnaire_adresse: z.string().nullish(), - etablissement_gestionnaire_code_postal: z.string().nullish(), - etablissement_gestionnaire_code_commune_insee: z.string().nullish(), - etablissement_gestionnaire_localite: z.string().nullish(), - etablissement_gestionnaire_complement_adresse: z.string().nullish(), - etablissement_gestionnaire_cedex: z.string().nullish(), - etablissement_gestionnaire_entreprise_raison_sociale: z.string().nullish(), - geo_coordonnees_etablissement_gestionnaire: z.string().nullish(), - etablissement_gestionnaire_region: z.string().nullish(), - etablissement_gestionnaire_num_departement: z.string().nullish(), - etablissement_gestionnaire_nom_departement: z.string().nullish(), - etablissement_gestionnaire_nom_academie: z.string().nullish(), - etablissement_gestionnaire_num_academie: z.string().nullish(), - etablissement_gestionnaire_siren: z.string().nullish(), - etablissement_gestionnaire_courriel: z.string().nullish(), - etablissement_gestionnaire_published: z.boolean().nullish(), - etablissement_gestionnaire_catalogue_published: z.boolean().nullish(), - rncp_etablissement_gestionnaire_habilite: z.boolean().nullish(), - etablissement_gestionnaire_date_creation: z.string().nullish(), - }) - ; - -const etablissementReferenceSchema = z - .object({ - etablissement_reference: z.string().nullish(), - etablissement_reference_published: z.boolean().nullish(), - etablissement_reference_habilite_rncp: z.boolean().nullish(), - etablissement_reference_certifie_qualite: z.boolean().nullish(), - etablissement_reference_date_creation: z.string().nullish(), - }) - ; - +const etablissementFormateurSchema = z.object({ + etablissement_formateur_id: z.string().nullish(), + etablissement_formateur_siret: z.string().nullish(), + etablissement_formateur_enseigne: z.string().nullish(), + etablissement_formateur_uai: z.string().nullish(), + etablissement_formateur_type: z.string().nullish(), + etablissement_formateur_conventionne: z.string().nullish(), + etablissement_formateur_declare_prefecture: z.string().nullish(), + etablissement_formateur_datadock: z.string().nullish(), + etablissement_formateur_adresse: z.string().nullish(), + etablissement_formateur_code_postal: z.string().nullish(), + etablissement_formateur_code_commune_insee: z.string().nullish(), + etablissement_formateur_localite: z.string().nullish(), + etablissement_formateur_complement_adresse: z.string().nullish(), + etablissement_formateur_cedex: z.string().nullish(), + etablissement_formateur_entreprise_raison_sociale: z.string().nullish(), + geo_coordonnees_etablissement_formateur: z.string().nullish(), + etablissement_formateur_region: z.string().nullish(), + etablissement_formateur_num_departement: z.string().nullish(), + etablissement_formateur_nom_departement: z.string().nullish(), + etablissement_formateur_nom_academie: z.string().nullish(), + etablissement_formateur_num_academie: z.string().nullish(), + etablissement_formateur_siren: z.string().nullish(), + etablissement_formateur_courriel: z.string().nullish(), + etablissement_formateur_published: z.boolean().nullish(), + etablissement_formateur_catalogue_published: z.boolean().nullish(), + rncp_etablissement_formateur_habilite: z.boolean().nullish(), + etablissement_formateur_date_creation: z.string().nullish(), +}); +const etablissementGestionnaireSchema = z.object({ + etablissement_gestionnaire_id: z.string().nullish(), + etablissement_gestionnaire_siret: z.string().nullish(), + etablissement_gestionnaire_enseigne: z.string().nullish(), + etablissement_gestionnaire_uai: z.string().nullish(), + etablissement_gestionnaire_type: z.string().nullish(), + etablissement_gestionnaire_conventionne: z.string().nullish(), + etablissement_gestionnaire_declare_prefecture: z.string().nullish(), + etablissement_gestionnaire_datadock: z.string().nullish(), + etablissement_gestionnaire_adresse: z.string().nullish(), + etablissement_gestionnaire_code_postal: z.string().nullish(), + etablissement_gestionnaire_code_commune_insee: z.string().nullish(), + etablissement_gestionnaire_localite: z.string().nullish(), + etablissement_gestionnaire_complement_adresse: z.string().nullish(), + etablissement_gestionnaire_cedex: z.string().nullish(), + etablissement_gestionnaire_entreprise_raison_sociale: z.string().nullish(), + geo_coordonnees_etablissement_gestionnaire: z.string().nullish(), + etablissement_gestionnaire_region: z.string().nullish(), + etablissement_gestionnaire_num_departement: z.string().nullish(), + etablissement_gestionnaire_nom_departement: z.string().nullish(), + etablissement_gestionnaire_nom_academie: z.string().nullish(), + etablissement_gestionnaire_num_academie: z.string().nullish(), + etablissement_gestionnaire_siren: z.string().nullish(), + etablissement_gestionnaire_courriel: z.string().nullish(), + etablissement_gestionnaire_published: z.boolean().nullish(), + etablissement_gestionnaire_catalogue_published: z.boolean().nullish(), + rncp_etablissement_gestionnaire_habilite: z.boolean().nullish(), + etablissement_gestionnaire_date_creation: z.string().nullish(), +}); +const etablissementReferenceSchema = z.object({ + etablissement_reference: z.string().nullish(), + etablissement_reference_published: z.boolean().nullish(), + etablissement_reference_habilite_rncp: z.boolean().nullish(), + etablissement_reference_certifie_qualite: z.boolean().nullish(), + etablissement_reference_date_creation: z.string().nullish(), +}); const stringOrArraySchema = z.union([z.string(), z.array(z.string())]); export const zFormationCatalogue = z @@ -172,7 +163,7 @@ export const zFormationCatalogue = z num_tel: z.string().nullish().describe("Numéro de téléphone de contact"), distance: z.number().nullish(), }) - + .merge(etablissementFormateurSchema) .merge(etablissementGestionnaireSchema) .merge(etablissementReferenceSchema); diff --git a/shared/models/catalogueEmailSiret.model.ts b/shared/models/catalogueEmailSiret.model.ts index 87ae4b3ca..102d595b7 100644 --- a/shared/models/catalogueEmailSiret.model.ts +++ b/shared/models/catalogueEmailSiret.model.ts @@ -9,15 +9,13 @@ const indexes: IModelDescriptor["indexes"] = [ [{ email: "text", siret: "text" }, {}], ]; -export const ZCatalogueEmailSiret = z - .object({ - _id: zObjectId, - email: z.string().email().describe("Email de l'utilisateur"), - siret: z.string().describe("Siret de l'utilisateur"), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().optional().describe("Date d'ajout en base de données"), - }) - ; +export const ZCatalogueEmailSiret = z.object({ + _id: zObjectId, + email: z.string().email().describe("Email de l'utilisateur"), + siret: z.string().describe("Siret de l'utilisateur"), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().optional().describe("Date d'ajout en base de données"), +}); export type ICatalogueEmailSiret = z.output; diff --git a/shared/models/deca.model/deca.model.ts b/shared/models/deca.model/deca.model.ts index bab8d8c2f..1b7f52a21 100644 --- a/shared/models/deca.model/deca.model.ts +++ b/shared/models/deca.model/deca.model.ts @@ -20,42 +20,40 @@ const indexes: IModelDescriptor["indexes"] = [ [{ created_at: -1 }, {}], ]; -export const ZDeca = z - .object({ - _id: zObjectId, +export const ZDeca = z.object({ + _id: zObjectId, - no_contrat: z.string().describe("Le numéro du contrat"), - statut: z.string().optional().describe("Le statut du contrat"), // Vide, corrigé, supprimé, rompu, annulé - flag_correction: z.boolean().optional().describe("Flag correction"), - date_suppression: z.date().optional().describe("La date de suppression ?"), - date_debut_contrat: z.date().describe("La date de début du contrat"), // AAAA-MM-JJ - date_fin_contrat: z.date().describe("La date de fin du contrat"), // AAAA-MM-JJ - date_effet_avenant: z.date().optional().describe("La date d'effet de l'avenant du contrat"), // AAAA-MM-JJ - date_effet_rupture: z.date().optional().describe("La date d'effet de la rupture"), // AAAA-MM-JJ - no_avenant: z.string().optional().describe("Le numéro de l'avenant du contrat"), - dispositif: z.string().optional().describe("dispositif"), // PROF/APPR - type_contrat: z.string().optional(), - // type_contrat: z - // .enum(["11", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "38"]) // 24 - // .optional() - // .describe( - // "Le type de contrat ou avenant doit correspondre à la situation du contrat (premier contrat, succession de contrats, avenants)." - // ), - rupture_avant_debut: z.boolean().optional().describe("rupture_avant_debut"), + no_contrat: z.string().describe("Le numéro du contrat"), + statut: z.string().optional().describe("Le statut du contrat"), // Vide, corrigé, supprimé, rompu, annulé + flag_correction: z.boolean().optional().describe("Flag correction"), + date_suppression: z.date().optional().describe("La date de suppression ?"), + date_debut_contrat: z.date().describe("La date de début du contrat"), // AAAA-MM-JJ + date_fin_contrat: z.date().describe("La date de fin du contrat"), // AAAA-MM-JJ + date_effet_avenant: z.date().optional().describe("La date d'effet de l'avenant du contrat"), // AAAA-MM-JJ + date_effet_rupture: z.date().optional().describe("La date d'effet de la rupture"), // AAAA-MM-JJ + no_avenant: z.string().optional().describe("Le numéro de l'avenant du contrat"), + dispositif: z.string().optional().describe("dispositif"), // PROF/APPR + type_contrat: z.string().optional(), + // type_contrat: z + // .enum(["11", "21", "22", "23", "31", "32", "33", "34", "35", "36", "37", "38"]) // 24 + // .optional() + // .describe( + // "Le type de contrat ou avenant doit correspondre à la situation du contrat (premier contrat, succession de contrats, avenants)." + // ), + rupture_avant_debut: z.boolean().optional().describe("rupture_avant_debut"), - type_employeur: z.number().optional().describe("Type employeur"), - employeur_specifique: z.number().optional().describe("Employeur specifique"), - type_derogation: z.number().optional().describe("Type de dérogation"), + type_employeur: z.number().optional().describe("Type employeur"), + employeur_specifique: z.number().optional().describe("Employeur specifique"), + type_derogation: z.number().optional().describe("Type de dérogation"), - alternant: ZDecaApprenant, - formation: ZDecaFormation, - etablissement_formation: ZDecaEtablissementFormation.optional(), - organisme_formation: ZDecaOrganismeFormation.optional(), - employeur: ZDecaEmployeur, - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().nullish().describe("Date d'ajout en base de données"), - }) - ; + alternant: ZDecaApprenant, + formation: ZDecaFormation, + etablissement_formation: ZDecaEtablissementFormation.optional(), + organisme_formation: ZDecaOrganismeFormation.optional(), + employeur: ZDecaEmployeur, + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().nullish().describe("Date d'ajout en base de données"), +}); export const ZDecaNew = ZDeca.omit({ _id: true, diff --git a/shared/models/deca.model/decaHistory.model.ts b/shared/models/deca.model/decaHistory.model.ts index 316fd5470..381e3f048 100644 --- a/shared/models/deca.model/decaHistory.model.ts +++ b/shared/models/deca.model/decaHistory.model.ts @@ -7,16 +7,14 @@ const collectionName = "decaHistory" as const; const indexes: IModelDescriptor["indexes"] = []; -export const ZDecaHistory = z - .object({ - _id: zObjectId, - key: z.string().describe("Modified key"), - from: z.any().describe("Value from"), - to: z.any().describe("Value to"), - deca_id: zObjectId, - time: z.date().describe("Modified time"), - }) - ; +export const ZDecaHistory = z.object({ + _id: zObjectId, + key: z.string().describe("Modified key"), + from: z.any().describe("Value from"), + to: z.any().describe("Value to"), + deca_id: zObjectId, + time: z.date().describe("Modified time"), +}); export type IDecaHistory = z.output; export type IDecaHistoryJson = Jsonify>; diff --git a/shared/models/deca.model/decaImportJobResult.model.ts b/shared/models/deca.model/decaImportJobResult.model.ts index 83713a84c..65ca1a4c0 100644 --- a/shared/models/deca.model/decaImportJobResult.model.ts +++ b/shared/models/deca.model/decaImportJobResult.model.ts @@ -10,19 +10,17 @@ const indexes: IModelDescriptor["indexes"] = [ [{ import_date: -1 }, {}], ]; -export const ZDecaImportJobResult = z - .object({ - _id: zObjectId, - import_date_string: z.string().describe("La date d'un import deca au format yyyy-MM-dd"), - import_date: z.date().describe("La date d'un import deca au format date tz Paris"), - has_completed: z - .boolean() - .describe( - "true marque que l'import pour ce jour précis est complet. informatif uniquement. false ne sera jamais enregistré" - ), - created_at: z.date().describe("Date d'ajout en base de données"), - }) - ; +export const ZDecaImportJobResult = z.object({ + _id: zObjectId, + import_date_string: z.string().describe("La date d'un import deca au format yyyy-MM-dd"), + import_date: z.date().describe("La date d'un import deca au format date tz Paris"), + has_completed: z + .boolean() + .describe( + "true marque que l'import pour ce jour précis est complet. informatif uniquement. false ne sera jamais enregistré" + ), + created_at: z.date().describe("Date d'ajout en base de données"), +}); export type IDecaImportJobResult = z.output; export type IDecaImportJobResultJson = Jsonify>; diff --git a/shared/models/deca.model/parts/deca.apprenant.part.ts b/shared/models/deca.model/parts/deca.apprenant.part.ts index 690f8f6c6..31f8647fc 100644 --- a/shared/models/deca.model/parts/deca.apprenant.part.ts +++ b/shared/models/deca.model/parts/deca.apprenant.part.ts @@ -1,28 +1,26 @@ import { Jsonify } from "type-fest"; import { z } from "zod"; -export const ZDecaApprenant = z - .object({ - nom: z.string().describe("Le nom de l'alternant"), - prenom: z.string().optional().describe("Le prenom de l'alternant"), - sexe: z.string().nullish().describe("Le sexe de l'alternant"), - date_naissance: z.date().describe("La date de naissance de l'alternant"), // AAAA-MM-JJ - departement_naissance: z.string().optional().describe("Le département de naissance de l'alternant"), - nationalite: z.number().optional().describe("Le code de la nationalité de l'alternant"), - handicap: z.boolean().optional().describe("Indique si l'alternant est identifié comme porteur d'un handicap"), - courriel: z.string().optional().describe("L'adresse email de l'alternant"), - telephone: z.string().optional().describe("Le numéro de téléphone de l'alternant"), - adresse: z - .object({ - numero: z.coerce.string().nullish().describe("Le numéro de l'adresse"), - voie: z.string().optional().describe("La voie de l'adresse"), - code_postal: z.string().optional().describe("Le code postal de l'adresse"), - }) - - .optional(), - derniere_classe: z.coerce.number().nullish().describe("La dernière classe de l'apprenant"), - }) - ; +export const ZDecaApprenant = z.object({ + nom: z.string().describe("Le nom de l'alternant"), + prenom: z.string().optional().describe("Le prenom de l'alternant"), + sexe: z.string().nullish().describe("Le sexe de l'alternant"), + date_naissance: z.date().describe("La date de naissance de l'alternant"), // AAAA-MM-JJ + departement_naissance: z.string().optional().describe("Le département de naissance de l'alternant"), + nationalite: z.number().optional().describe("Le code de la nationalité de l'alternant"), + handicap: z.boolean().optional().describe("Indique si l'alternant est identifié comme porteur d'un handicap"), + courriel: z.string().optional().describe("L'adresse email de l'alternant"), + telephone: z.string().optional().describe("Le numéro de téléphone de l'alternant"), + adresse: z + .object({ + numero: z.coerce.string().nullish().describe("Le numéro de l'adresse"), + voie: z.string().optional().describe("La voie de l'adresse"), + code_postal: z.string().optional().describe("Le code postal de l'adresse"), + }) + + .optional(), + derniere_classe: z.coerce.number().nullish().describe("La dernière classe de l'apprenant"), +}); export type IDecaApprenant = z.output; export type IDecaApprenantJson = Jsonify>; diff --git a/shared/models/deca.model/parts/deca.employeur.part.ts b/shared/models/deca.model/parts/deca.employeur.part.ts index 4f6aff04c..e3d5c9594 100644 --- a/shared/models/deca.model/parts/deca.employeur.part.ts +++ b/shared/models/deca.model/parts/deca.employeur.part.ts @@ -1,23 +1,21 @@ import { Jsonify } from "type-fest"; import { z } from "zod"; -export const ZDecaEmployeur = z - .object({ - siret: z.string().optional().describe("N° SIRET de l'employeur"), - denomination: z.string().optional().describe("Denomination de l'employeur"), - adresse: z - .object({ - code_postal: z.string().describe("Le code postal de l'adresse"), - }) - - .optional(), - naf: z.string().optional().describe("Code NAF de l’entreprise"), - code_idcc: z.string().optional().describe("Le code IDCC de l'employeur"), - nombre_de_salaries: z.number().nullish().describe("Effectif salarié de l'entreprise"), - courriel: z.string().optional().describe("Email de l’employeur"), - telephone: z.string().optional().describe("Téléphone de l'employeur"), - }) - ; +export const ZDecaEmployeur = z.object({ + siret: z.string().optional().describe("N° SIRET de l'employeur"), + denomination: z.string().optional().describe("Denomination de l'employeur"), + adresse: z + .object({ + code_postal: z.string().describe("Le code postal de l'adresse"), + }) + + .optional(), + naf: z.string().optional().describe("Code NAF de l’entreprise"), + code_idcc: z.string().optional().describe("Le code IDCC de l'employeur"), + nombre_de_salaries: z.number().nullish().describe("Effectif salarié de l'entreprise"), + courriel: z.string().optional().describe("Email de l’employeur"), + telephone: z.string().optional().describe("Téléphone de l'employeur"), +}); export type IDecaEmployeur = z.output; export type IDecaEmployeurJson = Jsonify>; diff --git a/shared/models/deca.model/parts/deca.etablissementFormation.part.ts b/shared/models/deca.model/parts/deca.etablissementFormation.part.ts index f5d8f2359..4c56a8238 100644 --- a/shared/models/deca.model/parts/deca.etablissementFormation.part.ts +++ b/shared/models/deca.model/parts/deca.etablissementFormation.part.ts @@ -3,11 +3,9 @@ import { z } from "zod"; import { extensions } from "../../../helpers/zodHelpers/zodPrimitives"; -export const ZDecaEtablissementFormation = z - .object({ - siret: extensions.siret.optional().describe("Le siret de l'établissement de la formation"), - }) - ; +export const ZDecaEtablissementFormation = z.object({ + siret: extensions.siret.optional().describe("Le siret de l'établissement de la formation"), +}); export type IDecaEtablissementFormation = z.output; export type IDecaEtablissementFormationJson = Jsonify>; diff --git a/shared/models/deca.model/parts/deca.formation.part.ts b/shared/models/deca.model/parts/deca.formation.part.ts index e818931e8..5bee46b32 100644 --- a/shared/models/deca.model/parts/deca.formation.part.ts +++ b/shared/models/deca.model/parts/deca.formation.part.ts @@ -1,16 +1,14 @@ import { Jsonify } from "type-fest"; import { z } from "zod"; -export const ZDecaFormation = z - .object({ - date_debut_formation: z.date().optional().describe("La date de début de la formation"), // AAAA-MM-JJ - date_fin_formation: z.date().optional().describe("La date de fin de la formation"), // AAAA-MM-JJ - code_diplome: z.string().optional().describe("Le code diplôme de la formation"), - intitule_ou_qualification: z.string().optional().describe("L'adresse email de l'alternant"), - rncp: z.string().optional().describe("Le code RNCP de la formation"), - type_diplome: z.string().optional().describe("Catégorie par niveau du Diplôme ou titre visé par l'Alternant"), - }) - ; +export const ZDecaFormation = z.object({ + date_debut_formation: z.date().optional().describe("La date de début de la formation"), // AAAA-MM-JJ + date_fin_formation: z.date().optional().describe("La date de fin de la formation"), // AAAA-MM-JJ + code_diplome: z.string().optional().describe("Le code diplôme de la formation"), + intitule_ou_qualification: z.string().optional().describe("L'adresse email de l'alternant"), + rncp: z.string().optional().describe("Le code RNCP de la formation"), + type_diplome: z.string().optional().describe("Catégorie par niveau du Diplôme ou titre visé par l'Alternant"), +}); export type IDecaFormation = z.output; export type IDecaFormationJson = Jsonify>; diff --git a/shared/models/deca.model/parts/deca.organismeFormation.part.ts b/shared/models/deca.model/parts/deca.organismeFormation.part.ts index d9f111e50..ffa48507c 100644 --- a/shared/models/deca.model/parts/deca.organismeFormation.part.ts +++ b/shared/models/deca.model/parts/deca.organismeFormation.part.ts @@ -1,12 +1,10 @@ import { Jsonify } from "type-fest"; import { z } from "zod"; -export const ZDecaOrganismeFormation = z - .object({ - siret: z.string().optional().describe("Le SIRET de l'organisme de formation principal"), - uai_cfa: z.string().optional().describe("L'UAI de l'organisme responsable"), - }) - ; +export const ZDecaOrganismeFormation = z.object({ + siret: z.string().optional().describe("Le SIRET de l'organisme de formation principal"), + uai_cfa: z.string().optional().describe("L'UAI de l'organisme responsable"), +}); export type IDecaOrganismeFormation = z.output; export type IDecaOrganismeFormationJson = Jsonify>; diff --git a/shared/models/document.model.ts b/shared/models/document.model.ts index d56185bb7..5a28dcd71 100644 --- a/shared/models/document.model.ts +++ b/shared/models/document.model.ts @@ -10,48 +10,46 @@ const indexes: IModelDescriptor["indexes"] = [ [{ kind: 1, created_at: 1 }, {}], ]; -export const ZUploadDocument = z - .object({ - _id: zObjectId, - kind: z.literal("upload"), - type_document: z.string().describe("Le type de document (exemple: DECA, etc..)"), - ext_fichier: z.enum(["xlsx", "xls", "csv"]).describe("Le type de fichier extension"), - nom_fichier: z.string().describe("Le nom de fichier"), - chemin_fichier: z.string().describe("Chemin du fichier binaire"), - taille_fichier: z.number().int().finite().describe("Taille du fichier en bytes"), - hash_secret: z.string().describe("Hash fichier"), - hash_fichier: z.string().describe("Checksum fichier"), - columns: z.array(z.string()).nullish().describe("Liste des colonnes"), - delimiter: z.string().nullish().describe("Délimiteur"), - import_progress: z.number().finite().nullish().describe("Progress percentage (-1 not started)"), - lines_count: z.number().int().finite().nullish().describe("Number of lines"), - added_by: z.string().describe("Qui a ajouté le fichier"), - updated_at: z.date().nullish().describe("Date de mise à jour en base de données"), - created_at: z.date().describe("Date d'ajout en base de données"), - job_id: z.string().nullish().describe("Identifiant du job de génération"), - job_status: z.enum(["pending", "paused", "importing", "done", "error"]).describe("Status du job de génération"), - job_error: z.string().nullish().describe("Erreur du job de génération"), - }); +export const ZUploadDocument = z.object({ + _id: zObjectId, + kind: z.literal("upload"), + type_document: z.string().describe("Le type de document (exemple: DECA, etc..)"), + ext_fichier: z.enum(["xlsx", "xls", "csv"]).describe("Le type de fichier extension"), + nom_fichier: z.string().describe("Le nom de fichier"), + chemin_fichier: z.string().describe("Chemin du fichier binaire"), + taille_fichier: z.number().int().finite().describe("Taille du fichier en bytes"), + hash_secret: z.string().describe("Hash fichier"), + hash_fichier: z.string().describe("Checksum fichier"), + columns: z.array(z.string()).nullish().describe("Liste des colonnes"), + delimiter: z.string().nullish().describe("Délimiteur"), + import_progress: z.number().finite().nullish().describe("Progress percentage (-1 not started)"), + lines_count: z.number().int().finite().nullish().describe("Number of lines"), + added_by: z.string().describe("Qui a ajouté le fichier"), + updated_at: z.date().nullish().describe("Date de mise à jour en base de données"), + created_at: z.date().describe("Date d'ajout en base de données"), + job_id: z.string().nullish().describe("Identifiant du job de génération"), + job_status: z.enum(["pending", "paused", "importing", "done", "error"]).describe("Status du job de génération"), + job_error: z.string().nullish().describe("Erreur du job de génération"), +}); -export const ZMailingListDocument = z - .object({ - _id: zObjectId, - kind: z.literal("mailingList"), - type_document: z.string().describe("Le type de document (exemple: DECA, etc..)"), - ext_fichier: z.enum(["xlsx", "xls", "csv"]).describe("Le type de fichier extension"), - nom_fichier: z.string().describe("Le nom de fichier"), - chemin_fichier: z.string().describe("Chemin du fichier binaire"), - taille_fichier: z.number().int().finite().describe("Taille du fichier en bytes"), - hash_secret: z.string().describe("Hash fichier"), - hash_fichier: z.string().describe("Checksum fichier"), - process_progress: z.number().finite().optional().describe("Number of lines processed"), - lines_count: z.number().int().finite().optional().describe("Number of lines"), - added_by: z.string().describe("Qui a ajouté le fichier"), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().describe("Date d'ajout en base de données"), - job_id: z.string().nullish().describe("Identifiant du job de génération"), - job_status: z.enum(["pending", "paused", "processing", "done", "error"]).describe("Status du job de génération"), - }); +export const ZMailingListDocument = z.object({ + _id: zObjectId, + kind: z.literal("mailingList"), + type_document: z.string().describe("Le type de document (exemple: DECA, etc..)"), + ext_fichier: z.enum(["xlsx", "xls", "csv"]).describe("Le type de fichier extension"), + nom_fichier: z.string().describe("Le nom de fichier"), + chemin_fichier: z.string().describe("Chemin du fichier binaire"), + taille_fichier: z.number().int().finite().describe("Taille du fichier en bytes"), + hash_secret: z.string().describe("Hash fichier"), + hash_fichier: z.string().describe("Checksum fichier"), + process_progress: z.number().finite().optional().describe("Number of lines processed"), + lines_count: z.number().int().finite().optional().describe("Number of lines"), + added_by: z.string().describe("Qui a ajouté le fichier"), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().describe("Date d'ajout en base de données"), + job_id: z.string().nullish().describe("Identifiant du job de génération"), + job_status: z.enum(["pending", "paused", "processing", "done", "error"]).describe("Status du job de génération"), +}); export const ZDocument = z.discriminatedUnion("kind", [ZUploadDocument, ZMailingListDocument]); diff --git a/shared/models/documentContent.model.ts b/shared/models/documentContent.model.ts index 8cf1cb417..cd21a5503 100644 --- a/shared/models/documentContent.model.ts +++ b/shared/models/documentContent.model.ts @@ -10,16 +10,14 @@ const indexes: IModelDescriptor["indexes"] = [ [{ type_document: 1, _id: 1 }, {}], ]; -export const ZDocumentContent = z - .object({ - _id: zObjectId, - document_id: z.string().describe("Identifiant du document"), - content: z.record(z.unknown()).optional().describe("Contenu du document"), - type_document: z.string().optional().describe("Le type de document (exemple: DECA, etc..)"), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().optional().describe("Date d'ajout en base de données"), - }) - ; +export const ZDocumentContent = z.object({ + _id: zObjectId, + document_id: z.string().describe("Identifiant du document"), + content: z.record(z.unknown()).optional().describe("Contenu du document"), + type_document: z.string().optional().describe("Le type de document (exemple: DECA, etc..)"), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().optional().describe("Date d'ajout en base de données"), +}); export type IDocumentContent = z.output; export type IDocumentContentJson = Jsonify>; diff --git a/shared/models/emailDenied.model.ts b/shared/models/emailDenied.model.ts index bfe7ac4fd..12df48d44 100644 --- a/shared/models/emailDenied.model.ts +++ b/shared/models/emailDenied.model.ts @@ -6,15 +6,13 @@ const collectionName = "emailDenied" as const; const indexes: IModelDescriptor["indexes"] = [[{ email: 1 }, {}]]; -export const ZEmailDenied = z - .object({ - _id: zObjectId, - email: z.string().describe("L'email rejetée"), - reason: z.enum(["unsubscribe"]), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().describe("Date d'ajout en base de données"), - }) - ; +export const ZEmailDenied = z.object({ + _id: zObjectId, + email: z.string().describe("L'email rejetée"), + reason: z.enum(["unsubscribe"]), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().describe("Date d'ajout en base de données"), +}); export type IEmailDenied = z.output; diff --git a/shared/models/events/bal_emails.event.ts b/shared/models/events/bal_emails.event.ts index e33d1d2b2..d594b62b7 100644 --- a/shared/models/events/bal_emails.event.ts +++ b/shared/models/events/bal_emails.event.ts @@ -3,28 +3,23 @@ import { z } from "zod"; import { zTemplate } from "../../mailer"; import { zObjectId } from "../common"; -const zEmailError = z - .object({ - type: z.enum(["fatal", "soft_bounce", "hard_bounce", "complaint", "invalid_email", "blocked", "error"]).optional(), - message: z.string().optional(), - }) - ; - +const zEmailError = z.object({ + type: z.enum(["fatal", "soft_bounce", "hard_bounce", "complaint", "invalid_email", "blocked", "error"]).optional(), + message: z.string().optional(), +}); export type IEmailError = z.output; -export const ZEventBalEmail = z - .object({ - _id: zObjectId, - type: z.literal("email.bal").describe("Type de l'évènement"), - person_id: z.string().describe("Identifiant de la personne"), - template: zTemplate, - created_at: z.date(), - updated_at: z.date(), - opened_at: z.date().nullable(), - delivered_at: z.date().nullable(), - messageId: z.string().nullable(), - errors: z.array(zEmailError), - }) - ; +export const ZEventBalEmail = z.object({ + _id: zObjectId, + type: z.literal("email.bal").describe("Type de l'évènement"), + person_id: z.string().describe("Identifiant de la personne"), + template: zTemplate, + created_at: z.date(), + updated_at: z.date(), + opened_at: z.date().nullable(), + delivered_at: z.date().nullable(), + messageId: z.string().nullable(), + errors: z.array(zEmailError), +}); export type IEventBalEmail = z.output; diff --git a/shared/models/mailingList.model.ts b/shared/models/mailingList.model.ts index 203f2c3fd..016cad0bf 100644 --- a/shared/models/mailingList.model.ts +++ b/shared/models/mailingList.model.ts @@ -13,45 +13,41 @@ const indexes: IModelDescriptor["indexes"] = [ [{ source: 1 }, { name: "source" }], ]; -export const ZMailingList = z - .object({ - _id: zObjectId, - campaign_name: z.string().describe("Nom de la campagne"), - source: z.string().describe("Source de la campagne"), - email: z.string().describe("Nom de la colonne email"), - secondary_email: z.string().optional().describe("Nom de la colonne email secondaire"), - identifier_columns: z.array(z.string()).describe("Liste des colonnes d'identifiants"), - output_columns: z - .array( - z - .object({ - column: z.string(), - output: z.string(), - simple: z.boolean(), - }) - - ) - .describe("Liste des colonnes de sortie"), - training_columns: z - .object({ - cle_ministere_educatif: z.string().optional(), - mef: z.string().optional(), - cfd: z.string().optional(), - rncp: z.string().optional(), - code_postal: z.string().optional(), - uai_lieu_formation: z.string().optional(), - uai_formateur: z.string().optional(), - uai_formateur_responsable: z.string().optional(), - code_insee: z.string().optional(), +export const ZMailingList = z.object({ + _id: zObjectId, + campaign_name: z.string().describe("Nom de la campagne"), + source: z.string().describe("Source de la campagne"), + email: z.string().describe("Nom de la colonne email"), + secondary_email: z.string().optional().describe("Nom de la colonne email secondaire"), + identifier_columns: z.array(z.string()).describe("Liste des colonnes d'identifiants"), + output_columns: z + .array( + z.object({ + column: z.string(), + output: z.string(), + simple: z.boolean(), }) - - .describe("Liste des colonnes lié à la formation"), - document_id: z.string().optional().describe("Identifiant du document généré"), - added_by: z.string().describe("L'utilisateur ayant crée la liste"), - updated_at: z.date().describe("Date de mise à jour en base de données"), - created_at: z.date().describe("Date d'ajout en base de données"), - }) - ; + ) + .describe("Liste des colonnes de sortie"), + training_columns: z + .object({ + cle_ministere_educatif: z.string().optional(), + mef: z.string().optional(), + cfd: z.string().optional(), + rncp: z.string().optional(), + code_postal: z.string().optional(), + uai_lieu_formation: z.string().optional(), + uai_formateur: z.string().optional(), + uai_formateur_responsable: z.string().optional(), + code_insee: z.string().optional(), + }) + + .describe("Liste des colonnes lié à la formation"), + document_id: z.string().optional().describe("Identifiant du document généré"), + added_by: z.string().describe("L'utilisateur ayant crée la liste"), + updated_at: z.date().describe("Date de mise à jour en base de données"), + created_at: z.date().describe("Date d'ajout en base de données"), +}); export const ZMailingListWithDocument = ZMailingList.extend({ document: ZDocument.nullish(), diff --git a/shared/models/organisation.model.ts b/shared/models/organisation.model.ts index 93584c96e..d9968edf9 100644 --- a/shared/models/organisation.model.ts +++ b/shared/models/organisation.model.ts @@ -27,36 +27,32 @@ const indexes: IModelDescriptor["indexes"] = [ [{ "etablissements.siret": 1 }, { name: "siret" }], ]; -export const ZOrganisation = z - .object({ - _id: zObjectId, - nom: z.string().optional().describe("Nom de l'organisation"), - siren: z.string().optional().describe("Siren de l'organisation"), - email_domains: z.array(z.string()).optional().describe("Liste des domaines email"), - etablissements: z - .array( - z - .object({ - nom: z.string().optional().describe("Nom de l'établissement"), - siret: z.string().optional().describe("Siret actif de l'établissement"), - is_hq: z.boolean().optional().describe("Siège social"), - is_close: z.boolean().optional().describe("Est fermé"), - }) - - ) - .optional() - .describe("Liste des établissements"), - _meta: z - .object({ - sources: z.array(z.string()).optional(), +export const ZOrganisation = z.object({ + _id: zObjectId, + nom: z.string().optional().describe("Nom de l'organisation"), + siren: z.string().optional().describe("Siren de l'organisation"), + email_domains: z.array(z.string()).optional().describe("Liste des domaines email"), + etablissements: z + .array( + z.object({ + nom: z.string().optional().describe("Nom de l'établissement"), + siret: z.string().optional().describe("Siret actif de l'établissement"), + is_hq: z.boolean().optional().describe("Siège social"), + is_close: z.boolean().optional().describe("Est fermé"), }) - .passthrough() - .optional() - .describe("Métadonnées"), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().optional().describe("Date d'ajout en base de données"), - }) - ; + ) + .optional() + .describe("Liste des établissements"), + _meta: z + .object({ + sources: z.array(z.string()).optional(), + }) + .passthrough() + .optional() + .describe("Métadonnées"), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().optional().describe("Date d'ajout en base de données"), +}); export type IOrganisation = z.output; export type IOrganisationJson = Jsonify>; diff --git a/shared/models/person.model.ts b/shared/models/person.model.ts index 1c16b101c..6458525d9 100644 --- a/shared/models/person.model.ts +++ b/shared/models/person.model.ts @@ -21,26 +21,24 @@ const indexes: IModelDescriptor["indexes"] = [ ], ]; -export const ZPerson = z - .object({ - _id: zObjectId, - email: z.string().describe("Email de la personne"), - civility: z.enum(["Madame", "Monsieur"]).optional().describe("civilité"), - nom: z.string().optional().describe("Le nom de la personne"), - prenom: z.string().optional().describe("Le prénom de la personne"), - organisations: z.array(z.string().describe("Identifiant de l'organisation")).describe("Liste des organisations"), - sirets: z.array(z.string()).optional().describe("Liste de sirets recensé (sécurisation qualité de la donnée)"), - _meta: z - .object({ - sources: z.array(z.string()).optional(), - }) - .passthrough() - .describe("Métadonnées") - .optional(), - updated_at: z.date().describe("Date de mise à jour en base de données").optional(), - created_at: z.date().describe("Date d'ajout en base de données").optional(), - }) - ; +export const ZPerson = z.object({ + _id: zObjectId, + email: z.string().describe("Email de la personne"), + civility: z.enum(["Madame", "Monsieur"]).optional().describe("civilité"), + nom: z.string().optional().describe("Le nom de la personne"), + prenom: z.string().optional().describe("Le prénom de la personne"), + organisations: z.array(z.string().describe("Identifiant de l'organisation")).describe("Liste des organisations"), + sirets: z.array(z.string()).optional().describe("Liste de sirets recensé (sécurisation qualité de la donnée)"), + _meta: z + .object({ + sources: z.array(z.string()).optional(), + }) + .passthrough() + .describe("Métadonnées") + .optional(), + updated_at: z.date().describe("Date de mise à jour en base de données").optional(), + created_at: z.date().describe("Date d'ajout en base de données").optional(), +}); export const ZPersonWithOrganisation = ZPerson.extend({ organisation: ZOrganisation.nullish(), diff --git a/shared/models/session.model.ts b/shared/models/session.model.ts index 09355878b..9d0be9d21 100644 --- a/shared/models/session.model.ts +++ b/shared/models/session.model.ts @@ -7,15 +7,13 @@ const collectionName = "sessions" as const; const indexes: IModelDescriptor["indexes"] = [[{ expires_at: 1 }, { expireAfterSeconds: 0 }]]; -export const ZSession = z - .object({ - _id: zObjectId, - token: z.string().describe("Token de la session"), - updated_at: z.date().describe("Date de mise à jour en base de données"), - created_at: z.date().describe("Date d'ajout en base de données"), - expires_at: z.date().describe("Date d'expiration"), - }) - ; +export const ZSession = z.object({ + _id: zObjectId, + token: z.string().describe("Token de la session"), + updated_at: z.date().describe("Date de mise à jour en base de données"), + created_at: z.date().describe("Date d'ajout en base de données"), + expires_at: z.date().describe("Date d'expiration"), +}); export type ISession = z.output; export type ISessionJson = Jsonify>; diff --git a/shared/models/user.model.ts b/shared/models/user.model.ts index f9d375e41..12b532502 100644 --- a/shared/models/user.model.ts +++ b/shared/models/user.model.ts @@ -23,33 +23,29 @@ const indexes: IModelDescriptor["indexes"] = [ ], ]; -export const ZUser = z - .object({ - _id: zObjectId, - email: z.string().email().describe("Email de l'utilisateur"), - password: z.string().describe("Mot de passe de l'utilisateur"), - person_id: z.string().describe("Identifiant de la personne"), - is_admin: z.boolean().optional().describe("Est administrateur"), - is_support: z.boolean().optional().describe("Est support"), - api_key: z.string().optional().describe("Clé API"), - api_key_used_at: z.date().nullish().describe("Date de dernière utilisation de la clé API"), - updated_at: z.date().optional().describe("Date de mise à jour en base de données"), - created_at: z.date().optional().describe("Date d'ajout en base de données"), - }) - ; +export const ZUser = z.object({ + _id: zObjectId, + email: z.string().email().describe("Email de l'utilisateur"), + password: z.string().describe("Mot de passe de l'utilisateur"), + person_id: z.string().describe("Identifiant de la personne"), + is_admin: z.boolean().optional().describe("Est administrateur"), + is_support: z.boolean().optional().describe("Est support"), + api_key: z.string().optional().describe("Clé API"), + api_key_used_at: z.date().nullish().describe("Date de dernière utilisation de la clé API"), + updated_at: z.date().optional().describe("Date de mise à jour en base de données"), + created_at: z.date().optional().describe("Date d'ajout en base de données"), +}); -export const ZUserPublic = z - .object({ - _id: zObjectId, - email: ZUser.shape.email, - person_id: ZUser.shape.person_id, - is_admin: ZUser.shape.is_admin, - is_support: ZUser.shape.is_support, - api_key_used_at: ZUser.shape.api_key_used_at, - updated_at: ZUser.shape.updated_at, - created_at: ZUser.shape.created_at, - }) - ; +export const ZUserPublic = z.object({ + _id: zObjectId, + email: ZUser.shape.email, + person_id: ZUser.shape.person_id, + is_admin: ZUser.shape.is_admin, + is_support: ZUser.shape.is_support, + api_key_used_at: ZUser.shape.api_key_used_at, + updated_at: ZUser.shape.updated_at, + created_at: ZUser.shape.created_at, +}); export type IUser = z.output; export type IUserPublic = Jsonify>; diff --git a/shared/routes/auth.routes.ts b/shared/routes/auth.routes.ts index e82472bd4..11800f21f 100644 --- a/shared/routes/auth.routes.ts +++ b/shared/routes/auth.routes.ts @@ -40,11 +40,9 @@ export const zAuthRoutes = { "/auth/reset-password": { method: "post", path: "/auth/reset-password", - body: z - .object({ - password: ZUser.shape.password, - }) - , + body: z.object({ + password: ZUser.shape.password, + }), response: { "200": ZResOk, }, @@ -57,12 +55,10 @@ export const zAuthRoutes = { "/auth/login": { method: "post", path: "/auth/login", - body: z - .object({ - email: ZUser.shape.email, - password: ZUser.shape.password, - }) - , + body: z.object({ + email: ZUser.shape.email, + password: ZUser.shape.password, + }), response: { "200": zUserWithPersonPublic, }, diff --git a/shared/routes/common.routes.ts b/shared/routes/common.routes.ts index 3d2163311..a24963b53 100644 --- a/shared/routes/common.routes.ts +++ b/shared/routes/common.routes.ts @@ -5,59 +5,55 @@ import { AnyZodObject, ZodType } from "zod"; import { z } from "../helpers/zodWithOpenApi"; import { AccessPermission, AccessRessouces } from "../security/permissions"; -export const ZResError = z - .object({ - data: z - .any() - .optional() - .openapi({ - description: "Données contextuelles liées à l'erreur", - example: { - validationError: { - issues: [ - { - code: "invalid_type", - expected: "number", - received: "nan", - path: ["longitude"], - message: "Number attendu", - }, - ], - name: "ZodError", - statusCode: 400, - code: "FST_ERR_VALIDATION", - validationContext: "querystring", - }, +export const ZResError = z.object({ + data: z + .any() + .optional() + .openapi({ + description: "Données contextuelles liées à l'erreur", + example: { + validationError: { + issues: [ + { + code: "invalid_type", + expected: "number", + received: "nan", + path: ["longitude"], + message: "Number attendu", + }, + ], + name: "ZodError", + statusCode: 400, + code: "FST_ERR_VALIDATION", + validationContext: "querystring", }, - }), - code: z.string().nullish(), - message: z.string().openapi({ - description: "Un message explicatif de l'erreur", - example: "querystring.longitude: Number attendu", - }), - name: z.string().openapi({ - description: "Le type générique de l'erreur", - example: "Bad Request", - }), - statusCode: z.number().openapi({ - description: "Le status code retourné", - example: 400, + }, }), - }) - ; + code: z.string().nullish(), + message: z.string().openapi({ + description: "Un message explicatif de l'erreur", + example: "querystring.longitude: Number attendu", + }), + name: z.string().openapi({ + description: "Le type générique de l'erreur", + example: "Bad Request", + }), + statusCode: z.number().openapi({ + description: "Le status code retourné", + example: 400, + }), +}); export const ZResOk = z.object({}); export type IResError = z.input; export type IResErrorJson = Jsonify>; -export const ZReqParamsSearchPagination = z - .object({ - page: z.preprocess((v) => parseInt(v as string, 10), z.number().positive().optional()), - limit: z.preprocess((v) => parseInt(v as string, 10), z.number().positive().optional()), - q: z.string().optional(), - }) - ; +export const ZReqParamsSearchPagination = z.object({ + page: z.preprocess((v) => parseInt(v as string, 10), z.number().positive().optional()), + limit: z.preprocess((v) => parseInt(v as string, 10), z.number().positive().optional()), + q: z.string().optional(), +}); export type IReqParamsSearchPagination = z.input; export const ZReqHeadersAuthorization = z diff --git a/shared/routes/core.routes.ts b/shared/routes/core.routes.ts index 994573cb5..404d88600 100644 --- a/shared/routes/core.routes.ts +++ b/shared/routes/core.routes.ts @@ -17,8 +17,7 @@ export const zCoreRoutes = { }), env: z.enum(["local", "recette", "production", "preview", "test"]), }) - .describe("API Health") - , + .describe("API Health"), }, securityScheme: null, openapi: { diff --git a/shared/routes/document.routes.ts b/shared/routes/document.routes.ts index 1b996885e..2bcba1bea 100644 --- a/shared/routes/document.routes.ts +++ b/shared/routes/document.routes.ts @@ -8,11 +8,9 @@ export const zDocumentRoutes = { "/documents/columns": { method: "get", path: "/documents/columns", - querystring: z - .object({ - type: z.string(), - }) - , + querystring: z.object({ + type: z.string(), + }), response: { "200": z.array(z.string()), }, @@ -37,11 +35,9 @@ export const zDocumentRoutes = { "/documents/sample": { method: "get", path: "/documents/sample", - querystring: z - .object({ - type: z.string(), - }) - , + querystring: z.object({ + type: z.string(), + }), response: { "200": z.array(ZDocumentContent), }, diff --git a/shared/routes/emails.routes.ts b/shared/routes/emails.routes.ts index 2382f8f72..bca4f608f 100644 --- a/shared/routes/emails.routes.ts +++ b/shared/routes/emails.routes.ts @@ -8,11 +8,9 @@ export const zEmailRoutes = { "/emails/preview": { method: "get", path: "/emails/preview", - querystring: z - .object({ - data: z.string(), - }) - , + querystring: z.object({ + data: z.string(), + }), response: { "200": z.unknown(), }, @@ -36,11 +34,9 @@ export const zEmailRoutes = { "/emails/unsubscribe": { method: "get", path: "/emails/unsubscribe", - querystring: z - .object({ - data: z.string(), - }) - , + querystring: z.object({ + data: z.string(), + }), response: { "200": z.unknown(), }, @@ -56,12 +52,10 @@ export const zEmailRoutes = { webhookKey: z.string(), }) .passthrough(), - body: z - .object({ - event: z.string(), //https://developers.sendinblue.com/docs/transactional-webhooks - "message-id": z.string(), - }) - , + body: z.object({ + event: z.string(), //https://developers.sendinblue.com/docs/transactional-webhooks + "message-id": z.string(), + }), response: { "200": z.unknown(), }, diff --git a/shared/routes/mailingList.routes.ts b/shared/routes/mailingList.routes.ts index 6359c22a4..0bbcc180b 100644 --- a/shared/routes/mailingList.routes.ts +++ b/shared/routes/mailingList.routes.ts @@ -50,13 +50,11 @@ export const zMailingListRoutes = { path: "/mailing-lists/:id/progress", params: z.object({ id: zObjectId }), response: { - "200": z - .object({ - status: ZMailingListDocument.shape.job_status, - process_progress: z.number(), - lines_count: z.number(), - }) - , + "200": z.object({ + status: ZMailingListDocument.shape.job_status, + process_progress: z.number(), + lines_count: z.number(), + }), }, securityScheme: { auth: "cookie-session", diff --git a/shared/routes/upload.routes.ts b/shared/routes/upload.routes.ts index 611b50a0d..9cc513607 100644 --- a/shared/routes/upload.routes.ts +++ b/shared/routes/upload.routes.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { zObjectId } from "../models/common"; import { ZUploadDocumentPublic } from "../models/document.model"; +import { ZMailingListWithDocument } from "../models/mailingList.model"; import { IRoutesDef } from "./common.routes"; export const zUploadRoutes = { @@ -18,6 +19,19 @@ export const zUploadRoutes = { ressources: {}, }, }, + "/admin/mailing-list/:user_id": { + method: "get", + path: "/admin/mailing-list/:user_id", + params: z.object({ user_id: zObjectId }), + response: { + "200": z.array(ZMailingListWithDocument), + }, + securityScheme: { + auth: "cookie-session", + access: "admin", + ressources: {}, + }, + }, "/admin/documents/types": { method: "get", path: "/admin/documents/types", @@ -35,13 +49,10 @@ export const zUploadRoutes = { "/admin/upload": { method: "post", path: "/admin/upload", - querystring: z - .object({ - type_document: z.string(), - import_content: z.string().optional(), - delimiter: z.string(), - }) - , + querystring: z.object({ + type_document: z.string(), + delimiter: z.string(), + }), body: z.unknown(), response: { "200": ZUploadDocumentPublic, diff --git a/shared/routes/upload.support.routes.ts b/shared/routes/upload.support.routes.ts index ed1603612..794b21ddc 100644 --- a/shared/routes/upload.support.routes.ts +++ b/shared/routes/upload.support.routes.ts @@ -9,11 +9,9 @@ export const zUploadSupportRoutes = { path: "/support/files-list", response: { "200": z.array( - z - .object({ - id: z.string(), - }) - + z.object({ + id: z.string(), + }) ), }, securityScheme: { @@ -25,11 +23,9 @@ export const zUploadSupportRoutes = { "/support/file/download": { method: "get", path: "/support/file/download", - querystring: z - .object({ - id: z.string(), - }) - , + querystring: z.object({ + id: z.string(), + }), response: { "200": z.unknown(), }, @@ -44,12 +40,10 @@ export const zUploadSupportRoutes = { "/support/upload": { method: "post", path: "/support/upload", - querystring: z - .object({ - verified_key: z.string(), - email: z.string().email(), - }) - , + querystring: z.object({ + verified_key: z.string(), + email: z.string().email(), + }), body: z.unknown(), response: { "200": ZResOk, @@ -61,11 +55,9 @@ export const zUploadSupportRoutes = { "/support/file/delete": { method: "delete", path: "/support/file/delete", - querystring: z - .object({ - id: z.string(), - }) - , + querystring: z.object({ + id: z.string(), + }), response: { "200": z.object({ success: z.literal(true) }), }, diff --git a/shared/routes/user.routes.ts b/shared/routes/user.routes.ts index 09ea77723..c78ba469b 100644 --- a/shared/routes/user.routes.ts +++ b/shared/routes/user.routes.ts @@ -4,7 +4,6 @@ import { zObjectId } from "../models/common"; import { ZUser, zUserWithPersonPublic } from "../models/user.model"; import { IRoutesDef, ZReqParamsSearchPagination } from "./common.routes"; - export const zUserAdminRoutes = { get: { "/admin/users": { @@ -34,13 +33,11 @@ export const zUserAdminRoutes = { "/admin/user": { method: "post", path: "/admin/user", - body: z - .object({ - email: ZUser.shape.email, - password: ZUser.shape.password, - organisation_id: z.string(), - }) - , + body: z.object({ + email: ZUser.shape.email, + password: ZUser.shape.password, + organisation_id: z.string(), + }), response: { "200": zUserWithPersonPublic, }, diff --git a/shared/routes/v1/deca.routes.ts b/shared/routes/v1/deca.routes.ts index 26ade3570..241b70156 100644 --- a/shared/routes/v1/deca.routes.ts +++ b/shared/routes/v1/deca.routes.ts @@ -7,35 +7,32 @@ const validationSchema = { .object({ siret: extensions.siret, }) - .describe("Organisation deca Request body") - , + .describe("Organisation deca Request body"), headers: ZReqHeadersAuthorization, response: { "200": z .object({ - contrats: z - .object({ - total: z.number(), - appr: z.number(), - prof: z.number(), - }) - , + contrats: z.object({ + total: z.number(), + appr: z.number(), + prof: z.number(), + }), premier_contrat: z .object({ date_debut_contrat: z.date().optional(), date_fin_contrat: z.date().optional(), }) - + .nullish(), dernier_contrat: z .object({ date_debut_contrat: z.date().optional(), date_fin_contrat: z.date().optional(), }) - + .nullish(), }) - + .describe("Organisation deca Response body"), }, } as const; diff --git a/shared/routes/v1/organisation.routes.ts b/shared/routes/v1/organisation.routes.ts index 9c67d9000..6cb27d78b 100644 --- a/shared/routes/v1/organisation.routes.ts +++ b/shared/routes/v1/organisation.routes.ts @@ -8,8 +8,7 @@ const validationSchema = { email: z.string().trim().email("Email non valide"), siret: extensions.siret, }) - .describe("Organisation validation Request body") - , + .describe("Organisation validation Request body"), headers: ZReqHeadersAuthorization, response: { "200": z @@ -17,7 +16,7 @@ const validationSchema = { is_valid: z.boolean(), on: z.enum(["email", "domain"]).optional(), }) - + .describe("Organisation validation Response body"), }, } as const; diff --git a/ui/app/admin/fichier/import/page.tsx b/ui/app/admin/fichier/import/page.tsx index 997d36ced..8104d5d75 100644 --- a/ui/app/admin/fichier/import/page.tsx +++ b/ui/app/admin/fichier/import/page.tsx @@ -14,11 +14,11 @@ import { IGetRoutes, IPostRoutes, IResponse } from "shared"; import ToggleSwitchInput from "../../../../components/form/ToggleSwitchInput"; import Toast, { useToast } from "../../../../components/toast/Toast"; import { apiGet, apiPost } from "../../../../utils/api.utils"; +import { queryClient } from "../../../../utils/query.utils"; import Breadcrumb, { PAGES } from "../../../components/breadcrumb/Breadcrumb"; interface FormValues extends Zod.input { file: FileList; - should_import_content: boolean; has_new_type_document: boolean; new_type_document: string; delimiter_other: string; @@ -54,7 +54,6 @@ const AdminImportPage = () => { } = useForm({ defaultValues: { type_document: "", - should_import_content: true, has_new_type_document: false, }, }); @@ -68,7 +67,6 @@ const AdminImportPage = () => { type_document, has_new_type_document, new_type_document, - should_import_content, delimiter, delimiter_other, }) => { @@ -81,7 +79,6 @@ const AdminImportPage = () => { querystring: { type_document: has_new_type_document ? new_type_document : type_document, delimiter: askDelimiterOther ? delimiter_other : delimiter, - ...(should_import_content && { import_content: "true" }), }, body: formData, }); @@ -101,6 +98,7 @@ const AdminImportPage = () => { console.error(error); } finally { setIsSubmitting(false); + queryClient.invalidateQueries({ queryKey: ["/admin/documents"] }); } }; @@ -152,16 +150,6 @@ const AdminImportPage = () => { }} /> )} - { const [isDeleting, setIsDeleting] = useState(false); const { data: documentLists, refetch } = useQuery({ - queryKey: ["documentLists"], + queryKey: ["/admin/documents"], queryFn: async () => apiGet("/admin/documents", {}), - refetchInterval: 1000, + refetchInterval: 15_000, }); const onDeleteDocument = async () => { diff --git a/ui/app/admin/utilisateurs/[id]/components/UserView.tsx b/ui/app/admin/utilisateurs/[id]/components/UserView.tsx index ad7180205..9a3384ff3 100644 --- a/ui/app/admin/utilisateurs/[id]/components/UserView.tsx +++ b/ui/app/admin/utilisateurs/[id]/components/UserView.tsx @@ -60,6 +60,17 @@ const UserView: FC = ({ user }) => { }, }} /> + + ); }; diff --git a/ui/app/admin/utilisateurs/[id]/liste-diffusion/page.tsx b/ui/app/admin/utilisateurs/[id]/liste-diffusion/page.tsx new file mode 100644 index 000000000..449c020e3 --- /dev/null +++ b/ui/app/admin/utilisateurs/[id]/liste-diffusion/page.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { Box, Typography } from "@mui/material"; +import { useQuery } from "@tanstack/react-query"; +import { IMailingListWithDocumentJson } from "shared/models/mailingList.model"; + +import { apiGet } from "../../../../../utils/api.utils"; +import Breadcrumb, { PAGES } from "../../../../components/breadcrumb/Breadcrumb"; +import ListMailingList from "../../../../liste-diffusion/components/ListMailingList"; + +interface Props { + params: { id: string }; +} + +const AdminImportPage = ({ params }: Props) => { + const { data: mailingLists, refetch } = useQuery({ + queryKey: ["/admin/mailing-list/:user_id", { user_id: params.id }], + queryFn: async () => apiGet("/admin/mailing-list/:user_id", { params: { user_id: params.id } }), + }); + + return ( + <> + + + + {PAGES.adminListeDiffusion(params.id).title} + + + + + ); +}; +export default AdminImportPage; diff --git a/ui/app/components/breadcrumb/Breadcrumb.tsx b/ui/app/components/breadcrumb/Breadcrumb.tsx index 4d2784cb6..74f164726 100644 --- a/ui/app/components/breadcrumb/Breadcrumb.tsx +++ b/ui/app/components/breadcrumb/Breadcrumb.tsx @@ -46,6 +46,10 @@ export const PAGES = { title: "Gestion des fichiers", path: "/admin/fichier", }), + adminListeDiffusion: (id: string) => ({ + title: "Gestion des listes de diffusion", + path: `/admin/utilisateurs/${id}/liste-diffusion`, + }), adminImport: () => ({ title: "Import de fichier", path: "/admin/fichier/import",