Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#2751 - Page de bilan #2953

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion front/src/app/hooks/jwt.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export const useAdminToken = () => {
return rawToken;
};

export const useJwt = (route: Route<typeof routes.conventionDocument>) => {
export const useJwt = (
route: Route<
typeof routes.conventionDocument | typeof routes.assessmentDocument
>,
) => {
const jwtQueryParam: ConventionSupportedJwt | undefined = route.params?.jwt;
const inclusionConnectJwt = useAppSelector(
authSelectors.inclusionConnectToken,
Expand Down
224 changes: 224 additions & 0 deletions front/src/app/pages/convention/AssessmentDocumentPage.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof routes.assessmentDocument>;
};

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 = [
<img key="logo-rf" src={logoRf} alt="Logo RF" />,
<img key={"logo-if"} src={logoIf} alt="" />,
];

useEffect(() => {
dispatch(
assessmentSlice.actions.getAssessmentRequested({
conventionId,
jwt,
feedbackTopic: "assessment",
}),
);
}, [dispatch, conventionId, jwt]);

if (isConventionLoading || isAssessmentLoading) return <Loader />;
if (!convention) return <p>Pas de convention correspondante trouvée</p>;
if (!assessment) return <p>Pas de bilan correspondant trouvé</p>;

return (
<MainWrapper layout="default" vSpacing={8}>
<Document
logos={logos}
title={`Bilan ${
convention.internshipKind === "immersion"
? "de l'Immersion Professionelle"
: "du mini-stage"
} au sein de ${convention.businessName}`}
customActions={[]}
>
<h2 className={fr.cx("fr-h4")}>
Identifiant de la convention: {convention.id}
</h2>
<p>
{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 <strong>{convention.businessName}</strong> (Siret n° :{" "}
<a
href={makeSiretDescriptionLink(convention.siret)}
target="_blank"
rel="noreferrer"
>
{convention.siret}
</a>
) à l'adresse suivante <strong>{convention.immersionAddress}</strong>.
</p>
<p>
Métier observé : {convention.immersionAppellation.appellationLabel}.
</p>
<p>
Objectif{" "}
{convention.internshipKind === "immersion"
? "de l'immersion"
: "du mini-stage"}{" "}
: {convention.immersionObjective}.
</p>
<p>
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"}
.
</p>
<h2 className={fr.cx("fr-h4", "fr-mt-4w")}>
Voici les informations saisies durant{" "}
{convention.internshipKind === "immersion"
? "cette immersion"
: "ce mini-stage"}{" "}
:
</h2>
<ul>
<li>
{convention.internshipKind === "immersion"
? "L'immersion a-t-elle"
: "Le mini-stage a-il"}{" "}
eu lieu ? {assessment.status === "DID_NOT_SHOW" ? "Non" : "Oui"}
</li>
{assessment.status === "COMPLETED" && (
<>
<li>
Nombre d'heures totales de l'immersion :{" "}
{computeTotalHours({
convention,
assessmentStatus: assessment.status,
missedHours: 0,
})}
</li>
<li>
Date réelle de fin de l'immersion :{" "}
{toDisplayedDate({
date: new Date(convention.dateEnd),
withHours: false,
})}
</li>
</>
)}
{assessment.status === "PARTIALLY_COMPLETED" && (
<>
<li>
Nombre d'heures totales de l'immersion :{" "}
{computeTotalHours({
convention,
assessmentStatus: assessment.status,
missedHours:
assessment.status === "PARTIALLY_COMPLETED"
? assessment.numberOfMissedHours
: 0,
})}
</li>
<li>
Date réelle de fin de l'immersion :{" "}
{isStringDate(assessment.lastDayOfPresence ?? "")
? toDisplayedDate({
date: new Date(assessment.lastDayOfPresence ?? ""),
withHours: false,
})
: "DATE INVALIDE"}
</li>
</>
)}
</ul>
<h2 className={fr.cx("fr-h4", "fr-mt-4w")}>
Résultats de{" "}
{convention.internshipKind === "immersion"
? "l'immersion"
: "du mini-stage"}{" "}
:
</h2>
<ul>
<li>Embauche ? {assessment.endedWithAJob ? "Oui" : "Non"}</li>
{assessment.endedWithAJob && (
<>
<li>
{toDisplayedDate({
date: convertLocaleDateToUtcTimezoneDate(
new Date(assessment.contractStartDate),
),
})}
</li>
<li>Type de contrat : {assessment.typeOfContract}</li>
</>
)}
</ul>
<h2 className={fr.cx("fr-h4", "fr-mt-4w")}>Appréciation générale :</h2>
<p>{assessment.establishmentFeedback}</p>
<h2 className={fr.cx("fr-h4", "fr-mt-4w")}>Conseils pour la suite :</h2>
<p>{assessment.establishmentAdvices}</p>
<hr className={fr.cx("fr-hr", "fr-mb-6w", "fr-mt-10w")} />
<footer className={fr.cx("fr-text--xs")}>
<p>
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.
</p>
<p>
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.
</p>
</footer>
</Document>
</MainWrapper>
);
};
6 changes: 3 additions & 3 deletions front/src/app/pages/convention/ConventionDocumentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import React, { useEffect, useState } from "react";
import {
ConventionDocument,
ConventionRenewedInformations,
Document,
Loader,
MainWrapper,
NPSForm,
Expand Down Expand Up @@ -195,7 +195,7 @@ export const ConventionDocumentPage = ({
<MainWrapper layout="default" vSpacing={8}>
{isPdfLoading && <Loader />}
{canShowConvention && (
<ConventionDocument
<Document
logos={logos}
title={
internshipKind === "immersion"
Expand Down Expand Up @@ -1009,7 +1009,7 @@ export const ConventionDocumentPage = ({
status: convention.status,
}}
/>
</ConventionDocument>
</Document>
)}
</MainWrapper>
);
Expand Down
3 changes: 2 additions & 1 deletion front/src/app/routes/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -137,7 +138,7 @@ const getPageByRouteName: {
<AgencyDetailForAgencyDashboard route={route} />
</AgencyDashboardPrivateRoute>
),

assessmentDocument: (route) => <AssessmentDocumentPage route={route} />,
beneficiaryDashboard: () => <BeneficiaryDashboardPage />,
initiateConvention: () => <InitiateConventionPage />,
conventionImmersion: (route) => <ConventionImmersionPage route={route} />,
Expand Down
4 changes: 4 additions & 0 deletions front/src/app/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import type { ArgTypes, Meta, StoryObj } from "@storybook/react";
import React from "react";
import {
ConventionDocument,
ConventionDocumentProperties,
} from "./ConventionDocument";
import { ConventionDocumentProperties, Document } from "./Document";

const Component = ConventionDocument;
const Component = Document;
type Story = StoryObj<typeof Component>;
const argTypes: Partial<ArgTypes<ConventionDocumentProperties>> | undefined =
{};
Expand All @@ -14,12 +11,12 @@ const componentDescription = `
Affiche un document de convention imprimable.

\`\`\`tsx
import { ConventionDocument } from "react-design-system";
import { Document } from "react-design-system";
\`\`\`
`;

export default {
title: "ConventionDocument",
title: "Document",
component: Component,
argTypes,
parameters: {
Expand All @@ -33,7 +30,7 @@ export default {

export const Default: Story = {
args: {
children: <div>&lt; insérer ici le contenu html de la convention &gt;</div>,
title: "Insérer ici le titre de la convention",
children: <div>&lt; insérer ici le contenu html du document &gt;</div>,
title: "Insérer ici le titre du document",
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { fr } from "@codegouvfr/react-dsfr";
import Button from "@codegouvfr/react-dsfr/Button";
import React, { ReactElement } from "react";
import { useStyles } from "tss-react/dsfr";
import "./ConventionDocument.scss";
import "./Document.scss";

const componentName = "im-convention-document";

Expand All @@ -13,7 +13,7 @@ export type ConventionDocumentProperties = {
customActions?: React.ReactNode[];
};

export const ConventionDocument = ({
export const Document = ({
children,
logos,
title,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Document";
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export * from "./button-with-submenu";
export * from "./convention-document";
export * from "./convention-form-layout";
export * from "./convention-form-sidebar";
export * from "./convention-summary";
Expand All @@ -10,6 +9,7 @@ export * from "./convention-total-hours";
export * from "./copy-button";
export * from "./crisp-chat";
export * from "./discussion-meta";
export * from "./document";
export * from "./error-notifications";
export * from "./field";
export * from "./file";
Expand Down
1 change: 1 addition & 0 deletions shared/src/domElementIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ export const domElementIds = {
conventionDocument: {
downloadPdfButton: "im-convention-document__download-pdf-button",
},
assessmentDocument: {},
landingEstablishment: {},
conventionToSign: {
form: "im-convention-to-sign-form",
Expand Down
1 change: 1 addition & 0 deletions shared/src/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading