;
- metaDescription: string;
- filesize: any;
- filename: string;
- html: any;
- meta_title: string;
- type: any;
- slug: string;
-}
-
-export const LetterModel = ({
- filename,
- filesize,
- html,
- slug,
-}: LetterModelProps) => {
- const trackCopy = useCallback(() => {
- matopush([
- MatomoBaseEvent.TRACK_EVENT,
- MatomoBaseEvent.PAGE_MODELS,
- MatomoActionEvent.TYPE_CTRL_C,
- slug,
- ]);
- }, []);
-
- useEffect(() => {
- document.addEventListener("copy", trackCopy);
- return () => {
- document.removeEventListener("copy", trackCopy);
- };
- }, [trackCopy]);
-
- const filesizeFormated = Math.round((filesize / 1000) * 100) / 100;
- const [, extension] = filename.split(/\.([a-z]{2,4})$/);
- return (
- <>
-
-
-
-
-
-
- Télécharger le document ({extension} - {filesizeFormated}Ko)
-
-
-
- {html}
-
-
-
- Type: Modèle de document - Format: {extension} - Taille:{" "}
- {filesizeFormated}
- Ko{" "}
-
-
- {getDisclaimer(slug)}
-
-
- Télécharger le modèle ({extension} - {filesizeFormated}Ko)
-
-
-
- >
- );
-};
-
-const { spacings, fonts } = theme;
-
-const FloatWrapper = styled.div`
- position: absolute;
- top: -1rem;
- right: 2rem;
-`;
-
-const LightWrapper = styled(Wrapper).attrs(() => ({ variant: "light" }))`
- position: relative;
- padding-top: ${spacings.large};
-`;
-const Disclaimer = styled(Wrapper).attrs(() => ({ variant: "dark" }))`
- margin-top: ${spacings.medium};
- margin-bottom: ${spacings.large};
-`;
-
-const Download = styled(icons.Download)`
- width: 3rem;
-`;
-
-const Notice = styled.p`
- margin-top: -2rem;
- font-size: ${fonts.sizes.small};
-`;
-
-const Centered = styled.p`
- display: flex;
- justify-content: center;
-`;
diff --git a/packages/code-du-travail-frontend/src/modeles/__tests__/modeles.test.tsx b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/modeles.test.tsx
similarity index 90%
rename from packages/code-du-travail-frontend/src/modeles/__tests__/modeles.test.tsx
rename to packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/modeles.test.tsx
index 73ec888d89..73c6748ef0 100644
--- a/packages/code-du-travail-frontend/src/modeles/__tests__/modeles.test.tsx
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/modeles.test.tsx
@@ -23,11 +23,9 @@ describe(" ", () => {
intro={""}
relatedItems={[]}
metaDescription={""}
- filesize={undefined}
+ filesize={10}
filename={""}
html={undefined}
- meta_title={""}
- type={undefined}
/>
);
@@ -50,11 +48,9 @@ describe(" ", () => {
intro={""}
relatedItems={[]}
metaDescription={""}
- filesize={undefined}
+ filesize={10}
filename={""}
html={undefined}
- meta_title={""}
- type={undefined}
/>
);
fireEvent.keyDown(container, { key: "c" });
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/queries.es.test.ts b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/queries.es.test.ts
index b7a563c40f..d334a63518 100644
--- a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/queries.es.test.ts
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/__tests__/queries.es.test.ts
@@ -1,6 +1,6 @@
/** @jest-environment node */
-import { fetchAllModels } from "../queries";
+import { fetchAllModels, fetchModel } from "../queries";
describe("Modèles de courrier", () => {
it("Récupération de tous les modèles de courrier", async () => {
@@ -20,4 +20,49 @@ describe("Modèles de courrier", () => {
},
]);
});
+
+ it("Récupération d'un modèle de courrier avec son slug", async () => {
+ const result = await fetchModel({
+ slug: "demande-de-rendez-vous-en-vue-dune-rupture-conventionnelle",
+ });
+ expect(result).toEqual({
+ "_id": "15",
+ "author": "Ministère du Travail",
+ "breadcrumbs": [
+ {
+ "label": "Départ de l’entreprise",
+ "position": 8,
+ "slug": "/themes/depart-de-lentreprise"
+ },
+ {
+ "label": "Rupture conventionnelle",
+ "position": 3,
+ "slug": "/themes/rupture-conventionnelle"
+ },
+ {
+ "label": "Rupture conventionnelle individuelle",
+ "position": 1,
+ "slug": "/themes/rupture-conventionnelle-individuelle"
+ }
+ ],
+ "date": "16/12/2019",
+ "description": "La rupture conventionnelle individuelle est une modalité de rupture spécifique du CDI. Elle nécessite le consentement de l’employeur et du salarié, et son homologation par l’administration. La rupture ouvre droit à une indemnité de rupture conventionnelle. Ce modèle permet d’initier la procédure de rupture par l’invitation à un premier entretien.",
+ "filename": "demande_rdv_rupture_conventionnelle.docx",
+ "filesize": 14535,
+ "html": "« Prénom Nom du salarié »
« Adresse »
« Code postal + Ville »
« Tél : 00.00.00.00.00 »
« E-mail : …………@... »
« Société »
« Prénom Nom du représentant »
« Fonction (DRH, etc.) »
« Adresse »
« Code postal + Ville »
A « lieu » , le « date »
Objet : Demande de rendez-vous en vue d’une éventuelle rupture conventionnelle
« Madame / Monsieur » ,
Je sollicite un entretien afin de vous proposer que nous convenions ensemble, si vous en êtes d’accord, de la rupture de mon contrat de travail dans le cadre légal de la rupture conventionnelle.
A cette occasion, je vous présenterai les raisons de ma démarche et nous pourrons déterminer d’un commun accord les modalités de la rupture de mon contrat de travail.
Seriez-vous disponible pour un rendez-vous ce mois-ci, à la date qui vous convient ?
Vous pouvez me joindre aux coordonnées présentées ci-dessus.
Veuillez agréer, « Madame / Monsieur » , l’expression de ma considération distinguée.
« Prénom Nom du salarié »
« Signature »
",
+ "metaDescription": "La rupture conventionnelle individuelle est une modalité de rupture spécifique du CDI. Elle nécessite le consentement de l’employeur et du salarié, et son homologation par l’administration. La rupture ouvre droit à une indemnité de rupture conventionnelle. Ce modèle permet d’initier la procédure de rupture par l’invitation à un premier entretien.",
+ "title": "Demande de rendez-vous en vue d’une rupture conventionnelle"
+ });
+ });
+
+ it("Récupération d'un modèle de courrier retourne undefined s'il n'existe pas", async () => {
+ const result = await fetchModel({ slug: "wrong" });
+ expect(result).toEqual(undefined);
+ });
+
+ it("Récupération d'un modèle de courrier avec son id", async () => {
+ const result = await fetchModel({ _id: "15" });
+ expect(result?.title).toEqual("Demande de rendez-vous en vue d’une rupture conventionnelle");
+ expect(result?._id).toEqual(15);
+ });
});
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/CopyButton.tsx b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/CopyButton.tsx
new file mode 100644
index 0000000000..3269a800f5
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/CopyButton.tsx
@@ -0,0 +1,54 @@
+import { fr } from "@codegouvfr/react-dsfr";
+import { Button } from "@codegouvfr/react-dsfr/Button";
+import { useState } from "react";
+import { css } from "../../../../styled-system/css";
+
+export const CopyButton = () => {
+ const [isCopied, setCopied] = useState(false);
+
+ const copyContent = () => {
+ const elementsByClassName = document?.getElementById("content-to-copy");
+ if (elementsByClassName) {
+ navigator?.clipboard?.writeText(elementsByClassName.innerText);
+ setCopied(true);
+ }
+ };
+
+ return (
+ <>
+
+ Copier le modèle
+
+
+ {isCopied && (
+
+ )}
+
+ >
+ );
+};
+
+const w100 = css({
+ w: "100%!",
+ justifyContent: "center",
+ textAlign: "center",
+});
+
+const fixHeight = css({
+ h: "48px",
+});
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/DownloadTile.tsx b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/DownloadTile.tsx
new file mode 100644
index 0000000000..f79e1f3542
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/DownloadTile.tsx
@@ -0,0 +1,30 @@
+import { Tile } from "@codegouvfr/react-dsfr/Tile";
+import { toUrl } from "../../../lib";
+
+export const DownloadTile = ({
+ filename,
+ filesize,
+ title,
+}: {
+ filename: string,
+ filesize: number,
+ title: string,
+}) => {
+ const filesizeFormated = Math.round((filesize / 1000) * 100) / 100;
+ const [, extension] = filename.split(/\.([a-z]{2,4})$/);
+
+ return (
+
+ );
+};
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/LetterModelContent.tsx b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/LetterModelContent.tsx
new file mode 100644
index 0000000000..58dc026664
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/components/LetterModelContent.tsx
@@ -0,0 +1,85 @@
+"use client";
+
+import { fr } from "@codegouvfr/react-dsfr";
+import Html from "../../common/Html";
+import { css } from "../../../../styled-system/css";
+import { getDisclaimer } from "../helpers";
+import { Alert } from "@codegouvfr/react-dsfr/Alert";
+import { DownloadTile } from "./DownloadTile";
+import { CopyButton } from "./CopyButton";
+
+export interface LetterModelProps {
+ date: string;
+ intro: string;
+ title: string;
+ filesize: any;
+ filename: string;
+ slug: string;
+ html: any;
+}
+
+export const LetterModelContent = ({
+ filename,
+ filesize,
+ html,
+ slug,
+ title,
+ intro,
+ date,
+}: LetterModelProps) => {
+ return (
+
+
{title}
+
Mise à jour le : {date}
+ {intro && (
+
+ {intro}
+
+ )}
+
+
+ {html}
+
+
+
+
+
+
+
+
+ );
+};
+
+const border = css({
+ border: `1px solid`,
+ borderRadius: "8px",
+ borderColor: "var(--artwork-minor-blue-cumulus)",
+});
+const maxWidth = css({
+ maxWidth: "720px",
+ m: "auto"
+});
diff --git a/packages/code-du-travail-frontend/src/modeles/helpers.ts b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/helpers.ts
similarity index 100%
rename from packages/code-du-travail-frontend/src/modeles/helpers.ts
rename to packages/code-du-travail-frontend/src/modules/modeles-de-courriers/helpers.ts
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/index.ts b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/index.ts
index b69c251208..3440e1ad4b 100644
--- a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/index.ts
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/index.ts
@@ -1 +1,4 @@
export * from "./queries";
+export * from "./modeles";
+export * from "./helpers";
+
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/modeles.tsx b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/modeles.tsx
new file mode 100644
index 0000000000..fe53987d15
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/modeles.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import React, { useCallback, useEffect } from "react";
+import { Breadcrumb } from "@socialgouv/cdtn-types";
+import { MatomoActionEvent, MatomoBaseEvent } from "../../lib";
+import { push as matopush } from "@socialgouv/matomo-next";
+import { Feedback } from "../layout/feedback";
+import { RelatedItems } from "../common/RelatedItems";
+import { Share } from "../common/Share";
+import { fr } from "@codegouvfr/react-dsfr";
+import { DownloadTile } from "./components/DownloadTile";
+import { CopyButton } from "./components/CopyButton";
+import { LetterModelContent } from "./components/LetterModelContent";
+
+export interface LetterModelProps {
+ breadcrumbs: Breadcrumb[];
+ date: string;
+ intro: string;
+ title: string;
+ relatedItems: Array;
+ metaDescription: string;
+ filesize: number;
+ filename: string;
+ html: any;
+ slug: string;
+}
+
+export const LetterModel = ({
+ filename,
+ filesize,
+ html,
+ slug,
+ relatedItems,
+ title,
+ metaDescription,
+ intro,
+ date,
+}: LetterModelProps) => {
+ const trackCopy = useCallback(() => {
+ matopush([
+ MatomoBaseEvent.TRACK_EVENT,
+ MatomoBaseEvent.PAGE_MODELS,
+ MatomoActionEvent.TYPE_CTRL_C,
+ slug,
+ ]);
+ }, []);
+
+ useEffect(() => {
+ document.addEventListener("copy", trackCopy);
+ return () => {
+ document.removeEventListener("copy", trackCopy);
+ };
+ }, [trackCopy]);
+
+ return (
+
+ );
+};
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/queries.ts b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/queries.ts
index 792be3f62c..2739db4da6 100644
--- a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/queries.ts
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/queries.ts
@@ -4,6 +4,8 @@ import {
} from "@socialgouv/cdtn-types";
import { elasticDocumentsIndex, elasticsearchClient } from "../../api/utils";
import { SOURCES } from "@socialgouv/cdtn-utils";
+import { DocumentElasticResult, fetchDocument } from "../documents";
+import { nonNullable } from "@socialgouv/modeles-social";
export const fetchAllModels = async <
K extends keyof DocumentElasticWithSource,
@@ -27,7 +29,44 @@ export const fetchAllModels = async <
_source: fields,
index: elasticDocumentsIndex,
});
- return response.hits.hits
- .map(({ _source }) => _source)
- .filter((source) => source !== undefined);
+ return response.hits.hits.map(({ _source }) => _source).filter(nonNullable);
+};
+
+export const fetchModel = async (
+ filter: Record
+): Promise<
+ DocumentElasticResult> | undefined
+> => {
+ return await fetchDocument<
+ DocumentElasticWithSource,
+ keyof DocumentElasticResult>
+ >(
+ [
+ "breadcrumbs",
+ "title",
+ "meta_title",
+ "date",
+ "type",
+ "html",
+ "author",
+ "filename",
+ "filesize",
+ "intro",
+ "description",
+ "metaDescription",
+ "references",
+ ],
+ {
+ query: {
+ bool: {
+ filter: [
+ { term: { source: SOURCES.LETTERS } },
+ { term: filter},
+ { term: { isPublished: true } },
+ ],
+ },
+ },
+ size: 1,
+ }
+ );
};
diff --git a/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/type.ts b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/type.ts
new file mode 100644
index 0000000000..4332301cf0
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/modeles-de-courriers/type.ts
@@ -0,0 +1,7 @@
+import {
+ DocumentElasticWithSource,
+ MailTemplateDoc,
+} from "@socialgouv/cdtn-types";
+
+export type ElasticLaborMailTemplateDoc =
+ DocumentElasticWithSource;
diff --git a/packages/code-du-travail-frontend/src/modules/widgets/WidgetWithIframeResizer.tsx b/packages/code-du-travail-frontend/src/modules/widgets/WidgetWithIframeResizer.tsx
new file mode 100644
index 0000000000..dcb0a06dc3
--- /dev/null
+++ b/packages/code-du-travail-frontend/src/modules/widgets/WidgetWithIframeResizer.tsx
@@ -0,0 +1,13 @@
+"use client";
+
+import { useIframeResizer } from "../../common/hooks";
+
+export function WidgetWithIframeResizer({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ useIframeResizer();
+
+ return <>{children}>;
+}
diff --git a/yarn.lock b/yarn.lock
index 98fdac1feb..f975feca08 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1874,7 +1874,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@cdt/frontend@workspace:packages/code-du-travail-frontend"
dependencies:
- "@codegouvfr/react-dsfr": ^1.13.4
+ "@codegouvfr/react-dsfr": ^1.13.9
"@elastic/elasticsearch": ^8.13.1
"@matejmazur/react-katex": ^3.1.3
"@opentelemetry/instrumentation-generic-pool": ^0.37.0
@@ -1985,9 +1985,9 @@ __metadata:
languageName: node
linkType: hard
-"@codegouvfr/react-dsfr@npm:^1.13.4":
- version: 1.13.4
- resolution: "@codegouvfr/react-dsfr@npm:1.13.4"
+"@codegouvfr/react-dsfr@npm:^1.13.9":
+ version: 1.13.9
+ resolution: "@codegouvfr/react-dsfr@npm:1.13.9"
dependencies:
tsafe: ^1.7.2
yargs-parser: ^21.1.1
@@ -2000,7 +2000,7 @@ __metadata:
copy-dsfr-to-public: bin/copy-dsfr-to-public.js
only-include-used-icons: bin/only-include-used-icons.js
react-dsfr: bin/react-dsfr.js
- checksum: 59691d2030df4b2273075081e6f9f770427ef4ef697801f04ec952b48045d6fc3107d7340dfabcdcffe059e14ab4e0d5b429b538a6cee32fadef887bc1f5c2c9
+ checksum: 32914874809571944dc905a231d4bd968d0ca3ea2f1f49e21dad30e10e500dda884d1cb69ac1526f9af244d42bd8994081f4bcf71c71f37a5eb7b1aabb561a8b
languageName: node
linkType: hard