diff --git a/.infra/files/configs/mongodb/seed.gpg b/.infra/files/configs/mongodb/seed.gpg index 5b94f01e05..af49a2366e 100644 --- a/.infra/files/configs/mongodb/seed.gpg +++ b/.infra/files/configs/mongodb/seed.gpg @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9fdaa57b6d751a468c9d66696ae44aab98443b2a58586ca7866d38d3e6803958 -size 644811608 +oid sha256:761323cfd10877427e2098e94fa2b6ce65d229960dea0c8ba79f079d4a56a19d +size 580408856 diff --git a/.talismanrc b/.talismanrc index a76b7068e6..d48901fd10 100644 --- a/.talismanrc +++ b/.talismanrc @@ -22,7 +22,7 @@ fileignoreconfig: - filename: .infra/files/configs/mailpit/auth checksum: 0c02db6c367747bfd696e8c93ed78c456fe311452e7a4ee1d7d5acc88f3b2189 - filename: .infra/files/configs/mongodb/seed.gpg - checksum: 3cb836a5c356c64ac6634c8aeb03b9737950a66ed61d9ae8f6b1528ad7b17434 + checksum: e09bc952da0dbeb33331dc317980e6537dc4d37674ca08748246d4d093b82294 - filename: .infra/vault/vault.yml checksum: 1a62fbc5e3e877b5241d59c61fda05a61b6c8bb4fb7f5662f2f1bc44c288782b - filename: server/.env.test diff --git a/server/src/commands.ts b/server/src/commands.ts index e6ffb376cc..30d48b4d70 100644 --- a/server/src/commands.ts +++ b/server/src/commands.ts @@ -164,6 +164,12 @@ program.command("migrations:status").description("Check migrations status").acti program.command("migrations:create").description("Run migrations create").requiredOption("-d, --description ", "description").action(createJobAction("migrations:create")) +// Temporaire, one shot à executer en recette et prod +program + .command("recruiters:set-missing-job-start-date") + .description("Récupération des geo_coordinates manquants dans la collection Recruiters") + .option("-q, --queued", "Run job asynchronously", false) + .action(createJobAction("recruiters:set-missing-job-start-date")) // Temporaire, one shot à executer en recette et prod program .command("recruiters:get-missing-geocoordinates") diff --git a/server/src/http/controllers/jobs/jobs.controller.ts b/server/src/http/controllers/jobs/jobs.controller.ts index 28208a0600..505473fe72 100644 --- a/server/src/http/controllers/jobs/jobs.controller.ts +++ b/server/src/http/controllers/jobs/jobs.controller.ts @@ -29,8 +29,6 @@ import { getPeJobFromId } from "../../../services/pejob.service" import { getFicheMetierRomeV3FromDB } from "../../../services/rome.service" import { Server } from "../../server" -import { createDelegationSchema, createEstablishmentSchema, getEstablishmentEntitySchema, updateJobSchema } from "./jobs.validators" - const config = { rateLimit: { max: 5, @@ -45,11 +43,9 @@ export default (server: Server) => { schema: zRoutes.get["/v1/jobs/establishment"], config, onRequest: server.auth(zRoutes.get["/v1/jobs/establishment"]), - // TODO: AttachValidation Error ? }, async (req, res) => { const { establishment_siret, email } = req.query - await getEstablishmentEntitySchema.validateAsync({ establishment_siret, email }, { abortEarly: false }) const establishment = await Recruiter.findOne({ establishment_siret, email }).lean() @@ -95,8 +91,6 @@ export default (server: Server) => { }, async (req, res) => { const { body } = req - // Validate establishment parameters - await createEstablishmentSchema.validateAsync(body, { abortEarly: false }) const { first_name, last_name, phone, email, origin, idcc, establishment_siret } = body const user = getUserFromRequest(req, zRoutes.post["/v1/jobs/establishment"]).value @@ -161,6 +155,7 @@ export default (server: Server) => { job_level_label: body.job_level_label, job_start_date: new Date(body.job_start_date), job_description: body.job_description, + job_employer_description: body.job_employer_description, job_creation_date: dayjs().toDate(), job_expiration_date: addExpirationPeriod(dayjs()).toDate(), job_status: JOB_STATUS.ACTIVE, @@ -196,8 +191,6 @@ export default (server: Server) => { return res.status(400).send({ error: true, message: "Job does not exists" }) } - await updateJobSchema.validateAsync(req.body, { abortEarly: false }) - const updatedRecruiter = await patchOffre(jobId, req.body) return res.status(200).send(updatedRecruiter) @@ -260,8 +253,6 @@ export default (server: Server) => { return res.status(400).send({ error: true, message: "Job does not exists" }) } - await createDelegationSchema.validateAsync(req.body) - const updatedRecruiter = await createJobDelegations({ jobId: jobId.toString(), etablissementCatalogueIds: req.body.establishmentIds }) res.status(200) diff --git a/server/src/http/controllers/jobs/jobs.validators.ts b/server/src/http/controllers/jobs/jobs.validators.ts deleted file mode 100644 index af3b1f513c..0000000000 --- a/server/src/http/controllers/jobs/jobs.validators.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Joi from "joi" -import { validateSIRET } from "shared/validators/siretValidator" - -export const getEstablishmentEntitySchema = Joi.object({ - establishment_siret: Joi.string() - .pattern(/^[0-9]+$/, "Le siret est composé uniquement de chiffres") - .min(14) - .max(14) - .custom((value) => { - if (!validateSIRET(value)) { - throw new Error("Le siret ne respecte pas l'algorithme luhn (https://fr.wikipedia.org/wiki/Formule_de_Luhn)") - } - - return value - }) - .required(), - email: Joi.string().email().required(), -}) - -export const createEstablishmentSchema = Joi.object({ - establishment_siret: Joi.string() - .pattern(/^[0-9]+$/, "Le siret est composé uniquement de chiffres") - .min(14) - .max(14) - .required(), - first_name: Joi.string().required(), - last_name: Joi.string().required(), - email: Joi.string().email().required(), - phone: Joi.string() - .pattern(/^[0-9]+$/) - .min(10) - .max(10), - idcc: Joi.string(), - origin: Joi.string(), -}) - -export const updateJobSchema = Joi.object({ - job_level_label: Joi.string().valid( - "Indifférent", - "Cap, autres formations niveau (Infrabac)", - "BP, Bac, autres formations niveau (Bac)", - "BTS, DEUST, autres formations niveau (Bac+2)", - "Licence, autres formations niveau (Bac+3)", - "Master, titre ingénieur, autres formations niveau (Bac+5)" - ), - job_start_date: Joi.date().greater("now").iso(), - job_type: Joi.array().items(Joi.string().valid("Apprentissage", "Professionalisation")), - job_expiration_date: Joi.string(), - is_disabled_elligible: Joi.boolean(), - job_count: Joi.number(), - job_duration: Joi.number().min(6).max(36), - job_rythm: Joi.string().valid("Indifférent", "2 jours / 3 jours", "1 semaine / 1 semaine", "2 semaines / 3 semaines", "6 semaines / 6 semaines"), - job_description: Joi.string(), - job_employer_description: Joi.string(), - custom_address: Joi.string(), - custom_geo_coordinates: Joi.string().when("custom_address", { is: Joi.exist(), then: Joi.required() }), -}) - -export const createDelegationSchema = Joi.object({ - establishmentIds: Joi.array().items(Joi.string().required()).required(), -}) diff --git a/server/src/http/routes/appointmentRequest.controller.ts b/server/src/http/routes/appointmentRequest.controller.ts index 11b72e5d87..02328697c7 100644 --- a/server/src/http/routes/appointmentRequest.controller.ts +++ b/server/src/http/routes/appointmentRequest.controller.ts @@ -137,7 +137,7 @@ export default (server: Server) => { const [emailCandidat, emailCfa] = await Promise.all([ mailer.sendEmail({ to: user.email, - subject: `Le centre de formation a bien reçu votre demande de contact !`, + subject: `Votre demande de RDV auprès de ${eligibleTrainingsForAppointment.etablissement_formateur_raison_sociale}`, template: getStaticFilePath("./templates/mail-candidat-confirmation-rdv.mjml.ejs"), data: mailData, }), diff --git a/server/src/http/routes/etablissementRecruteur.controller.ts b/server/src/http/routes/etablissementRecruteur.controller.ts index 53c9a861b7..a9f3d77c15 100644 --- a/server/src/http/routes/etablissementRecruteur.controller.ts +++ b/server/src/http/routes/etablissementRecruteur.controller.ts @@ -70,17 +70,9 @@ export default (server: Server) => { } const result = await getEntrepriseDataFromSiret({ siret, cfa_delegated_siret }) + if ("error" in result) { - switch (result.errorCode) { - case BusinessErrorCodes.IS_CFA: { - throw Boom.badRequest(result.message, { - isCfa: true, - }) - } - default: { - throw Boom.badRequest(result.message) - } - } + throw Boom.badRequest(result.message, result) } else { return res.status(200).send(result) } diff --git a/server/src/jobs/jobs.ts b/server/src/jobs/jobs.ts index a45bbb1599..d5b325e493 100644 --- a/server/src/jobs/jobs.ts +++ b/server/src/jobs/jobs.ts @@ -28,6 +28,7 @@ import { removeIsDelegatedFromJobs } from "./lba_recruteur/formulaire/misc/remov import { removeVersionKeyFromAllCollections } from "./lba_recruteur/formulaire/misc/removeVersionKeyFromAllCollections" import { repiseGeocoordinates } from "./lba_recruteur/formulaire/misc/repriseGeocoordinates" import { updateAddressDetailOnRecruitersCollection } from "./lba_recruteur/formulaire/misc/updateAddressDetailOnRecruitersCollection" +import { updateMissingStartDate } from "./lba_recruteur/formulaire/misc/updateMissingStartDate" import { relanceFormulaire } from "./lba_recruteur/formulaire/relanceFormulaire" import { generateIndexes } from "./lba_recruteur/indexes/generateIndexes" import { relanceOpco } from "./lba_recruteur/opco/relanceOpco" @@ -189,6 +190,8 @@ export async function runJob(job: IInternalJobsCronTask | IInternalJobsSimple): return CronsMap[job.name].handler() } switch (job.name) { + case "recruiters:set-missing-job-start-date": + return updateMissingStartDate() case "recruiters:get-missing-geocoordinates": return repiseGeocoordinates() case "recruiters:get-missing-address-detail": diff --git a/server/src/jobs/lba_recruteur/formulaire/misc/exportPE.ts b/server/src/jobs/lba_recruteur/formulaire/misc/exportPE.ts index 74aa96e71f..b711b3b193 100644 --- a/server/src/jobs/lba_recruteur/formulaire/misc/exportPE.ts +++ b/server/src/jobs/lba_recruteur/formulaire/misc/exportPE.ts @@ -191,11 +191,12 @@ export const exportPE = async (): Promise => { try { const csvPath = new URL("./exportPE.csv", import.meta.url) const buffer: any[] = [] + const threshold = dayjs().subtract(30, "days").toDate() // Retrieve only active offers const offres: any[] = await db .collection("jobs") - .find({ job_status: JOB_STATUS.ACTIVE, recruiterStatus: RECRUITER_STATUS.ACTIF, geo_coordinates: { $nin: ["NOT FOUND", null] } }) + .find({ job_status: JOB_STATUS.ACTIVE, recruiterStatus: RECRUITER_STATUS.ACTIF, geo_coordinates: { $nin: ["NOT FOUND", null] }, job_update_date: { $gte: threshold } }) .toArray() logger.info(`get info from ${offres.length} offers...`) diff --git a/server/src/jobs/lba_recruteur/formulaire/misc/updateMissingStartDate.ts b/server/src/jobs/lba_recruteur/formulaire/misc/updateMissingStartDate.ts new file mode 100644 index 0000000000..38662e1d74 --- /dev/null +++ b/server/src/jobs/lba_recruteur/formulaire/misc/updateMissingStartDate.ts @@ -0,0 +1,19 @@ +import { logger } from "../../../../common/logger" +import { Recruiter } from "../../../../common/model/index" +import { asyncForEach } from "../../../../common/utils/asyncUtils" + +export const updateMissingStartDate = async () => { + logger.info("Start update missing job_start_date") + const forms = await Recruiter.find({ "jobs.job_start_date": null }) + await asyncForEach(forms, async (form) => { + await asyncForEach(form.jobs, async (job) => { + if (job.job_start_date === null) { + if (job.job_creation_date) { + job.job_start_date = job.job_creation_date + } + } + }) + await form.save({ timestamps: false }) + }) + logger.info("End update missing job_start_date") +} diff --git a/server/src/services/application.service.ts b/server/src/services/application.service.ts index 1b04b42901..a9ded2a57c 100644 --- a/server/src/services/application.service.ts +++ b/server/src/services/application.service.ts @@ -193,18 +193,18 @@ export const sendApplication = async ({ encryptedId, }) + const searched_for_job_label = query.searched_for_job_label || "" + const buildTopic = (aCompanyType, aJobTitle) => { let res = "Candidature" if (aCompanyType === "matcha") { res = `Candidature en alternance - ${aJobTitle}` } else { - res = `Candidature spontanée en alternance` + res = `Candidature spontanée en alternance ${searched_for_job_label ? "- " + searched_for_job_label : ""}` } return res } - const searched_for_job_label = query.searched_for_job_label || "" - // Sends acknowledge email to "candidate" and application email to "company" const [emailCandidat, emailCompany] = await Promise.all([ mailer.sendEmail({ @@ -339,7 +339,7 @@ interface IApplicationTemplates { } /** * @description Return template file path for given type - * @param {string} type + * @param {string} applicationType * @return {IApplicationTemplates} */ const getEmailTemplates = (applicationType: string): IApplicationTemplates => { diff --git a/server/src/services/constant.service.ts b/server/src/services/constant.service.ts index 702b281543..ce7477ebac 100644 --- a/server/src/services/constant.service.ts +++ b/server/src/services/constant.service.ts @@ -109,4 +109,5 @@ export type IRole = IRoles[keyof IRoles] export enum BusinessErrorCodes { IS_CFA = "IS_CFA", ALREADY_EXISTS = "ALREADY_EXISTS", + CLOSED = "CLOSED", } diff --git a/server/src/services/etablissement.service.ts b/server/src/services/etablissement.service.ts index 5d7eb7c884..748ba7540c 100644 --- a/server/src/services/etablissement.service.ts +++ b/server/src/services/etablissement.service.ts @@ -535,15 +535,18 @@ export const validateCreationEntrepriseFromCfa = async ({ siret, cfa_delegated_s export const getEntrepriseDataFromSiret = async ({ siret, cfa_delegated_siret }: { siret: string; cfa_delegated_siret?: string }) => { const result = await getEtablissementFromGouv(siret) + if (!result) { return errorFactory("Le numéro siret est invalide.") } + const { etat_administratif, activite_principale } = result.data + if (etat_administratif === "F") { - return errorFactory("Cette entreprise est considérée comme fermée.") + return errorFactory("Cette entreprise est considérée comme fermée.", BusinessErrorCodes.CLOSED) } // Check if a CFA already has the company as partenaire - if (!cfa_delegated_siret) { + if (cfa_delegated_siret === "undefined") { // Allow cfa to add themselves as a company if (activite_principale.code.startsWith("85")) { return errorFactory("Le numéro siret n'est pas référencé comme une entreprise.", BusinessErrorCodes.IS_CFA) diff --git a/shared/constants/recruteur.ts b/shared/constants/recruteur.ts index ebb6cebfad..1a485045bc 100644 --- a/shared/constants/recruteur.ts +++ b/shared/constants/recruteur.ts @@ -89,6 +89,7 @@ export const TRAINING_RYTHM = { "1S1S": "1 semaine / 1 semaine", "2S3S": "2 semaines / 3 semaines", "6S6S": "6 semaines / 6 semaines", + NONRENSEIGNE: "Non renseigné", } export const ROLES = { @@ -100,4 +101,5 @@ export const ROLES = { export enum BusinessErrorCodes { IS_CFA = "IS_CFA", ALREADY_EXISTS = "ALREADY_EXISTS", + CLOSED = "CLOSED", } diff --git a/shared/helpers/dayjs.ts b/shared/helpers/dayjs.ts new file mode 100644 index 0000000000..529f1ec953 --- /dev/null +++ b/shared/helpers/dayjs.ts @@ -0,0 +1,29 @@ +import dayjs from "dayjs" +// eslint-disable-next-line import/extensions +import "dayjs/locale/fr.js" +import advancedFormat from "dayjs/plugin/advancedFormat.js" +import customParseFormat from "dayjs/plugin/customParseFormat.js" +import duration from "dayjs/plugin/duration.js" +import isBetween from "dayjs/plugin/isBetween.js" +import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js" +import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js" +import isYesterday from "dayjs/plugin/isYesterday.js" +import localizedFormat from "dayjs/plugin/localizedFormat.js" +import timezone from "dayjs/plugin/timezone.js" +import utc from "dayjs/plugin/utc.js" + +dayjs.locale("fr") +dayjs.extend(utc) +dayjs.extend(duration) +dayjs.extend(timezone) +dayjs.extend(isSameOrBefore) +dayjs.extend(isSameOrAfter) +dayjs.extend(isBetween) +dayjs.extend(customParseFormat) +dayjs.extend(advancedFormat) +dayjs.extend(isYesterday) +dayjs.extend(localizedFormat) + +export type TDayjs = dayjs.Dayjs + +export default dayjs diff --git a/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap b/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap index 75d3ef113c..e945372376 100644 --- a/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap +++ b/shared/helpers/openapi/__snapshots__/generateOpenapi.test.ts.snap @@ -640,6 +640,14 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` }, "job_level_label": { "description": "Niveau de formation visé en fin de stage", + "enum": [ + "Cap, autres formations niveau (Infrabac)", + "BP, Bac, autres formations niveau (Bac)", + "BTS, DEUST, autres formations niveau (Bac+2)", + "Licence, autres formations niveau (Bac+3)", + "Master, titre ingénieur, autres formations niveau (Bac+5)", + "Indifférent", + ], "type": [ "string", "null", @@ -654,6 +662,14 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` }, "job_rythm": { "description": "Répartition de la présence de l'alternant en formation/entreprise", + "enum": [ + "Indifférent", + "2 jours / 3 jours", + "1 semaine / 1 semaine", + "2 semaines / 3 semaines", + "6 semaines / 6 semaines", + "Non renseigné", + ], "type": [ "string", "null", @@ -748,6 +764,7 @@ exports[`generateOpenApiSchema > should generate proper schema 1`] = ` }, }, "required": [ + "job_start_date", "rome_code", "job_status", "job_type", @@ -5117,7 +5134,6 @@ L'email est envoyé depuis l'adresse générique \\"Ne pas répondre\\" de La bo "type": "string", }, "job_start_date": { - "pattern": "\\\\d{4}-[01]\\\\d-[0-3]\\\\d", "type": "string", }, "job_type": { @@ -5254,6 +5270,14 @@ L'email est envoyé depuis l'adresse générique \\"Ne pas répondre\\" de La bo }, "job_level_label": { "description": "Niveau de formation visé en fin de stage", + "enum": [ + "Cap, autres formations niveau (Infrabac)", + "BP, Bac, autres formations niveau (Bac)", + "BTS, DEUST, autres formations niveau (Bac+2)", + "Licence, autres formations niveau (Bac+3)", + "Master, titre ingénieur, autres formations niveau (Bac+5)", + "Indifférent", + ], "type": [ "string", "null", @@ -5261,6 +5285,14 @@ L'email est envoyé depuis l'adresse générique \\"Ne pas répondre\\" de La bo }, "job_rythm": { "description": "Répartition de la présence de l'alternant en formation/entreprise", + "enum": [ + "Indifférent", + "2 jours / 3 jours", + "1 semaine / 1 semaine", + "2 semaines / 3 semaines", + "6 semaines / 6 semaines", + "Non renseigné", + ], "type": [ "string", "null", diff --git a/shared/models/job.model.ts b/shared/models/job.model.ts index 0db55aadea..ef4849b72b 100644 --- a/shared/models/job.model.ts +++ b/shared/models/job.model.ts @@ -1,6 +1,7 @@ import { Jsonify } from "type-fest" -import { TRAINING_CONTRACT_TYPE } from "../constants/recruteur" +import { NIVEAUX_POUR_LBA, TRAINING_CONTRACT_TYPE, TRAINING_RYTHM } from "../constants/recruteur" +import dayjs from "../helpers/dayjs" import { z } from "../helpers/zodWithOpenApi" import { zObjectId } from "./common" @@ -13,6 +14,8 @@ export enum JOB_STATUS { EN_ATTENTE = "En attente", } +const allJobRythm = Object.values(TRAINING_RYTHM) +const allJobLevel = [...Object.values(NIVEAUX_POUR_LBA), "Indifférent"] const allJobStatus = Object.values(JOB_STATUS) const allJobType = Object.values(TRAINING_CONTRACT_TYPE) export const ZJobType = z.array(z.enum([allJobType[0], ...allJobType.slice(1)])).describe("Type de contrat") @@ -30,8 +33,11 @@ const ZJobFields = z .object({ rome_label: z.string().nullish().describe("Libellé du métier concerné"), rome_appellation_label: z.string().nullish().describe("Libellé de l'appelation ROME"), - job_level_label: z.string().nullish().describe("Niveau de formation visé en fin de stage"), - job_start_date: z.coerce.date().nullish().describe("Date de début de l'alternance"), + job_level_label: z + .enum([allJobLevel[0], ...allJobLevel.slice(1)]) + .nullish() + .describe("Niveau de formation visé en fin de stage"), + job_start_date: z.coerce.date().describe("Date de début de l'alternance"), job_description: z.string().nullish().describe("Description de l'offre d'alternance"), job_employer_description: z.string().nullish().describe("Description de l'employer proposant l'offre d'alternance"), rome_code: z.array(z.string()).describe("Liste des romes liés au métier"), @@ -51,7 +57,10 @@ const ZJobFields = z is_disabled_elligible: z.boolean().nullish().describe("Poste ouvert aux personnes en situation de handicap"), job_count: z.number().nullish().describe("Nombre de poste(s) ouvert(s) pour cette offre"), job_duration: z.number().nullish().describe("Durée du contrat en année"), - job_rythm: z.string().nullish().describe("Répartition de la présence de l'alternant en formation/entreprise"), + job_rythm: z + .enum([allJobRythm[0], ...allJobRythm.slice(1)]) + .nullish() + .describe("Répartition de la présence de l'alternant en formation/entreprise"), custom_address: z.string().nullish().describe("Adresse personnalisée de l'entreprise"), custom_geo_coordinates: z.string().nullish().describe("Latitude/Longitude de l'adresse personnalisée de l'entreprise"), stats_detail_view: z.number().nullish().describe("Nombre de vues de la page de détail"), @@ -72,7 +81,6 @@ export const ZJobWrite = ZJobFields.pick({ rome_label: true, job_type: true, job_level_label: true, - job_start_date: true, is_disabled_elligible: true, job_count: true, job_duration: true, @@ -80,6 +88,11 @@ export const ZJobWrite = ZJobFields.pick({ job_description: true, delegations: true, }) + .extend({ + job_start_date: ZJobFields.shape.job_start_date.refine((date) => dayjs(date).isSameOrAfter(dayjs().utc().startOf("days")), { + message: "job_start_date must be greater or equal to today's date", + }), + }) .strict() .openapi("JobWrite") diff --git a/shared/package.json b/shared/package.json index 9657c5002a..68eb51c099 100644 --- a/shared/package.json +++ b/shared/package.json @@ -18,6 +18,7 @@ "@asteasolutions/zod-to-openapi": "^5.5.0", "@fastify/swagger": "^8.11.0", "bson": "^1.1.4", + "dayjs": "^1.11.10", "lodash-es": "^4.17.21", "luhn": "^2.4.1", "type-fest": "^4.3.1", diff --git a/shared/routes/v1Jobs.routes.ts b/shared/routes/v1Jobs.routes.ts index a30f3d406a..2072e919ee 100644 --- a/shared/routes/v1Jobs.routes.ts +++ b/shared/routes/v1Jobs.routes.ts @@ -1,3 +1,4 @@ +import dayjs from "../helpers/dayjs" import { extensions } from "../helpers/zodHelpers/zodPrimitives" import { z } from "../helpers/zodWithOpenApi" import { ZJob } from "../models" @@ -385,7 +386,7 @@ export const zV1JobsRoutes = { .enum(["Indifférent", "2 jours / 3 jours", "1 semaine / 1 semaine", "2 semaines / 3 semaines", "6 semaines / 6 semaines", "Non renseigné"]) .optional() .default("Non renseigné"), - job_start_date: z.string().regex(/\d{4}-[01]\d-[0-3]\d/, "expecting a date with format YYYY-MM-DD"), + job_start_date: z.coerce.date().refine((date) => dayjs(date).isSameOrAfter(dayjs().utc()), { message: "job_start_date must be greater or equal to today's date" }), job_employer_description: z.string().optional(), job_description: z.string().optional(), custom_address: z.string().optional(), diff --git a/ui/components/espace_pro/AjouterVoeux.tsx b/ui/components/espace_pro/AjouterVoeux.tsx index 739a0e164a..77bc149e87 100644 --- a/ui/components/espace_pro/AjouterVoeux.tsx +++ b/ui/components/espace_pro/AjouterVoeux.tsx @@ -314,7 +314,7 @@ const AjouterVoeuxForm = (props) => { - {(user && user.type !== AUTHTYPE.ENTREPRISE) || type !== AUTHTYPE.ENTREPRISE ? ( + {Boolean((user && user.type !== AUTHTYPE.ENTREPRISE) || (type && type !== AUTHTYPE.ENTREPRISE)) && ( Rythme de l'alternance (formation / entreprise) Facultatif @@ -330,7 +330,7 @@ const AjouterVoeuxForm = (props) => { {errors.job_rythm && touched.job_rythm && {errors.job_rythm as string}} - ) : null} + )} {(values.job_description || organisation?.includes("akto")) && ( diff --git a/ui/components/espace_pro/Authentification/CreationCompte.tsx b/ui/components/espace_pro/Authentification/CreationCompte.tsx index c5a176ea28..cce37aa5c5 100644 --- a/ui/components/espace_pro/Authentification/CreationCompte.tsx +++ b/ui/components/espace_pro/Authentification/CreationCompte.tsx @@ -32,7 +32,7 @@ const CreationCompteForm = ({ type, setQualiopi, setBandeau, origin }) => { }) } else { setFieldError("establishment_siret", entrepriseData.message) - setIsCfa(entrepriseData?.data?.isCfa) + setIsCfa(entrepriseData?.errorCode === "IS_CFA") setSubmitting(false) } } else { diff --git a/ui/components/espace_pro/Layout/Header.tsx b/ui/components/espace_pro/Layout/Header.tsx index 1e5b8f2722..2a56f4d000 100644 --- a/ui/components/espace_pro/Layout/Header.tsx +++ b/ui/components/espace_pro/Layout/Header.tsx @@ -68,7 +68,7 @@ const Header = () => { )} {user.type === AUTHTYPE.ADMIN && ( <> - router.push("/espace-pro/administration/users")}>Gestion des recruiteurs + router.push("/espace-pro/administration/users")}>Gestion des recruteurs router.push("/espace-pro/admin/utilisateurs")}>Gestion des administrateurs router.push("/espace-pro/admin")}>Rendez-vous Apprentissage diff --git a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/offre/[jobId].tsx b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/offre/[jobId].tsx index 7103849b43..305baf648f 100644 --- a/ui/pages/espace-pro/administration/entreprise/[establishment_id]/offre/[jobId].tsx +++ b/ui/pages/espace-pro/administration/entreprise/[establishment_id]/offre/[jobId].tsx @@ -4,14 +4,10 @@ import { Layout } from "../../../../../../components/espace_pro" import CreationOffre from "../../../../../../components/espace_pro/CreationOffre" import { authProvider, withAuth } from "../../../../../../components/espace_pro/withAuth" -function EntrepriseCreationOffre() { - return -} - function EntrepriseCreationOffrePage() { return ( - + ) } diff --git a/ui/pages/espace-pro/administration/users/index.tsx b/ui/pages/espace-pro/administration/users/index.tsx index 2b94ac6159..2ff486aaf2 100644 --- a/ui/pages/espace-pro/administration/users/index.tsx +++ b/ui/pages/espace-pro/administration/users/index.tsx @@ -227,7 +227,7 @@ function Users() { - Gestion des recruiteurs + Gestion des recruteurs diff --git a/ui/utils/api.ts b/ui/utils/api.ts index 9906e6b5d3..12318799a9 100644 --- a/ui/utils/api.ts +++ b/ui/utils/api.ts @@ -78,9 +78,8 @@ export const getEntrepriseInformation = async (siret: string, options: { cfa_del return data } catch (error: any) { captureException(error) - if (error && typeof error === "object" && "response" in error) { - const payload: { data?: { isCfa?: boolean }; error: boolean; statusCode: number; message: string } = error.response.data - return payload + if (error.context?.statusCode === 400) { + return error.context.errorData } else { return { statusCode: 500, message: "unkown error", error: true } } diff --git a/yarn.lock b/yarn.lock index 1aac0632de..89ca68b5ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17578,6 +17578,7 @@ __metadata: "@fastify/swagger": ^8.11.0 "@types/boom": ^7.3.3 bson: ^1.1.4 + dayjs: ^1.11.10 eslint-plugin-zod: ^1.4.0 lodash-es: ^4.17.21 luhn: ^2.4.1