diff --git a/front/src/app/hooks/jwt.hooks.ts b/front/src/app/hooks/jwt.hooks.ts index 36563bdffd..25c5c5c39a 100644 --- a/front/src/app/hooks/jwt.hooks.ts +++ b/front/src/app/hooks/jwt.hooks.ts @@ -14,7 +14,11 @@ export const useAdminToken = () => { return rawToken; }; -export const useJwt = (route: Route) => { +export const useJwt = ( + route: Route< + typeof routes.conventionDocument | typeof routes.assessmentDocument + >, +) => { const jwtQueryParam: ConventionSupportedJwt | undefined = route.params?.jwt; const inclusionConnectJwt = useAppSelector( authSelectors.inclusionConnectToken, diff --git a/front/src/app/pages/convention/AssessmentDocumentPage.tsx b/front/src/app/pages/convention/AssessmentDocumentPage.tsx new file mode 100644 index 0000000000..58072c4afd --- /dev/null +++ b/front/src/app/pages/convention/AssessmentDocumentPage.tsx @@ -0,0 +1,224 @@ +import { fr } from "@codegouvfr/react-dsfr"; +import React, { useEffect } from "react"; +import { Document, Loader, MainWrapper } from "react-design-system"; +import { useDispatch } from "react-redux"; +import { + computeTotalHours, + convertLocaleDateToUtcTimezoneDate, + isStringDate, + makeSiretDescriptionLink, + toDisplayedDate, +} from "shared"; +import { useConvention } from "src/app/hooks/convention.hooks"; +import { useJwt } from "src/app/hooks/jwt.hooks"; +import { useAppSelector } from "src/app/hooks/reduxHooks"; +import { routes } from "src/app/routes/routes"; +import { assessmentSelectors } from "src/core-logic/domain/assessment/assessment.selectors"; +import { assessmentSlice } from "src/core-logic/domain/assessment/assessment.slice"; +import { Route } from "type-route"; +import logoIf from "/assets/img/logo-if.svg"; +import logoRf from "/assets/img/logo-rf.svg"; + +type AssessmentDocumentPageProps = { + route: Route; +}; + +export const AssessmentDocumentPage = ({ + route, +}: AssessmentDocumentPageProps) => { + const dispatch = useDispatch(); + const { jwt, jwtPayload } = useJwt(route); + const conventionId = jwtPayload.applicationId; + const assessment = useAppSelector(assessmentSelectors.currentAssessment); + const isAssessmentLoading = useAppSelector(assessmentSelectors.isLoading); + const { convention, isLoading: isConventionLoading } = useConvention({ + jwt, + conventionId: jwtPayload.applicationId, + }); + + const logos = [ + Logo RF, + , + ]; + + useEffect(() => { + dispatch( + assessmentSlice.actions.getAssessmentRequested({ + conventionId, + jwt, + feedbackTopic: "assessment", + }), + ); + }, [dispatch, conventionId, jwt]); + + if (isConventionLoading || isAssessmentLoading) return ; + if (!convention) return

Pas de convention correspondante trouvée

; + if (!assessment) return

Pas de bilan correspondant trouvé

; + + return ( + + +

+ Identifiant de la convention: {convention.id} +

+

+ {convention.internshipKind === "immersion" + ? "Cette immersion s'est déroulée" + : "Ce mini-stage s'est déroulé"}{" "} + du{" "} + {toDisplayedDate({ + date: convertLocaleDateToUtcTimezoneDate( + new Date(convention.dateStart), + ), + })}{" "} + au{" "} + {toDisplayedDate({ + date: convertLocaleDateToUtcTimezoneDate( + new Date(convention.dateEnd), + ), + })}{" "} + au sein de {convention.businessName} (Siret n° :{" "} + + {convention.siret} + + ) à l'adresse suivante {convention.immersionAddress}. +

+

+ Métier observé : {convention.immersionAppellation.appellationLabel}. +

+

+ Objectif{" "} + {convention.internshipKind === "immersion" + ? "de l'immersion" + : "du mini-stage"}{" "} + : {convention.immersionObjective}. +

+

+ Le tuteur {convention.establishmentTutor.firstName}{" "} + {convention.establishmentTutor.lastName} de{" "} + {convention.signatories.beneficiary.firstName}{" "} + {convention.signatories.beneficiary.lastName} a évalué son{" "} + {convention.internshipKind === "immersion" + ? "immersion" + : "mini-stage"} + . +

+

+ Voici les informations saisies durant{" "} + {convention.internshipKind === "immersion" + ? "cette immersion" + : "ce mini-stage"}{" "} + : +

+
    +
  • + {convention.internshipKind === "immersion" + ? "L'immersion a-t-elle" + : "Le mini-stage a-il"}{" "} + eu lieu ? {assessment.status === "DID_NOT_SHOW" ? "Non" : "Oui"} +
  • + {assessment.status === "COMPLETED" && ( + <> +
  • + Nombre d'heures totales de l'immersion :{" "} + {computeTotalHours({ + convention, + assessmentStatus: assessment.status, + missedHours: 0, + })} +
  • +
  • + Date réelle de fin de l'immersion :{" "} + {toDisplayedDate({ + date: new Date(convention.dateEnd), + withHours: false, + })} +
  • + + )} + {assessment.status === "PARTIALLY_COMPLETED" && ( + <> +
  • + Nombre d'heures totales de l'immersion :{" "} + {computeTotalHours({ + convention, + assessmentStatus: assessment.status, + missedHours: + assessment.status === "PARTIALLY_COMPLETED" + ? assessment.numberOfMissedHours + : 0, + })} +
  • +
  • + Date réelle de fin de l'immersion :{" "} + {isStringDate(assessment.lastDayOfPresence ?? "") + ? toDisplayedDate({ + date: new Date(assessment.lastDayOfPresence ?? ""), + withHours: false, + }) + : "DATE INVALIDE"} +
  • + + )} +
+

+ Résultats de{" "} + {convention.internshipKind === "immersion" + ? "l'immersion" + : "du mini-stage"}{" "} + : +

+
    +
  • Embauche ? {assessment.endedWithAJob ? "Oui" : "Non"}
  • + {assessment.endedWithAJob && ( + <> +
  • + {toDisplayedDate({ + date: convertLocaleDateToUtcTimezoneDate( + new Date(assessment.contractStartDate), + ), + })} +
  • +
  • Type de contrat : {assessment.typeOfContract}
  • + + )} +
+

Appréciation générale :

+

{assessment.establishmentFeedback}

+

Conseils pour la suite :

+

{assessment.establishmentAdvices}

+
+
+

+ Ce document a été rédigé dans le cadre{" "} + {convention.internshipKind === "immersion" + ? "de l'immersion professionelle réalisée" + : "du mini-stage réalisé"} + par {convention.signatories.beneficiary.firstName}{" "} + {convention.signatories.beneficiary.lastName} chez{" "} + {convention.businessName}. Il peut être utilisé comme référence lors + de futurs entretiens ou dans le dossier de candidature pour + valoriser l'expérience acquise au sein de l'entreprise d'accueil. +

+

+ Nous vous encourageons à conserver ce PDF comme preuve officielle de + votre immersion et des compétences développées, en vue de vos + futures candidatures. +

+
+
+
+ ); +}; diff --git a/front/src/app/routes/Router.tsx b/front/src/app/routes/Router.tsx index da5484a397..aaabffa884 100644 --- a/front/src/app/routes/Router.tsx +++ b/front/src/app/routes/Router.tsx @@ -15,6 +15,7 @@ import { AdminUserDetail } from "src/app/pages/admin/AdminUserDetail"; import { AgencyDashboardPage } from "src/app/pages/agency-dashboard/AgencyDashboardPage"; import { AddAgencyPage } from "src/app/pages/agency/AddAgencyPage"; import { BeneficiaryDashboardPage } from "src/app/pages/beneficiary-dashboard/BeneficiaryDashboardPage"; +import { AssessmentDocumentPage } from "src/app/pages/convention/AssessmentDocumentPage"; import { ConventionConfirmationPage } from "src/app/pages/convention/ConventionConfirmationPage"; import { ConventionImmersionPage } from "src/app/pages/convention/ConventionImmersionPage"; import { ConventionManageInclusionConnectedPage } from "src/app/pages/convention/ConventionManageInclusionConnectedPage"; @@ -137,7 +138,7 @@ const getPageByRouteName: { ), - + assessmentDocument: (route) => , beneficiaryDashboard: () => , initiateConvention: () => , conventionImmersion: (route) => , diff --git a/front/src/app/routes/routes.ts b/front/src/app/routes/routes.ts index 46284ee784..78fbc11cb5 100644 --- a/front/src/app/routes/routes.ts +++ b/front/src/app/routes/routes.ts @@ -148,6 +148,10 @@ export const { RouteProvider, useRoute, routes } = createRouter({ ({ conventionId }) => `/${frontRoutes.conventionImmersionRoute}/confirmation/${conventionId}`, ), + assessmentDocument: defineRoute( + { jwt: param.query.optional.string }, + () => `/${frontRoutes.assessmentDocument}`, + ), conventionDocument: defineRoute( { jwt: param.query.optional.string, diff --git a/shared/src/domElementIds.ts b/shared/src/domElementIds.ts index d504d50afe..5abeaab4b4 100644 --- a/shared/src/domElementIds.ts +++ b/shared/src/domElementIds.ts @@ -670,6 +670,7 @@ export const domElementIds = { conventionDocument: { downloadPdfButton: "im-convention-document__download-pdf-button", }, + assessmentDocument: {}, landingEstablishment: {}, conventionToSign: { form: "im-convention-to-sign-form", diff --git a/shared/src/routes/routes.ts b/shared/src/routes/routes.ts index e76e12e7fe..bd45750008 100644 --- a/shared/src/routes/routes.ts +++ b/shared/src/routes/routes.ts @@ -11,6 +11,7 @@ export const frontRoutes = { [allowedStartInclusionConnectLoginPages[0]]: "tableau-de-bord-agence", [allowedStartInclusionConnectLoginPages[1]]: "tableau-de-bord-etablissement", [allowedStartInclusionConnectLoginPages[2]]: "admin", + assessmentDocument: "bilan-document", beneficiaryDashboard: "tableau-de-bord-beneficiaire", initiateConvention: "initier-convention", conventionImmersionRoute: "demande-immersion",