diff --git a/shared/types/src/elastic/contributions.ts b/shared/types/src/elastic/contributions.ts index af94986a4..eca8b35fb 100644 --- a/shared/types/src/elastic/contributions.ts +++ b/shared/types/src/elastic/contributions.ts @@ -68,7 +68,6 @@ export type ContributionElasticDocument = ContributionElasticDocumentBase & type ElasticSearchContributionFicheSp = { type: "fiche-sp"; url: string; - date: string; raw: string; }; @@ -104,6 +103,7 @@ type ElasticSearchContributionBase = { references: ContributionRef[]; idcc: string; messageBlock?: string; + date: string; } & ( | ElasticSearchContributionFicheSp | ElasticSearchContributionContent diff --git a/shared/types/src/hasura/contributions.ts b/shared/types/src/hasura/contributions.ts index a8c2e52eb..7f3033fe3 100644 --- a/shared/types/src/hasura/contributions.ts +++ b/shared/types/src/hasura/contributions.ts @@ -93,6 +93,7 @@ type ContributionDocumentJsonBasic = { questionId: string; description: string; idcc: string; // 0000 pour la générique, impossible d'être nulle + date: string; }; export type ContributionDocumentJsonContent = ContributionDocumentJsonBasic & { diff --git a/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts b/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts index a4a511d44..3958d6a4b 100644 --- a/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts +++ b/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts @@ -40,6 +40,7 @@ const contributionMock: ContributionsAnswers = { }; const doc: DocumentElasticWithSource = { + date: "", id: "", title: "", excludeFromSearch: true, diff --git a/targets/export-elasticsearch/src/ingester/contributions/__tests__/generateMessageBlock.test.ts b/targets/export-elasticsearch/src/ingester/contributions/__tests__/generateMessageBlock.test.ts index 78625f11f..0f3500686 100644 --- a/targets/export-elasticsearch/src/ingester/contributions/__tests__/generateMessageBlock.test.ts +++ b/targets/export-elasticsearch/src/ingester/contributions/__tests__/generateMessageBlock.test.ts @@ -10,7 +10,9 @@ import { generateMessageBlock } from "../generateMessageBlock"; jest.mock("../fetchMessageBlock"); jest.mock("../fetchAgreementMessage"); -const contributionGeneric: DocumentElasticWithSource | undefined = undefined; +const contributionGeneric: + | DocumentElasticWithSource + | undefined = undefined; const mockContribution: any = { questionId: "123", contentType: "ANSWER", @@ -122,14 +124,15 @@ describe("generateMessageBlock", () => { expect(fetchAgreementMessage).toHaveBeenCalledWith("1234"); expect(result).toEqual("fetchedAgreementMessage"); }); - - - }); + describe("Tests avec une contribution generic no cdt", () => { - let mockedContributionGeneric: DocumentElasticWithSource | undefined = undefined; + let mockedContributionGeneric: + | DocumentElasticWithSource + | undefined = undefined; beforeEach(() => { mockedContributionGeneric = { + date: "", contentType: "GENERIC_NO_CDT", id: "id", cdtnId: "cdtnId", @@ -151,34 +154,42 @@ describe("Tests avec une contribution generic no cdt", () => { idcc: "0000", type: "generic-no-cdt", messageBlockGenericNoCDT: "messageBlockGenericNoCDT", - messageBlockGenericNoCDTUnextendedCC: "messageBlockGenericNoCDTUnextendedCC" + messageBlockGenericNoCDTUnextendedCC: + "messageBlockGenericNoCDTUnextendedCC", }; (fetchMessageBlock as jest.Mock).mockResolvedValue({ contentAgreementWithoutLegal: "agrement without legal", - contentNotHandledWithoutLegal: "content not handled without legal" + contentNotHandledWithoutLegal: "content not handled without legal", }); (fetchAgreementMessage as jest.Mock).mockResolvedValue(undefined); - }) - it.each(["ANSWER", "SP"])("should throw a contentAgreementWithoutLegal", async (contentType) => { - - mockContribution.contentType = contentType; - mockContribution.idcc = "1234"; - - const messageBloc = await generateMessageBlock(mockedContributionGeneric, mockContribution); - - expect( - messageBloc - ).toEqual("agrement without legal"); }); + it.each(["ANSWER", "SP"])( + "should throw a contentAgreementWithoutLegal", + async (contentType) => { + mockContribution.contentType = contentType; + mockContribution.idcc = "1234"; + + const messageBloc = await generateMessageBlock( + mockedContributionGeneric, + mockContribution + ); - it.each(["NOTHING", "CDT", "UNFAVOURABLE"])("should throw a contentNotHandledWithoutLegal", async (contentType) => { - mockContribution.contentType = contentType; - mockContribution.idcc = "1234"; + expect(messageBloc).toEqual("agrement without legal"); + } + ); - const messageBloc = await generateMessageBlock(mockedContributionGeneric, mockContribution); + it.each(["NOTHING", "CDT", "UNFAVOURABLE"])( + "should throw a contentNotHandledWithoutLegal", + async (contentType) => { + mockContribution.contentType = contentType; + mockContribution.idcc = "1234"; - expect( - messageBloc - ).toEqual("content not handled without legal"); - }); -}) \ No newline at end of file + const messageBloc = await generateMessageBlock( + mockedContributionGeneric, + mockContribution + ); + + expect(messageBloc).toEqual("content not handled without legal"); + } + ); +}); diff --git a/targets/export-elasticsearch/src/ingester/contributions/fetchFicheSp.ts b/targets/export-elasticsearch/src/ingester/contributions/fetchFicheSp.ts index 9f6103309..567342ee5 100644 --- a/targets/export-elasticsearch/src/ingester/contributions/fetchFicheSp.ts +++ b/targets/export-elasticsearch/src/ingester/contributions/fetchFicheSp.ts @@ -39,7 +39,7 @@ export async function fetchFicheSp( if (res.error) { throw res.error; } - if (!res.data || res.error) { + if (!res.data) { throw new Error(`Impossible de récupérer la fiche sp ${ficheSpId}`); } if (res.data.documents.length !== 1) { diff --git a/targets/frontend/src/components/contributions/answers/Comment.tsx b/targets/frontend/src/components/contributions/answers/Comment.tsx index c28ae86d5..46f202ae8 100644 --- a/targets/frontend/src/components/contributions/answers/Comment.tsx +++ b/targets/frontend/src/components/contributions/answers/Comment.tsx @@ -61,7 +61,7 @@ export const Comment = ({ comment, onDelete }: Props) => { - {comment.content} + {comment.content} ) : ( diff --git a/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx b/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx index 30ede089c..abb31e070 100644 --- a/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx +++ b/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx @@ -146,7 +146,7 @@ const answerBase: AnswerWithStatus = { export: { createdAt: "29/09/2023", }, - }, + } }; const onSubmit = jest.fn(() => Promise.resolve()); diff --git a/targets/frontend/src/components/contributions/answers/answer.query.ts b/targets/frontend/src/components/contributions/answers/answer.query.ts index c3adf3664..6acd6111b 100644 --- a/targets/frontend/src/components/contributions/answers/answer.query.ts +++ b/targets/frontend/src/components/contributions/answers/answer.query.ts @@ -94,6 +94,7 @@ type QueryProps = { export type AnswerWithStatus = Answer & { status: AnswerStatus; updateDate?: string; + updatedAt: string; }; type QueryResult = { diff --git a/targets/frontend/src/components/contributions/questionList/QuestionList.tsx b/targets/frontend/src/components/contributions/questionList/QuestionList.tsx index de3aa741f..c41a4f40b 100644 --- a/targets/frontend/src/components/contributions/questionList/QuestionList.tsx +++ b/targets/frontend/src/components/contributions/questionList/QuestionList.tsx @@ -8,7 +8,6 @@ import { TableHead, TableRow, TextField, - Tooltip, } from "@mui/material"; import { useState } from "react"; @@ -17,7 +16,6 @@ import { useQuestionListQuery, } from "./QuestionList.query"; import { QuestionRow } from "./QuestionRow"; -import { statusesMapping } from "../status/data"; export const countAnswersWithStatus = ( answers: QueryQuestionAnswer[] | undefined, diff --git a/targets/frontend/src/components/contributions/status/StatusPublication.tsx b/targets/frontend/src/components/contributions/status/StatusPublication.tsx index 84770888a..439df6e50 100644 --- a/targets/frontend/src/components/contributions/status/StatusPublication.tsx +++ b/targets/frontend/src/components/contributions/status/StatusPublication.tsx @@ -25,7 +25,7 @@ export const StatusPublicationContainer = ({ status = "PUBLISHED"; tooltipText = `${statusesMapping[status].text} le ${format( parseISO(exportDate), - "dd/MM/yyyy HH:mm:ss" + "dd/MM/yyyy HH:mm" )}`; } return ( diff --git a/targets/frontend/src/components/editorialContent/ReferenceBlocks.tsx b/targets/frontend/src/components/editorialContent/ReferenceBlocks.tsx index 2cf69b979..15adbb52e 100644 --- a/targets/frontend/src/components/editorialContent/ReferenceBlocks.tsx +++ b/targets/frontend/src/components/editorialContent/ReferenceBlocks.tsx @@ -7,7 +7,7 @@ import { FormErrorMessage } from "../forms/ErrorMessage"; import { References } from "./References"; import { FormRadioGroup } from "../forms"; import React from "react"; -import { Box, Card, CardContent, Stack } from "@mui/material"; +import { Box, Card, CardContent } from "@mui/material"; import { theme } from "src/theme"; const JURIDIQUES_LABEL = "Références juridiques"; diff --git a/targets/frontend/src/hooks/index.js b/targets/frontend/src/hooks/index.js index 1d7b0a759..1dda40aa3 100644 --- a/targets/frontend/src/hooks/index.js +++ b/targets/frontend/src/hooks/index.js @@ -1,21 +1,7 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useState } from "react"; import { debounce } from "../lib/debounce"; -export function usePrevious(value) { - // The ref object is a generic container whose current property is mutable ... - // ... and can hold any value, similar to an instance property on a class - const ref = useRef(); - - // Store current value in ref - useEffect(() => { - ref.current = value; - }, [value]); // Only re-run if value changes - - // Return previous value (happens before update in useEffect above) - return ref.current; -} - export function useDebouncedState(defaultState, delay) { const [state, setState] = useState(defaultState); //eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/targets/frontend/src/lib/duration.ts b/targets/frontend/src/lib/duration.ts deleted file mode 100644 index 4839f857c..000000000 --- a/targets/frontend/src/lib/duration.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { formatDistanceToNow, parseISO } from "date-fns"; -import frLocale from "date-fns/locale/fr"; - -export function toMs(minutes = 0) { - return toSecond(minutes) * 1000; -} - -export function toSecond(minutes = 0) { - return minutes * 60; -} - -export const timeSince = (date: string) => - formatDistanceToNow(parseISO(date), { locale: frLocale }); - -export const getExpiryDate = (minutes: number) => - new Date(Date.now() + toMs(minutes)).toISOString(); diff --git a/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts b/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts index 8fb789087..5844eba22 100644 --- a/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts +++ b/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts @@ -10,6 +10,8 @@ jest.mock("../../common/getGlossaryContent.ts", () => { getGlossaryContent: jest.fn(() => "mocked-glossary-content"), }; }); +const updatedAt = + "Thu Jul 11 2024 15:18:08 GMT+0200 (Central European Summer Time)"; describe("mapContributionToDocument", () => { const inputDoc: HasuraDocument = { @@ -31,7 +33,7 @@ describe("mapContributionToDocument", () => { it("devrait mapper l'answer d'un document sans fiche SP", async () => { const inputContribution: ContributionsAnswers = { - updatedAt: "", + updatedAt: updatedAt, id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", content: "

Quand une femme tombe enceinte et décide de partir en congé maternité, cette dernière a droit à des indemnités journalières de sécurité sociale venant indemniser la période durant laquelle elle ne peut plus travailler. Certaines conventions collectives prévoient également un maintien de salaire versé par l’employeur. Si le maintien est à 100%, dans ce cas, les deux mécanismes ne sont pas cumulables. Si le maintien est inférieur à 100%, le pourcentage de rémunération restant est indemnisé par les indemnités de Sécurité sociale. 

Maintien de salaire

Les salariées ayant au moins une année de présence continue dans l'entreprise à la date de l'accouchement ont droit à un maintien de salaire, après déduction des indemnités de Sécurité sociale, qui leur assure leur salaire habituel, et ce pendant une durée de 36 jours (en principe 18 jours avant l’accouchement, 18 jours après).

Pour les salariées cadres âgées de moins de 25 ans et les autres salariées âgées de moins de 22 ans à la date de l'accouchement, la période de 36 jours est augmentée de 2 jours par enfant à charge. L'indemnité complémentaire ne pourra pas être versée plus de 46 jours. Est considéré comme enfant à charge tout enfant à charge de la salariée au sens de la législation des prestations familiales et âgé de moins de 15 ans à la date de l'accouchement.

A noter : Les périodes de suspension du contrat de travail (maladie, etc.) sont prises en compte pour l'ancienneté.

Si la salariée ne respecte pas la condition d’ancienneté, elle n’a pas droit au maintien de salaire versé par l’employeur mais aura potentiellement droit aux indemnités journalières de Sécurité sociale si elle respecte ses conditions d’octroi. 

Indemnités de Sécurité sociale

Conditions d’ouverture des droits aux indemnités journalières de Sécurité sociale

Pour être indemnisée, la salariée doit remplir les conditions suivantes :

  • Etre affiliée à la Sécurité sociale depuis au moins 10 mois à la date présumée de l'accouchement ;

  • cesser son activité professionnelle pendant au moins 8 semaines ;

  • avoir : 

    • soit travaillé au moins 150 heures au cours des 3 mois civils ou des 90 jours précédant l'arrêt, 

    • soit travaillé au moins 600 heures au cours des 12 mois précédant l’arrêt de travail, 

    • soit cotisé, au cours des 6 mois civils précédant l'arrêt, sur la base d'une rémunération au moins égale à 1 015 fois le montant du Smic horaire fixé au début de cette période, 

    • soit cotisé au cours des 12 mois civils précédant l’arrêt, sur la base d'une rémunération au moins égale à 2030 fois le montant du Smic horaire fixé en début de période.

Exemple : le congé a débuté le 1er juillet 2023 pour une date présumée d'accouchement au 1er septembre 2023.

Le droit aux indemnités journalières est ouvert si :

  • La salariée était déjà affiliée à la Sécurité sociale avant novembre 2022 ;

  • et a travaillé soit au moins 150 heures entre le 1er avril 2023 et le 30 juin 2023, soit au moins 600 heures entre le 1er juillet 2022 et le 30 juin 2023, soit a cotisé entre le 1er janvier 2023 et le 30 juin 2023 sur la base d'une rémunération au moins égale à 11 439,05 €, soit a cotisé entre le 1er juillet 2022 et le 30 juin 2023 sur la base d’une rémunération au moins égale à 22 878,1 €.

Montant

La CPAM verse des indemnités journalières, dont le montant est fixé selon les étapes de calcul suivantes :

  • Calcul du salaire journalier de base : somme des 3 derniers salaires bruts perçus avant la date d'interruption du travail, divisé par 91,25.

  • Montant maximal du salaire journalier de base : le salaire pris en compte ne peut pas dépasser le plafond mensuel de la sécurité sociale en vigueur lors du dernier jour du mois qui précède l'arrêt (soit 3 666 € par mois en 2023, ou 3 428 € en 2022).

  • Taux forfaitaire appliqué par la CPAM : la CPAM retire à ce salaire journalier de base un taux forfaitaire de 21 %.

  • Montant minimal et montant maximal des indemnités journalières : le montant ne peut pas être inférieur à 10,24 € ni supérieur à 95,22 € par jour.

Versement

Les indemnités journalières sont versées tous les 14 jours.

", @@ -213,6 +215,7 @@ describe("mapContributionToDocument", () => { }, ], type: "content", + date: "11/07/2024", }, initial_id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", is_available: true, @@ -271,7 +274,7 @@ describe("mapContributionToDocument", () => { describe("avec une contrib generic de type GENERIC_NO_CDT", () => { it("devrait mapper l'answer de la contrib generic", async () => { const inputContribution: ContributionsAnswers = { - updatedAt: "", + updatedAt: updatedAt, id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", content: null, description: null, @@ -305,6 +308,7 @@ describe("mapContributionToDocument", () => { ); expect(result?.document).toEqual({ contentType: "GENERIC_NO_CDT", + date: "11/07/2024", idcc: "0000", questionId: "3384f257-e319-46d1-a4cb-8e8294da337b", questionName: @@ -371,6 +375,7 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", + updatedAt: updatedAt, content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -422,6 +427,7 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", + updatedAt: updatedAt, content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -456,6 +462,7 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", + updatedAt: updatedAt, content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -507,6 +514,7 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", + updatedAt: updatedAt, content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -539,6 +547,7 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution2: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb6", + updatedAt: updatedAt, content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", diff --git a/targets/frontend/src/modules/contribution/api/query.ts b/targets/frontend/src/modules/contribution/api/query.ts index e692fcf9b..5bb32402e 100644 --- a/targets/frontend/src/modules/contribution/api/query.ts +++ b/targets/frontend/src/modules/contribution/api/query.ts @@ -7,6 +7,7 @@ export const getContributionAnswerById = gql` content description content_type + updatedAt: updated_at agreement { id name diff --git a/targets/frontend/src/modules/contribution/mapContributionToDocument.ts b/targets/frontend/src/modules/contribution/mapContributionToDocument.ts index a8aff6af5..14fe5b6e8 100644 --- a/targets/frontend/src/modules/contribution/mapContributionToDocument.ts +++ b/targets/frontend/src/modules/contribution/mapContributionToDocument.ts @@ -8,6 +8,7 @@ import { getReferences } from "./getReferences"; import { generateCdtnId } from "@shared/utils"; import { generateContributionSlug } from "./generateSlug"; import { getGlossaryContent } from "../common/getGlossaryContent"; +import { format } from "date-fns"; async function getBaseDocument( data: ContributionsAnswers, @@ -77,6 +78,7 @@ export const mapContributionToDocument = async ( const initalDoc: ContributionDocumentJson = { ...baseDoc, + date: format(new Date(data.updatedAt), "dd/MM/yyyy"), contentType: data.content_type, linkedContent: data.cdtn_references.map((v) => ({ cdtnId: v.cdtn_id!, diff --git a/targets/frontend/src/pages/api/sitemap.ts b/targets/frontend/src/pages/api/sitemap.ts index 4dde44487..13110580a 100644 --- a/targets/frontend/src/pages/api/sitemap.ts +++ b/targets/frontend/src/pages/api/sitemap.ts @@ -81,14 +81,10 @@ export default async function Sitemap( res.setHeader("Content-Type", "text/xml"); res.write(` - - - ${baseUrl}/ - ${new Date(latestPost).toISOString()} - 0.8 - - ${pages.concat(staticPages, glossaryPages).join("")} - + +${baseUrl}/${new Date(latestPost).toISOString()}0.8 +${pages.concat(staticPages, glossaryPages).join("")} + `); res.end(); const endProcess = process.hrtime(startProcessAt); diff --git a/targets/frontend/src/pages/fichiers.tsx b/targets/frontend/src/pages/fichiers.tsx index 777466b9f..65c63d539 100644 --- a/targets/frontend/src/pages/fichiers.tsx +++ b/targets/frontend/src/pages/fichiers.tsx @@ -10,8 +10,9 @@ import { ListItem, MenuItem, Select, - TextField as Field, Stack, + TextField as Field, + Typography, } from "@mui/material"; import prettyBytes from "pretty-bytes"; import { useEffect, useRef, useState } from "react"; @@ -24,11 +25,12 @@ import { CopyButton } from "src/components/button/CopyButton"; import { Layout } from "src/components/layout/auth.layout"; import { DropZone } from "src/components/storage/DropZone"; import { useDebouncedState } from "src/hooks/"; -import { timeSince } from "src/lib/duration"; import { request } from "src/lib/request"; import useSWR from "swr"; import { theme } from "../theme"; import { S3File } from "src/lib/upload"; +import { fr } from "@codegouvfr/react-dsfr"; +import { format, parseISO } from "date-fns"; function FilesPage() { const { data, error, isValidating, mutate } = useSWR("files", () => @@ -72,7 +74,7 @@ function FilesPage() { }) .catch((err) => { alert( - "Impossible de supprimer le fichier :/, le message d'erreur est : " + + "Impossible de supprimer le fichier, le message d'erreur est : " + JSON.stringify(err.data) ); }) @@ -229,27 +231,47 @@ function FilesPage() { minWidth: 0, }} > - {file.key} - - - Poids :{" "} - - {prettyBytes(file.size)} - {" "} - | Mise en ligne il y a{" "} - - {timeSince(file.lastModified.toString())} - - + + + + Poids :{" "} + + {prettyBytes(file.size)} + + + + Téléversé le{" "} + + {format( + parseISO(file.lastModified.toString()), + "dd/MM/yyyy" + )} + + +