From 05a90c9419cd706b33f900d12922efd7afa1e68f Mon Sep 17 00:00:00 2001 From: Maxime Golfier <25312957+maxgfr@users.noreply.github.com> Date: Thu, 22 Aug 2024 09:51:58 +0200 Subject: [PATCH] feat(contenus): ajout d'une date (#1458) * fix: ajout des modif * fix: tests * fix: code * fix: hasura * fix: hasura * fix: hasura * empty * feat: add code * feat: add code * fix: date * fix: date * fix: merge date * fix: build * fix: tests --- docker-compose.yml | 13 +- scripts/connect_db.sh | 117 +++++----- scripts/dump_db.sh | 29 +-- scripts/reset_from_dump.sh | 48 ++--- shared/types/src/hasura/contributions.ts | 1 + .../filterContributionToPublish.test.ts | 1 + targets/frontend/package.json | 1 + .../frontend/src/__tests__/sitemap.test.ts | 60 +++--- .../contributions/answers/Answer.tsx | 2 + .../contributions/answers/AnswerForm.tsx | 43 ++-- .../answers/__tests__/AnswerForm.test.tsx | 2 +- .../contributions/answers/answer.mutation.ts | 5 +- .../contributions/answers/answer.query.ts | 8 +- .../src/components/contributions/type.ts | 1 + .../src/components/forms/DatePicker/index.tsx | 62 ++++++ .../frontend/src/components/forms/index.ts | 1 + .../agreements/mapAgreementToDocument.ts | 45 ++++ .../mapContributionToDocument.test.ts | 25 ++- .../src/modules/contribution/api/query.ts | 1 + .../contribution/mapContributionToDocument.ts | 4 +- .../documents/api/documents.service.ts | 199 +----------------- .../informations/api/informations.query.ts | 1 + .../informationsEdit/Informations.query.ts | 9 +- .../informationsEdit/InformationsCreate.tsx | 2 +- .../informationsEdit/InformationsForm.tsx | 12 +- .../__tests__/InformationsForm.test.tsx | 3 +- .../editInformation.mapping.ts | 1 - .../editInformation.mutation.ts | 1 + .../InformationsList.query.ts | 1 + .../informations/mapInformationToDocument.ts | 103 +++++++++ .../frontend/src/modules/informations/type.ts | 1 + .../src/modules/models/api/modelsQuery.ts | 1 + .../modules/models/components/Common/Form.tsx | 21 +- .../components/Creation/model.mutation.ts | 1 + .../models/components/Edition/index.tsx | 1 - .../components/Edition/model.mutation.ts | 2 + .../models/components/Edition/model.query.ts | 12 +- .../models/components/List/list.query.ts | 4 +- .../modules/models/components/graphql.type.ts | 1 + .../src/modules/models/mapModelToDocument.ts | 47 +++++ targets/frontend/src/modules/models/type.ts | 1 + .../modules/sitemap/__tests__/date.spec.ts | 25 +++ targets/frontend/src/modules/sitemap/date.ts | 17 ++ targets/frontend/src/pages/api/sitemap.ts | 102 +++++---- targets/frontend/test/jest.setup.js | 2 + .../default/tables/contribution_answers.yaml | 2 + .../tables/information_informations.yaml | 8 + .../default/tables/model_models.yaml | 28 ++- .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 4 + .../up.sql | 2 + .../down.sql | 1 + .../up.sql | 1 + .../down.sql | 1 + .../up.sql | 1 + .../down.sql | 1 + .../up.sql | 1 + yarn.lock | 192 +++++++++++++++++ 67 files changed, 839 insertions(+), 472 deletions(-) create mode 100644 targets/frontend/src/components/forms/DatePicker/index.tsx create mode 100644 targets/frontend/src/modules/agreements/mapAgreementToDocument.ts create mode 100644 targets/frontend/src/modules/informations/mapInformationToDocument.ts create mode 100644 targets/frontend/src/modules/models/mapModelToDocument.ts create mode 100644 targets/frontend/src/modules/sitemap/__tests__/date.spec.ts create mode 100644 targets/frontend/src/modules/sitemap/date.ts create mode 100644 targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/up.sql create mode 100644 targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/up.sql create mode 100644 targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/up.sql create mode 100644 targets/hasura/migrations/default/1724228783542_set_display_date_by_default/down.sql create mode 100644 targets/hasura/migrations/default/1724228783542_set_display_date_by_default/up.sql create mode 100644 targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/down.sql create mode 100644 targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/up.sql create mode 100644 targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/down.sql create mode 100644 targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/up.sql create mode 100644 targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/up.sql create mode 100644 targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/up.sql create mode 100644 targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/down.sql create mode 100644 targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/up.sql diff --git a/docker-compose.yml b/docker-compose.yml index 0a43f72cb..fc60b3181 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -45,7 +45,7 @@ services: HASURA_GRAPHQL_ENDPOINT: "http://hasura:8080/v1/graphql" minio: - image: "minio/minio:latest" + image: minio/minio ports: - "9000:9000" - "8900:8900" @@ -53,16 +53,8 @@ services: MINIO_ROOT_USER: MINIO_ACCESS_KEY MINIO_ROOT_PASSWORD: MINIO_SECRET_KEY volumes: - - minio_data:/data/minio + - minio_data:/data command: 'minio server /data/minio --console-address ":8900"' - healthcheck: - test: - - CMD - - curl - - "-f" - - "http://localhost:9000/minio/health/live" - retries: 3 - timeout: 5s createbuckets: image: minio/mc @@ -70,6 +62,7 @@ services: - minio entrypoint: > /bin/sh -c " + sleep 5; /usr/bin/mc alias set myminio http://minio:9000 MINIO_ACCESS_KEY MINIO_SECRET_KEY; /usr/bin/mc mb myminio/cdtn; /usr/bin/mc anonymous set public myminio/cdtn; diff --git a/scripts/connect_db.sh b/scripts/connect_db.sh index bcaa689aa..0e457cd90 100755 --- a/scripts/connect_db.sh +++ b/scripts/connect_db.sh @@ -5,26 +5,29 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' -if ! command -v kubectl &> /dev/null; then - echo "${RED}Error : kubectl is not installed.${NC}" - exit 1 +if ! command -v kubectl &>/dev/null; then + echo "${RED}Error : kubectl is not installed.${NC}" + exit 1 fi write_mode=false port=5435 -namespace=$(kubectl config view --minify --output 'jsonpath={..namespace}'; echo) +namespace=$( + kubectl config view --minify --output 'jsonpath={..namespace}' + echo +) function usage() { - echo "Usage: $0 [OPTIONS]" - echo "Options:" - echo " -h, --help Display this help message" - echo " -n, --namespace Set a specific namespace (run on the current otherwise)" - echo " -p, --port Set a specific port for the postgres connexion (5435 by default)" - echo " -w, --write-mode Connect to the primary (read/write) pg instance. Connect to a replica (read only) by default" + echo "Usage: $0 [OPTIONS]" + echo "Options:" + echo " -h, --help Display this help message" + echo " -n, --namespace Set a specific namespace (run on the current otherwise)" + echo " -p, --port Set a specific port for the postgres connexion (5435 by default)" + echo " -w, --write-mode Connect to the primary (read/write) pg instance. Connect to a replica (read only) by default" } has_argument() { - [[ ("$1" == *=* && -n ${1#*=}) || ( ! -z "$2" && "$2" != -*) ]]; + [[ ("$1" == *=* && -n ${1#*=}) || (! -z "$2" && "$2" != -*) ]] } extract_argument() { @@ -34,46 +37,46 @@ extract_argument() { handle_options() { while [ $# -gt 0 ]; do case $1 in - -h | --help) + -h | --help) + usage + exit 0 + ;; + -w | --write-mode) + write_mode=true + ;; + -n | --namespace*) + if ! has_argument $@; then + echo -e "${RED}Namespace manquant.${NC}" >&2 + usage + exit 1 + fi + + namespace=$(extract_argument $@) + + shift + ;; + -p | --port*) + if ! has_argument $@; then + echo -e "${RED}Port manquant.${NC}" >&2 usage - exit 0 - ;; - -w | --write-mode) - write_mode=true - ;; - -n | --namespace*) - if ! has_argument $@; then - echo -e "${RED}Namespace manquant.${NC}" >&2 - usage - exit 1 - fi - - namespace=$(extract_argument $@) - - shift - ;; - -p | --port*) - if ! has_argument $@; then - echo -e "${RED}Port manquant.${NC}" >&2 - usage - exit 1 - fi - - port=$(extract_argument $@) - - if [[ ! $port =~ ^[0-9]+$ ]]; then - echo -e "${RED}Le port doit être composé de chiffre uniquement.${NC}" >&2 - usage - exit 1 - fi - - shift - ;; - *) - echo "Invalid option: $1" >&2 + exit 1 + fi + + port=$(extract_argument $@) + + if [[ ! $port =~ ^[0-9]+$ ]]; then + echo -e "${RED}Le port doit être composé de chiffre uniquement.${NC}" >&2 usage exit 1 - ;; + fi + + shift + ;; + *) + echo "Invalid option: $1" >&2 + usage + exit 1 + ;; esac shift done @@ -86,20 +89,20 @@ environment=$(kubectl config current-context) current_context=$(kubectl config current-context) if [[ $write_mode == "true" ]]; then - echo -e "Connection à la base de données de $namespace en mode ${RED}lecture et écriture${NC} (cluster: ${YELLOW}$environment${NC})." + echo -e "Connection à la base de données de $namespace en mode ${RED}lecture et écriture${NC} (cluster: ${YELLOW}$environment${NC})." else - echo -e "Connection à la base de données de $namespace en mode lecture seule (cluster: ${YELLOW}$environment${NC})." + echo -e "Connection à la base de données de $namespace en mode lecture seule (cluster: ${YELLOW}$environment${NC})." fi if [[ $write_mode == "true" && $current_context == *"prod"* ]]; then - echo -e "${RED}Attention ! Connection sur l'environnement de production en mode écriture. Il est recommandé de ne pas écrire directement sur une base de données en prod.${NC}" + echo -e "${RED}Attention ! Connection sur l'environnement de production en mode écriture. Il est recommandé de ne pas écrire directement sur une base de données en prod.${NC}" fi # Get the pg instance if [[ $write_mode == "true" ]]; then - pg_type="primary" + pg_type="primary" else - pg_type="replica" + pg_type="replica" fi pg_pods=$(kubectl -n $namespace get po --selector=cnpg.io/podRole=instance -o=custom-columns="NAME:.metadata.name,ROLE:.metadata.labels.role") @@ -126,7 +129,7 @@ local_connection_ssl_disabled=$(echo "$local_connection" | sed 's/?sslmode=requi local_connection_without_ssl=$(echo "$local_connection" | sed 's/?sslmode=require//') echo -e "Activation du port forwarding..." -kubectl port-forward -n $namespace $pod $port:5432 > /dev/null 2>&1 & +kubectl port-forward -n $namespace $pod $port:5432 >/dev/null 2>&1 & pid=$! # kill the port-forward regardless of how this script exits @@ -136,8 +139,8 @@ trap '{ }' EXIT # wait for $port to become available -while ! nc -vz localhost $port > /dev/null 2>&1 ; do - sleep 0.1 +while ! nc -vz localhost $port >/dev/null 2>&1; do + sleep 0.1 done echo -e "Port forwarding : ${GREEN}OK${NC}" echo -e "${GREEN}Adresse de connexion pour se connecter à la BDD depuis votre poste :\n$local_connection_ssl_disabled${NC}" @@ -148,5 +151,5 @@ echo -e "${GREEN}jdbc:$local_connection_without_ssl ${GREEN}" echo -e "Appuyez sur Ctrl+C pour quitter." while true; do - sleep 1 + sleep 1 done diff --git a/scripts/dump_db.sh b/scripts/dump_db.sh index 16b79c163..fb1eb8846 100755 --- a/scripts/dump_db.sh +++ b/scripts/dump_db.sh @@ -101,22 +101,23 @@ kubectl exec -n $namespace $pod -c postgres -- pg_dump -Fc -d $database >${folde echo -e "${GREEN}Dump terminé : ${folder}/${output}${NC}" confirm() { - while true; do - read -p "Voulez-vous relancer votre environnement avec ce dump ? (Y/n) " answer - case $answer in - [Yy]* ) break;; - [Nn]* ) echo -e "" - echo -e "Commande pour restaurer la BDD en local :" - echo -e "docker compose exec -T postgres pg_restore \\" - echo -e " --dbname postgres --clean --if-exists --user postgres \\" - echo -e " --no-owner --no-acl --verbose < ${folder}/${output} " - exit;; - * ) echo "Veuillez répondre par Y (oui) ou n (non).";; - esac - done + while true; do + read -p "Voulez-vous relancer votre environnement avec ce dump ? (Y/n) " answer + case $answer in + [Yy]*) break ;; + [Nn]*) + echo -e "" + echo -e "Commande pour restaurer la BDD en local :" + echo -e "docker compose exec -T postgres pg_restore \\" + echo -e " --dbname postgres --clean --if-exists --user postgres \\" + echo -e " --no-owner --no-acl --verbose < ${folder}/${output} " + exit + ;; + *) echo "Veuillez répondre par Y (oui) ou n (non)." ;; + esac + done } confirm ./scripts/reset_from_dump.sh "${folder}/${output}" - diff --git a/scripts/reset_from_dump.sh b/scripts/reset_from_dump.sh index 904c72764..64d0dc1a5 100755 --- a/scripts/reset_from_dump.sh +++ b/scripts/reset_from_dump.sh @@ -5,33 +5,33 @@ GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' -if ! command -v docker &> /dev/null; then - echo "${RED}Error : docker is not installed.${NC}" - exit 1 +if ! command -v docker &>/dev/null; then + echo "${RED}Error : docker is not installed.${NC}" + exit 1 fi function usage() { - echo "Usage: $0 [OPTIONS] dump" - echo "Options:" - echo " -h, --help Display this help message" + echo "Usage: $0 [OPTIONS] dump" + echo "Options:" + echo " -h, --help Display this help message" } handle_options() { while [ $# -gt 0 ]; do case $1 in - -h | --help) + -h | --help) + usage + exit 0 + ;; + *) + if [ -z "$dump_file" ]; then + dump_file=$1 + else + echo -e "${RED}Invalid option: $1${NC}" >&2 usage - exit 0 - ;; - *) - if [ -z "$dump_file" ]; then - dump_file=$1 - else - echo -e "${RED}Invalid option: $1${NC}" >&2 - usage - exit 1 - fi - ;; + exit 1 + fi + ;; esac shift done @@ -43,8 +43,8 @@ handle_options "$@" dump_file=$1 if [ ! -f "$dump_file" ]; then - echo -e "${RED}Erreur : Le fichier '$dump_file' n'existe pas.${NC}" - exit 1 + echo -e "${RED}Erreur : Le fichier '$dump_file' n'existe pas.${NC}" + exit 1 fi echo -e "${YELLOW}Suppression de l'environnement...${NC}" @@ -60,15 +60,15 @@ echo -e "${YELLOW}Restore de la base de données...${NC}" docker compose exec -T postgres pg_restore \ --dbname postgres --clean --if-exists --user postgres \ - --no-owner --no-acl --verbose < $dump_file + --no-owner --no-acl --verbose <$dump_file echo -e "${YELLOW}Clean de la base de données...${NC}" docker compose exec -T postgres psql \ - --dbname postgres --user postgres < .kontinuous/sql/post-restore.sql + --dbname postgres --user postgres <.kontinuous/sql/post-restore.sql -echo -e "${YELLOW}Lancement d'hasura et d'elasticsearch...${NC}" +echo -e "${YELLOW}Lancement d'hasura, d'elasticsearch et de minio...${NC}" -docker compose up --wait -d hasura elasticsearch +docker compose up --wait -d hasura elasticsearch minio createbuckets echo -e "${GREEN}Environnement prêt \o/${NC}" diff --git a/shared/types/src/hasura/contributions.ts b/shared/types/src/hasura/contributions.ts index 769deb762..d3a08091a 100644 --- a/shared/types/src/hasura/contributions.ts +++ b/shared/types/src/hasura/contributions.ts @@ -21,6 +21,7 @@ export type ContributionsAnswers = { agreement: ContributionAgreement; updatedAt: string; statuses?: ContributionStatus[]; + display_date: string; }; export type ContributionQuestion = { 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 2aa828206..162bc2c7a 100644 --- a/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts +++ b/targets/export-elasticsearch/src/ingester/contributions/__tests__/filterContributionToPublish.test.ts @@ -37,6 +37,7 @@ const contributionMock: ContributionsAnswers = { order: 1, }, statuses: [], + display_date: "2024-03-25", }; const doc: DocumentElasticWithSource = { diff --git a/targets/frontend/package.json b/targets/frontend/package.json index 1abcb64bf..0d84c1d6f 100644 --- a/targets/frontend/package.json +++ b/targets/frontend/package.json @@ -11,6 +11,7 @@ "@hookform/resolvers": "^3.3.1", "@mui/icons-material": "5.11.16", "@mui/material": "5.14.11", + "@mui/x-date-pickers": "^7.13.0", "@reach/accordion": "^0.16.1", "@reach/dialog": "^0.16.0", "@reach/menu-button": "^0.16.1", diff --git a/targets/frontend/src/__tests__/sitemap.test.ts b/targets/frontend/src/__tests__/sitemap.test.ts index 8050283cc..796fd82e9 100644 --- a/targets/frontend/src/__tests__/sitemap.test.ts +++ b/targets/frontend/src/__tests__/sitemap.test.ts @@ -5,80 +5,82 @@ jest.mock("p-limit", () => () => ({})); describe("Sitemap", () => { const documents: Document[] = [ { - __typename: "documents", - modified: "2022-01-03T00:30:44.301258+00:00", + updated_at: "2022-01-03T00:30:44.301258+00:00", slug: "l1235-12", source: "code_du_travail", + document: {}, }, { - __typename: "documents", - modified: "2022-01-11T00:31:14.726994+00:00", + updated_at: "2022-01-11T00:31:14.726994+00:00", slug: "un-salarie-peut-il-travailler-pendant-un-arret-de-travail", source: "fiches_service_public", + document: {}, }, { - __typename: "documents", - modified: "2022-01-05T15:59:30.542958+00:00", + updated_at: "2022-01-05T15:59:30.542958+00:00", slug: "demande-de-versement-de-lindemnite-inflation", source: "modeles_de_courriers", + document: {}, }, { - __typename: "documents", - modified: "2022-01-11T00:31:27.321974+00:00", + updated_at: "2022-01-11T00:31:27.321974+00:00", slug: "5-questions-reponses-sur-le-versement-du-salaire", source: "page_fiche_ministere_travail", + document: {}, }, { - __typename: "documents", - modified: "2022-01-07T13:09:02.024878+00:00", + updated_at: "2022-01-07T13:09:02.024878+00:00", slug: "indemnite-inflation-infographies", source: "information", + document: {}, }, { - __typename: "documents", - modified: "2020-11-16T15:46:33.470855+00:00", + updated_at: "2020-11-16T15:46:33.470855+00:00", slug: "greve", source: "themes", + document: {}, }, { - __typename: "documents", - modified: "2022-01-19T11:07:11.31437+00:00", + updated_at: "2022-01-19T11:07:11.31437+00:00", slug: "1634-quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite", source: "contributions", + document: {}, }, { - __typename: "documents", - modified: "2022-01-19T11:07:11.31437+00:00", + updated_at: "2022-01-19T11:07:11.31437+00:00", slug: "quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite", source: "contributions", + document: {}, }, ]; const glossaryTerms: Document[] = [ { - __typename: "glossary", - modified: "2020-11-25T14:38:50.085775+00:00", + updated_at: "2020-11-25T14:38:50.085775+00:00", slug: "abrogation", source: "glossary", + document: {}, }, ]; it("should generate urlEntry for given documents", async () => { - const { latestPost, pages, staticPages, glossaryPages } = + const { latestPostDate, pages, staticPages, glossaryPages } = await toUrlEntries(documents, glossaryTerms, "base.url"); - expect(latestPost).toEqual(1642590431314); + expect(latestPostDate.getTime()).toEqual( + new Date("2022-01-19T11:07:11.314Z").getTime() + ); expect(pages).toEqual([ - "base.url/code-du-travail/l1235-122022-01-03T00:30:44.301258+00:000.5\n", - "base.url/fiche-service-public/un-salarie-peut-il-travailler-pendant-un-arret-de-travail2022-01-11T00:31:14.726994+00:000.5\n", - "base.url/modeles-de-courriers/demande-de-versement-de-lindemnite-inflation2022-01-05T15:59:30.542958+00:000.5\n", - "base.url/fiche-ministere-travail/5-questions-reponses-sur-le-versement-du-salaire2022-01-11T00:31:27.321974+00:000.5\n", - "base.url/information/indemnite-inflation-infographies2022-01-07T13:09:02.024878+00:000.7\n", - "base.url/themes/greve2020-11-16T15:46:33.470855+00:000.5\n", - "base.url/contribution/1634-quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite2022-01-19T11:07:11.31437+00:000.5\n", - "base.url/contribution/quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite2022-01-19T11:07:11.31437+00:000.7\n", + "base.url/code-du-travail/l1235-122022-01-030.5", + "base.url/fiche-service-public/un-salarie-peut-il-travailler-pendant-un-arret-de-travail2022-01-110.5", + "base.url/modeles-de-courriers/demande-de-versement-de-lindemnite-inflation2022-01-050.7", + "base.url/fiche-ministere-travail/5-questions-reponses-sur-le-versement-du-salaire2022-01-110.5", + "base.url/information/indemnite-inflation-infographies2022-01-070.7", + "base.url/themes/greve2020-11-160.5", + "base.url/contribution/1634-quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite2022-01-190.5", + "base.url/contribution/quelles-sont-les-conditions-dindemnisation-pendant-le-conge-de-maternite2022-01-190.7", ]); expect(staticPages.length).toEqual(8); expect(staticPages[0]).toContain("base.url/a-propos"); expect(glossaryPages).toEqual([ - "base.url/glossaire/abrogation2020-11-25T14:38:50.085775+00:000.5\n", + "base.url/glossaire/abrogation2020-11-250.5", ]); }); }); diff --git a/targets/frontend/src/components/contributions/answers/Answer.tsx b/targets/frontend/src/components/contributions/answers/Answer.tsx index 3f5588cae..e53485eb6 100644 --- a/targets/frontend/src/components/contributions/answers/Answer.tsx +++ b/targets/frontend/src/components/contributions/answers/Answer.tsx @@ -70,6 +70,7 @@ export const ContributionsAnswer = ({ legiReferences: data.legiReferences, cdtnReferences: data.cdtnReferences, otherReferences: data.otherReferences, + displayDate: data.displayDate, }); if (newStatus === "TO_PUBLISH") { await onPublish(answer.id); @@ -93,6 +94,7 @@ export const ContributionsAnswer = ({ legiReferences: data.legiReferences, cdtnReferences: data.cdtnReferences, otherReferences: data.otherReferences, + displayDate: data.displayDate, }); } setSnack({ open: true, severity: "error", message: e.message }); diff --git a/targets/frontend/src/components/contributions/answers/AnswerForm.tsx b/targets/frontend/src/components/contributions/answers/AnswerForm.tsx index 619591bed..35059cdea 100644 --- a/targets/frontend/src/components/contributions/answers/AnswerForm.tsx +++ b/targets/frontend/src/components/contributions/answers/AnswerForm.tsx @@ -5,7 +5,12 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { useRouter } from "next/router"; -import { FormEditionField, FormRadioGroup, FormTextField } from "../../forms"; +import { + FormDatePicker, + FormEditionField, + FormRadioGroup, + FormTextField, +} from "../../forms"; import { Answer, answerRelationSchema, documentSchema, Status } from "../type"; import { AnswerWithStatus } from "./answer.query"; import { @@ -18,21 +23,18 @@ import { getNextStatus, getPrimaryButtonLabel } from "../status/utils"; import { FicheSpDocumentInput } from "./references/FicheSpDocumentInput"; import { LoadingButton } from "src/components/button/LoadingButton"; -const answerFormBaseSchema = answerRelationSchema - .pick({ - content: true, - description: true, - contentType: true, - messageBlockGenericNoCDT: true, - cdtnReferences: true, - kaliReferences: true, - legiReferences: true, - otherReferences: true, - contentFichesSpDocument: true, - }) - .extend({ - updateDate: z.string(), - }); +const answerFormBaseSchema = answerRelationSchema.pick({ + content: true, + description: true, + contentType: true, + messageBlockGenericNoCDT: true, + cdtnReferences: true, + kaliReferences: true, + legiReferences: true, + otherReferences: true, + contentFichesSpDocument: true, + displayDate: true, +}); const answerWithAnswerSchema = answerFormBaseSchema.extend({ contentType: z.literal("ANSWER"), content: z @@ -123,7 +125,7 @@ export const AnswerForm = ({ cdtnReferences: answer?.cdtnReferences ?? [], contentFichesSpDocument: answer?.contentFichesSpDocument ?? undefined, messageBlockGenericNoCDT: answer?.messageBlockGenericNoCDT ?? undefined, - updateDate: answer?.updateDate ?? "", + displayDate: answer?.displayDate ?? undefined, }, }); @@ -226,12 +228,11 @@ export const AnswerForm = ({ )} - 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 254e66f21..c581b0751 100644 --- a/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx +++ b/targets/frontend/src/components/contributions/answers/__tests__/AnswerForm.test.tsx @@ -141,7 +141,7 @@ const answerBase: AnswerWithStatus = { name: "", }, }, - updateDate: "29/09/2023", + displayDate: "2023-09-29", document_exports: [ { export_es_status: { diff --git a/targets/frontend/src/components/contributions/answers/answer.mutation.ts b/targets/frontend/src/components/contributions/answers/answer.mutation.ts index 2229da75f..35905f831 100644 --- a/targets/frontend/src/components/contributions/answers/answer.mutation.ts +++ b/targets/frontend/src/components/contributions/answers/answer.mutation.ts @@ -10,8 +10,8 @@ import { } from "./answerReferences"; export const contributionAnswerUpdateMutation = ` -mutation contributionAnswerUpdate($id: uuid!, $content: String, $description: String, $contentType: String, $status: statustype!, $userId: uuid!, $contentServicePublicCdtnId: String, $messageIntroNoCDT: String, $messageBlockGenericNoCDT: String, $kaliReferences: [contribution_answer_kali_references_insert_input!]!, $legiReferences: [contribution_answer_legi_references_insert_input!]!, $otherReferences: [contribution_answer_other_references_insert_input!]!, $cdtnReferences: [contribution_answer_cdtn_references_insert_input!]!) { - update_contribution_answers_by_pk(pk_columns: {id: $id}, _set: {content: $content, description: $description, content_type: $contentType, content_service_public_cdtn_id: $contentServicePublicCdtnId, message_block_generic_no_CDT: $messageBlockGenericNoCDT}) { +mutation contributionAnswerUpdate($id: uuid!, $displayDate: date, $content: String, $description: String, $contentType: String, $status: statustype!, $userId: uuid!, $contentServicePublicCdtnId: String, $messageIntroNoCDT: String, $messageBlockGenericNoCDT: String, $kaliReferences: [contribution_answer_kali_references_insert_input!]!, $legiReferences: [contribution_answer_legi_references_insert_input!]!, $otherReferences: [contribution_answer_other_references_insert_input!]!, $cdtnReferences: [contribution_answer_cdtn_references_insert_input!]!) { + update_contribution_answers_by_pk(pk_columns: {id: $id}, _set: {display_date: $displayDate, content: $content, description: $description, content_type: $contentType, content_service_public_cdtn_id: $contentServicePublicCdtnId, message_block_generic_no_CDT: $messageBlockGenericNoCDT}) { __typename } insert_contribution_answer_statuses_one(object: {status: $status, user_id: $userId, answer_id: $id}) { @@ -57,6 +57,7 @@ export type MutationProps = Pick< | "legiReferences" | "otherReferences" | "cdtnReferences" + | "displayDate" > & { status: string; userId: string; diff --git a/targets/frontend/src/components/contributions/answers/answer.query.ts b/targets/frontend/src/components/contributions/answers/answer.query.ts index 4be8675cb..9dd715d29 100644 --- a/targets/frontend/src/components/contributions/answers/answer.query.ts +++ b/targets/frontend/src/components/contributions/answers/answer.query.ts @@ -1,6 +1,5 @@ import { useQuery } from "urql"; import { useMemo } from "react"; -import { format, parseISO } from "date-fns"; import { Answer, AnswerStatus } from "../type"; import { initStatus } from "../status/utils"; @@ -14,7 +13,7 @@ query contribution_answer($id: uuid) { description content contentType: content_type - updatedAt: updated_at + displayDate: display_date contentServicePublicCdtnId: content_service_public_cdtn_id messageBlockGenericNoCDT: message_block_generic_no_CDT question { @@ -94,8 +93,6 @@ type QueryProps = { export type AnswerWithStatus = Answer & { status: AnswerStatus; - updateDate?: string; - updatedAt: string; }; type QueryResult = { @@ -130,8 +127,5 @@ export const useContributionAnswerQuery = ({ return { ...answer, status: initStatus(answer), - updateDate: answer?.updatedAt - ? format(parseISO(answer?.updatedAt), "dd/MM/yyyy") - : undefined, }; }; diff --git a/targets/frontend/src/components/contributions/type.ts b/targets/frontend/src/components/contributions/type.ts index 73bfb9ceb..299da0ad7 100644 --- a/targets/frontend/src/components/contributions/type.ts +++ b/targets/frontend/src/components/contributions/type.ts @@ -144,6 +144,7 @@ const answerBaseSchema = z.object({ .optional(), messageBlockGenericNoCDT: z.string().nullable().optional(), updatedAt: z.string(), + displayDate: z.string(), }); export const questionBaseSchema = z.object({ diff --git a/targets/frontend/src/components/forms/DatePicker/index.tsx b/targets/frontend/src/components/forms/DatePicker/index.tsx new file mode 100644 index 000000000..c5b1d1fb9 --- /dev/null +++ b/targets/frontend/src/components/forms/DatePicker/index.tsx @@ -0,0 +1,62 @@ +import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; +import React from "react"; +import { Controller } from "react-hook-form"; +import { CommonFormProps } from "../type"; +import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; +import fr from "date-fns/locale/fr"; +import { format, parseISO } from "date-fns"; + +type FormDatePickerProps = CommonFormProps; + +export const FormDatePicker = ({ + name, + control, + label, + rules, + disabled, +}: FormDatePickerProps) => { + const formatDate = (date?: string | null) => { + try { + if (date && date !== "") { + const dateString = format(parseISO(date), "dd/MM/yyyy"); + const [day, month, year] = dateString.split("/").map(Number); + const formattedDate = new Date(year, month - 1, day); + return formattedDate; + } + return null; + } catch (error) { + return null; + } + }; + + const unFormatDate = (date?: Date | null) => { + try { + if (date) { + return format(new Date(date.toString()), "yyyy-MM-dd"); + } + return null; + } catch (error) { + return null; + } + }; + + return ( + + ( + { + onChange(unFormatDate(date)); + }} + disabled={disabled} + /> + )} + /> + + ); +}; diff --git a/targets/frontend/src/components/forms/index.ts b/targets/frontend/src/components/forms/index.ts index 6762619b0..bed40e439 100644 --- a/targets/frontend/src/components/forms/index.ts +++ b/targets/frontend/src/components/forms/index.ts @@ -7,3 +7,4 @@ export * from "./ToggleButtonGroup"; export * from "./File"; export * from "./AutocompleteChips"; export * from "./Switch"; +export * from "./DatePicker"; diff --git a/targets/frontend/src/modules/agreements/mapAgreementToDocument.ts b/targets/frontend/src/modules/agreements/mapAgreementToDocument.ts new file mode 100644 index 000000000..cea02632c --- /dev/null +++ b/targets/frontend/src/modules/agreements/mapAgreementToDocument.ts @@ -0,0 +1,45 @@ +import { parseISO } from "date-fns"; +import { generateCdtnId } from "@shared/utils"; +import slugify from "@socialgouv/cdtn-slugify"; +import { AgreementDoc, HasuraDocument } from "@socialgouv/cdtn-types"; +import { Agreement } from "../agreements"; + +export const mapAgreementToDocument = ( + data: Agreement, + document?: HasuraDocument +): HasuraDocument => { + return { + cdtn_id: document?.cdtn_id ?? generateCdtnId(data.name), + initial_id: data.id, + source: "conventions_collectives", + meta_description: `IDCC ${data.id}: ${data.name}`, + title: data.name, + text: `IDCC ${data.id}: ${data.name} ${data.shortName}`, + slug: document?.slug ?? slugify(data.shortName), + is_searchable: document + ? document.is_searchable + : data.kali_id !== undefined, + is_available: true, + is_published: data.kali_id !== undefined, + document: + data.kali_id !== undefined + ? { + date_publi: data.publicationDate + ? `${data.publicationDate}T00:00:00.000Z` + : undefined, + effectif: data.workerNumber ?? undefined, + num: Number(data.id), + url: data.legifranceUrl ?? undefined, + shortTitle: data.shortName, + synonymes: data.synonyms, + } + : { + date_publi: data.publicationDate + ? parseISO(data.publicationDate).toISOString() + : undefined, + num: Number(data.id), + shortTitle: data.shortName, + synonymes: data.synonyms, + }, + }; +}; diff --git a/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts b/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts index dedffb00c..7fcc5fd9b 100644 --- a/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts +++ b/targets/frontend/src/modules/contribution/__tests__/mapContributionToDocument.test.ts @@ -10,8 +10,6 @@ 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 = { @@ -33,7 +31,8 @@ describe("mapContributionToDocument", () => { it("devrait mapper l'answer d'un document sans fiche SP", async () => { const inputContribution: ContributionsAnswers = { - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", 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.

", @@ -264,6 +263,7 @@ describe("mapContributionToDocument", () => { cdtn_references: [], content_fiche_sp: null, message_block_generic_no_CDT: null, + display_date: "2024-07-11", }; const result = await mapContributionToDocument( @@ -277,7 +277,8 @@ 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: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", content: null, description: null, @@ -357,6 +358,7 @@ describe("mapContributionToDocument", () => { cdtn_references: [], content_fiche_sp: null, message_block_generic_no_CDT: null, + display_date: "2024-07-11", }; await expect( mapContributionToDocument( @@ -380,7 +382,8 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -432,7 +435,8 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -467,7 +471,8 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -519,7 +524,8 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb5", - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", content: "

Texte de la réponse

", description: "Texte de la réponse", content_type: "ANSWER", @@ -552,7 +558,8 @@ describe("mapContributionToDocument", () => { // @ts-ignore const inputContribution2: ContributionsAnswers = { id: "effee3b9-84fb-4667-944b-4b1e1fd14eb6", - updatedAt: updatedAt, + updatedAt: "2024-07-11T13:18:08.000Z", + display_date: "2024-07-11T13:18:08.000Z", 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 d2b165dcb..5d6968b05 100644 --- a/targets/frontend/src/modules/contribution/api/query.ts +++ b/targets/frontend/src/modules/contribution/api/query.ts @@ -8,6 +8,7 @@ export const getContributionAnswerById = gql` description content_type updatedAt: updated_at + display_date agreement { id name diff --git a/targets/frontend/src/modules/contribution/mapContributionToDocument.ts b/targets/frontend/src/modules/contribution/mapContributionToDocument.ts index 7933bca95..e740209e4 100644 --- a/targets/frontend/src/modules/contribution/mapContributionToDocument.ts +++ b/targets/frontend/src/modules/contribution/mapContributionToDocument.ts @@ -8,7 +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"; +import { format, parseISO } from "date-fns"; async function getBaseDocument( data: ContributionsAnswers, @@ -78,7 +78,7 @@ export const mapContributionToDocument = async ( const initalDoc: ContributionDocumentJson = { ...baseDoc, - date: format(new Date(data.updatedAt), "dd/MM/yyyy"), + date: format(parseISO(data.display_date), "dd/MM/yyyy"), contentType: data.content_type, linkedContent: data.cdtn_references.map((v) => ({ cdtnId: v.cdtn_id!, diff --git a/targets/frontend/src/modules/documents/api/documents.service.ts b/targets/frontend/src/modules/documents/api/documents.service.ts index 0e4f361f2..702e72a26 100644 --- a/targets/frontend/src/modules/documents/api/documents.service.ts +++ b/targets/frontend/src/modules/documents/api/documents.service.ts @@ -1,22 +1,18 @@ import { DocumentsRepository } from "./documents.repository"; import { ConflictError, NotFoundError } from "src/lib/api/ApiErrors"; -import { Information, InformationsRepository } from "src/modules/informations"; -import { format, parseISO } from "date-fns"; -import { generateCdtnId, generateInitialId } from "@shared/utils"; -import slugify from "@socialgouv/cdtn-slugify"; +import { InformationsRepository } from "src/modules/informations"; import { ContributionRepository, mapContributionToDocument, } from "src/modules/contribution"; import { ModelRepository } from "../../models/api"; -import { Model } from "../../models"; -import { AgreementDoc, HasuraDocument } from "@socialgouv/cdtn-types"; import { generateContributionSlug } from "src/modules/contribution/generateSlug"; import { AgreementRepository } from "../../agreements/api"; -import { Agreement } from "../../agreements"; import { SourceRoute } from "@socialgouv/cdtn-sources"; -import { getGlossaryContent } from "src/modules/common/getGlossaryContent"; import pMap from "p-map"; +import { mapAgreementToDocument } from "src/modules/agreements/mapAgreementToDocument"; +import { mapInformationToDocument } from "src/modules/informations/mapInformationToDocument"; +import { mapModelToDocument } from "src/modules/models/mapModelToDocument"; export class DocumentsService { private readonly informationsRepository: InformationsRepository; @@ -39,187 +35,6 @@ export class DocumentsService { this.agreementRepository = agreementRepository; } - private async mapInformationToDocument( - data: Information, - document?: HasuraDocument - ): Promise> { - const introWithGlossary = await getGlossaryContent( - "markdown", - data.intro ?? "" - ); - return { - cdtn_id: document?.cdtn_id ?? generateCdtnId(data.title), - initial_id: data.id ?? generateInitialId(), - source: "information", - meta_description: data.metaDescription ?? data.description, - title: data.title, - text: data.title, - slug: document?.slug ?? slugify(data.title), - is_searchable: document ? document.is_searchable : true, - is_published: document ? document.is_published : true, - is_available: true, - document: { - date: data.updatedAt - ? format(new Date(data.updatedAt), "dd/MM/yyyy") - : undefined, - intro: data.intro, - introWithGlossary, - description: data.description, - sectionDisplayMode: data.sectionDisplayMode, - dismissalProcess: data.dismissalProcess, - references: data.references.length - ? [ - { - label: data.referenceLabel, - links: data.references, - }, - ] - : undefined, - contents: await Promise.all( - data.contents.map( - async ({ name, title, blocks, references, referenceLabel }) => { - return { - name, - title, - blocks: await Promise.all( - blocks.map(async (block) => { - const htmlWithGlossary = await getGlossaryContent( - "markdown", - block.content ?? "" - ); - return { - type: block.type, - ...(block.type === "content" - ? { - title: block.content, - } - : { - markdown: block.content, - htmlWithGlossary, - }), - ...(block.type === "graphic" - ? { - size: block.file?.size, - imgUrl: block.img?.url, - altText: block.img?.altText, - fileUrl: block.file?.url, - } - : {}), - ...(block.type === "content" - ? { - blockDisplayMode: block.contentDisplayMode, - contents: block.contents?.length - ? block.contents.map(({ document }) => { - return { - title: document.title, - cdtnId: document.cdtnId, - source: document.source, - }; - }) - : undefined, - } - : {}), - }; - }) - ), - references: references?.length - ? [ - { - label: referenceLabel, - links: references, - }, - ] - : undefined, - }; - } - ) - ), - }, - }; - } - - private mapModelToDocument( - data: Model, - document?: HasuraDocument - ): HasuraDocument { - return { - cdtn_id: document?.cdtn_id ?? generateCdtnId(data.title), - initial_id: data.id!, - source: "modeles_de_courriers", - meta_description: data.metaDescription, - title: data.title, - text: data.intro, - slug: document?.slug ?? slugify(data.title), - is_searchable: document ? document.is_searchable : true, - is_published: document ? document.is_published : true, - is_available: true, - document: { - meta_title: data.metaTitle, - type: data.type, - date: format(parseISO(data.updatedAt), "dd/MM/yyyy"), - author: "Ministère du Travail", - references: data.legiReferences - .map((item) => ({ - url: `https://www.legifrance.gouv.fr/codes/article_lc/${item.legiArticle.cid}`, - title: item.legiArticle.label, - type: "external", - })) - .concat( - data.otherReferences.map((item) => ({ - url: item.url ?? "", - title: item.label, - type: "external", - })) - ), - description: data.metaDescription, - intro: data.intro, - filename: data.file.url, - filesize: parseInt(data.file.size ?? "0"), - html: data.previewHTML, - }, - }; - } - - private mapAgreementToDocument( - data: Agreement, - document?: HasuraDocument - ): HasuraDocument { - return { - cdtn_id: document?.cdtn_id ?? generateCdtnId(data.name), - initial_id: data.id, - source: "conventions_collectives", - meta_description: `IDCC ${data.id}: ${data.name}`, - title: data.name, - text: `IDCC ${data.id}: ${data.name} ${data.shortName}`, - slug: document?.slug ?? slugify(data.shortName), - is_searchable: document - ? document.is_searchable - : data.kali_id !== undefined, - is_available: true, - is_published: data.kali_id !== undefined, - document: - data.kali_id !== undefined - ? { - date_publi: data.publicationDate - ? `${data.publicationDate}T00:00:00.000Z` - : undefined, - effectif: data.workerNumber ?? undefined, - num: Number(data.id), - url: data.legifranceUrl ?? undefined, - shortTitle: data.shortName, - synonymes: data.synonyms, - } - : { - date_publi: data.publicationDate - ? parseISO(data.publicationDate).toISOString() - : undefined, - num: Number(data.id), - shortTitle: data.shortName, - synonymes: data.synonyms, - }, - }; - } - public async publish(id: string, source: SourceRoute) { let document = await this.documentsRepository.fetch({ source, @@ -237,7 +52,7 @@ export class DocumentsService { cause: null, }); } - document = await this.mapInformationToDocument(information, document); + document = await mapInformationToDocument(information, document); break; case "contributions": const contribution = await this.contributionRepository.fetch(id); @@ -284,7 +99,7 @@ export class DocumentsService { cause: null, }); } - document = this.mapModelToDocument(model, document); + document = mapModelToDocument(model, document); break; case "conventions_collectives": @@ -296,7 +111,7 @@ export class DocumentsService { cause: null, }); } - document = this.mapAgreementToDocument(agreement, document); + document = mapAgreementToDocument(agreement, document); break; default: diff --git a/targets/frontend/src/modules/informations/api/informations.query.ts b/targets/frontend/src/modules/informations/api/informations.query.ts index 4f56bb2ae..1b34450bc 100644 --- a/targets/frontend/src/modules/informations/api/informations.query.ts +++ b/targets/frontend/src/modules/informations/api/informations.query.ts @@ -13,6 +13,7 @@ export const informationsQuery = gql` sectionDisplayMode title updatedAt + displayDate dismissalProcess contents(order_by: { order: asc }) { id diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/Informations.query.ts b/targets/frontend/src/modules/informations/components/informationsEdit/Informations.query.ts index f8f025dc0..a51ef4621 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/Informations.query.ts +++ b/targets/frontend/src/modules/informations/components/informationsEdit/Informations.query.ts @@ -16,6 +16,7 @@ const informationsQuery = gql` title updatedAt dismissalProcess + displayDate contents(order_by: { order: asc }) { id name @@ -79,9 +80,7 @@ export type InformationsQueryProps = { id?: string; }; -export type InformationsResult = Information & { - updateDate: string; -}; +export type InformationsResult = Information; export type InformationsQueryResult = { data?: InformationsResult; @@ -101,14 +100,10 @@ export const useInformationsQuery = ({ }, }); const information = data?.information_informations[0]; - const updateDate = information?.updatedAt - ? format(parseISO(information.updatedAt), "dd/MM/yyyy") - : ""; return { data: information ? { ...information, - updateDate, } : undefined, error, diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsCreate.tsx b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsCreate.tsx index d455f7f90..0c1040442 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsCreate.tsx +++ b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsCreate.tsx @@ -21,7 +21,7 @@ export const InformationsCreate = (): JSX.Element => { }); setDefaultData({ title: "", - updatedAt: "", + displayDate: "", dismissalProcess: false, description: "", metaDescription: "", diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx index a8c590951..8cb9603f8 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx +++ b/targets/frontend/src/modules/informations/components/informationsEdit/InformationsForm.tsx @@ -2,7 +2,11 @@ import { Button, FormControl, Stack, Typography } from "@mui/material"; import React, { useState } from "react"; import { useFieldArray, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { FormRadioGroup, FormTextField } from "src/components/forms"; +import { + FormDatePicker, + FormRadioGroup, + FormTextField, +} from "src/components/forms"; import { InformationsResult } from "./Informations.query"; import { Information, informationSchema } from "../../type"; @@ -59,12 +63,10 @@ export const InformationsForm = ({
- diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/__tests__/InformationsForm.test.tsx b/targets/frontend/src/modules/informations/components/informationsEdit/__tests__/InformationsForm.test.tsx index 5f39bb13e..5689a90af 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/__tests__/InformationsForm.test.tsx +++ b/targets/frontend/src/modules/informations/components/informationsEdit/__tests__/InformationsForm.test.tsx @@ -42,8 +42,7 @@ const information: InformationsResult = { url: "http://ref1.ref", }, ], - updateDate: "19/04/2023", - updatedAt: "2023-10-20T15:29:22.687685+00:00", + displayDate: "2023-04-19T15:29:22.687685+00:00", }; const onSubmit = jest.fn(() => Promise.resolve()); diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mapping.ts b/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mapping.ts index 06788b482..847e85937 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mapping.ts +++ b/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mapping.ts @@ -11,7 +11,6 @@ import { UpsertInformationObject } from "./editInformation.type"; const removeTypename = (obj: any) => { delete obj?.__typename; - delete obj?.updateDate; return obj; }; diff --git a/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mutation.ts b/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mutation.ts index 1778b48ee..1034e1d02 100644 --- a/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mutation.ts +++ b/targets/frontend/src/modules/informations/components/informationsEdit/editInformation.mutation.ts @@ -50,6 +50,7 @@ export const informationMutation = gql` metaDescription referenceLabel sectionDisplayMode + displayDate ] } ) { diff --git a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts b/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts index d024e5ccb..e7c7a2254 100644 --- a/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts +++ b/targets/frontend/src/modules/informations/components/informationsList/InformationsList.query.ts @@ -16,6 +16,7 @@ export const informationsListQuery = `query informationsList($search: String) { referenceLabel sectionDisplayMode title + displayDate updatedAt } }`; diff --git a/targets/frontend/src/modules/informations/mapInformationToDocument.ts b/targets/frontend/src/modules/informations/mapInformationToDocument.ts new file mode 100644 index 000000000..49df56f2b --- /dev/null +++ b/targets/frontend/src/modules/informations/mapInformationToDocument.ts @@ -0,0 +1,103 @@ +import { Information } from "src/modules/informations"; +import { format, parseISO } from "date-fns"; +import { generateCdtnId, generateInitialId } from "@shared/utils"; +import slugify from "@socialgouv/cdtn-slugify"; +import { HasuraDocument } from "@socialgouv/cdtn-types"; +import { getGlossaryContent } from "src/modules/common/getGlossaryContent"; + +export const mapInformationToDocument = async ( + data: Information, + document?: HasuraDocument +): Promise> => { + const introWithGlossary = await getGlossaryContent( + "markdown", + data.intro ?? "" + ); + return { + cdtn_id: document?.cdtn_id ?? generateCdtnId(data.title), + initial_id: data.id ?? generateInitialId(), + source: "information", + meta_description: data.metaDescription ?? data.description, + title: data.title, + text: data.title, + slug: document?.slug ?? slugify(data.title), + is_searchable: document ? document.is_searchable : true, + is_published: document ? document.is_published : true, + is_available: true, + document: { + date: format(parseISO(data.displayDate), "dd/MM/yyyy"), + intro: data.intro, + introWithGlossary, + description: data.description, + sectionDisplayMode: data.sectionDisplayMode, + dismissalProcess: data.dismissalProcess, + references: data.references.length + ? [ + { + label: data.referenceLabel, + links: data.references, + }, + ] + : undefined, + contents: await Promise.all( + data.contents.map( + async ({ name, title, blocks, references, referenceLabel }) => { + return { + name, + title, + blocks: await Promise.all( + blocks.map(async (block) => { + const htmlWithGlossary = await getGlossaryContent( + "markdown", + block.content ?? "" + ); + return { + type: block.type, + ...(block.type === "content" + ? { + title: block.content, + } + : { + markdown: block.content, + htmlWithGlossary, + }), + ...(block.type === "graphic" + ? { + size: block.file?.size, + imgUrl: block.img?.url, + altText: block.img?.altText, + fileUrl: block.file?.url, + } + : {}), + ...(block.type === "content" + ? { + blockDisplayMode: block.contentDisplayMode, + contents: block.contents?.length + ? block.contents.map(({ document }) => { + return { + title: document.title, + cdtnId: document.cdtnId, + source: document.source, + }; + }) + : undefined, + } + : {}), + }; + }) + ), + references: references?.length + ? [ + { + label: referenceLabel, + links: references, + }, + ] + : undefined, + }; + } + ) + ), + }, + }; +}; diff --git a/targets/frontend/src/modules/informations/type.ts b/targets/frontend/src/modules/informations/type.ts index 596a6605f..69adc9103 100644 --- a/targets/frontend/src/modules/informations/type.ts +++ b/targets/frontend/src/modules/informations/type.ts @@ -94,5 +94,6 @@ export const informationSchema = z.object({ updatedAt: z.string().nullable().optional(), contents: z.array(informationContentSchema), references: z.array(referenceSchema), + displayDate: z.string(), }); export type Information = z.infer; diff --git a/targets/frontend/src/modules/models/api/modelsQuery.ts b/targets/frontend/src/modules/models/api/modelsQuery.ts index e94be66d5..812edb196 100644 --- a/targets/frontend/src/modules/models/api/modelsQuery.ts +++ b/targets/frontend/src/modules/models/api/modelsQuery.ts @@ -13,6 +13,7 @@ export const modelsQuery = gql` previewHTML createdAt updatedAt + displayDate file { id url diff --git a/targets/frontend/src/modules/models/components/Common/Form.tsx b/targets/frontend/src/modules/models/components/Common/Form.tsx index fd9574a15..5be556139 100644 --- a/targets/frontend/src/modules/models/components/Common/Form.tsx +++ b/targets/frontend/src/modules/models/components/Common/Form.tsx @@ -13,6 +13,7 @@ import { FormFileField, FormTextField, FormToggleButtonGroup, + FormDatePicker, } from "src/components/forms"; import { Controller, useForm } from "react-hook-form"; @@ -128,6 +129,7 @@ export const ModelForm = ({ previewHTML: newData.previewHTML!, legiReferences: newData.legiReferences!, otherReferences: newData.otherReferences!, + displayDate: newData.displayDate!, }); setSnack({ open: true, @@ -183,18 +185,13 @@ export const ModelForm = ({ return ( - - {model && ( - - - - )} + + + { previewHTML: data.previewHTML, models_legi_references: formatLegiReferences(data.legiReferences), models_other_references: formatOtherReferences(data.otherReferences), + displayDate: data.displayDate, }, }); if (result.error) { diff --git a/targets/frontend/src/modules/models/components/Edition/index.tsx b/targets/frontend/src/modules/models/components/Edition/index.tsx index 4ecf2f53e..b846aae07 100644 --- a/targets/frontend/src/modules/models/components/Edition/index.tsx +++ b/targets/frontend/src/modules/models/components/Edition/index.tsx @@ -15,7 +15,6 @@ export const ModelEdition = ({ id }: Props): React.ReactElement => { const update = useModelUpdateMutation(); const publish = usePublishMutation(); - console.log("Use list model query ", data, fetching, error); if (error) { return ( diff --git a/targets/frontend/src/modules/models/components/Edition/model.mutation.ts b/targets/frontend/src/modules/models/components/Edition/model.mutation.ts index 6960c004a..bacf03fd2 100644 --- a/targets/frontend/src/modules/models/components/Edition/model.mutation.ts +++ b/targets/frontend/src/modules/models/components/Edition/model.mutation.ts @@ -27,6 +27,7 @@ const updateModelQuery = gql` metaDescription type previewHTML + displayDate ] } ) { @@ -88,6 +89,7 @@ export const useModelUpdateMutation = (): MutationFn => { models_other_references: { data: formatOtherReferences(data.otherReferences), }, + displayDate: data.displayDate, }, }); if (result.error) { diff --git a/targets/frontend/src/modules/models/components/Edition/model.query.ts b/targets/frontend/src/modules/models/components/Edition/model.query.ts index c6a828fbb..755aa504d 100644 --- a/targets/frontend/src/modules/models/components/Edition/model.query.ts +++ b/targets/frontend/src/modules/models/components/Edition/model.query.ts @@ -14,6 +14,7 @@ export const listModelsQuery = gql` previewHTML createdAt updatedAt + displayDate file { id url @@ -63,17 +64,8 @@ export const useListModelQuery = ({ id, }, }); - const model = data?.model; - const updatedAt = model?.updatedAt - ? format(parseISO(model.updatedAt), "dd/MM/yyyy") - : ""; return { - data: model - ? { - ...model, - updatedAt, - } - : undefined, + data: data?.model, error, fetching, reexecuteQuery, diff --git a/targets/frontend/src/modules/models/components/List/list.query.ts b/targets/frontend/src/modules/models/components/List/list.query.ts index 5fcaafb26..ce592370d 100644 --- a/targets/frontend/src/modules/models/components/List/list.query.ts +++ b/targets/frontend/src/modules/models/components/List/list.query.ts @@ -10,12 +10,12 @@ export const listModelsQuery = gql` id title type - updatedAt + displayDate } } `; -export type ModelResult = Pick; +export type ModelResult = Pick; export type QueryResult = { models: ModelResult[]; diff --git a/targets/frontend/src/modules/models/components/graphql.type.ts b/targets/frontend/src/modules/models/components/graphql.type.ts index 58d06c6b4..8fa2f6e72 100644 --- a/targets/frontend/src/modules/models/components/graphql.type.ts +++ b/targets/frontend/src/modules/models/components/graphql.type.ts @@ -60,4 +60,5 @@ export type ModelModelsInsertInput = { title?: string; type?: string; updatedAt?: string; + displayDate?: string; }; diff --git a/targets/frontend/src/modules/models/mapModelToDocument.ts b/targets/frontend/src/modules/models/mapModelToDocument.ts new file mode 100644 index 000000000..aadaf73ad --- /dev/null +++ b/targets/frontend/src/modules/models/mapModelToDocument.ts @@ -0,0 +1,47 @@ +import { format, parseISO } from "date-fns"; +import { generateCdtnId } from "@shared/utils"; +import slugify from "@socialgouv/cdtn-slugify"; +import { Model } from "../models"; +import { HasuraDocument } from "@socialgouv/cdtn-types"; + +export const mapModelToDocument = ( + data: Model, + document?: HasuraDocument +): HasuraDocument => { + return { + cdtn_id: document?.cdtn_id ?? generateCdtnId(data.title), + initial_id: data.id!, + source: "modeles_de_courriers", + meta_description: data.metaDescription, + title: data.title, + text: data.intro, + slug: document?.slug ?? slugify(data.title), + is_searchable: document ? document.is_searchable : true, + is_published: document ? document.is_published : true, + is_available: true, + document: { + meta_title: data.metaTitle, + type: data.type, + date: format(parseISO(data.displayDate), "dd/MM/yyyy"), + author: "Ministère du Travail", + references: data.legiReferences + .map((item) => ({ + url: `https://www.legifrance.gouv.fr/codes/article_lc/${item.legiArticle.cid}`, + title: item.legiArticle.label, + type: "external", + })) + .concat( + data.otherReferences.map((item) => ({ + url: item.url ?? "", + title: item.label, + type: "external", + })) + ), + description: data.metaDescription, + intro: data.intro, + filename: data.file.url, + filesize: parseInt(data.file.size ?? "0"), + html: data.previewHTML, + }, + }; +}; diff --git a/targets/frontend/src/modules/models/type.ts b/targets/frontend/src/modules/models/type.ts index 463c29abd..d3013d351 100644 --- a/targets/frontend/src/modules/models/type.ts +++ b/targets/frontend/src/modules/models/type.ts @@ -32,6 +32,7 @@ export const modelSchema = z.object({ file: fileSchema, legiReferences: z.array(legiReferenceSchema), otherReferences: z.array(otherReferenceSchema), + displayDate: z.string(), }); export type Model = z.infer; diff --git a/targets/frontend/src/modules/sitemap/__tests__/date.spec.ts b/targets/frontend/src/modules/sitemap/__tests__/date.spec.ts new file mode 100644 index 000000000..49a6f4816 --- /dev/null +++ b/targets/frontend/src/modules/sitemap/__tests__/date.spec.ts @@ -0,0 +1,25 @@ +import { transformStringDate, formatDateToCustomISO } from "../date"; + +describe("Date", () => { + describe("transformStringDate", () => { + it("should transform a French date string to a Date object", () => { + const frenchDate = "01/02/2023"; + const expectedDate = new Date("2023-02-01"); + expect(transformStringDate(frenchDate)).toEqual(expectedDate); + }); + + it("should transform a non-French date string to a Date object", () => { + const nonFrenchDate = "2023-02-01"; + const expectedDate = new Date(nonFrenchDate); + expect(transformStringDate(nonFrenchDate)).toEqual(expectedDate); + }); + }); + + describe("formatDateToCustomISO", () => { + it("should format a date to a custom ISO string", () => { + const date = new Date("2023-02-01T12:34:56Z"); + const expectedFormattedDate = "2023-02-01"; + expect(formatDateToCustomISO(date)).toEqual(expectedFormattedDate); + }); + }); +}); diff --git a/targets/frontend/src/modules/sitemap/date.ts b/targets/frontend/src/modules/sitemap/date.ts new file mode 100644 index 000000000..f173f65c7 --- /dev/null +++ b/targets/frontend/src/modules/sitemap/date.ts @@ -0,0 +1,17 @@ +export const transformStringDate = (date: string): Date => { + const isFrenchDate = date.match(/^\d{2}\/\d{2}\/\d{4}$/); + if (isFrenchDate) { + const [day, month, year] = date.split("/"); + return new Date(`${year}-${month}-${day}`); + } else { + return new Date(date); + } +}; + +export function formatDateToCustomISO(date: Date): string { + const pad = (num: number) => (num < 10 ? "0" : "") + num; + const year = date.getFullYear(); + const month = pad(date.getMonth() + 1); + const day = pad(date.getDate()); + return `${year}-${month}-${day}`; +} diff --git a/targets/frontend/src/pages/api/sitemap.ts b/targets/frontend/src/pages/api/sitemap.ts index 13110580a..4998494b7 100644 --- a/targets/frontend/src/pages/api/sitemap.ts +++ b/targets/frontend/src/pages/api/sitemap.ts @@ -6,12 +6,16 @@ import { } from "@socialgouv/cdtn-sources"; import { NextApiRequest, NextApiResponse } from "next"; import pLimit from "p-limit"; +import { + formatDateToCustomISO, + transformStringDate, +} from "src/modules/sitemap/date"; export type Document = { - __typename: string; - modified: string; + updated_at: string; slug: string; source: SourceRoute; + document: any; }; const slugStartsWithNumber = (slug: string) => { @@ -23,25 +27,27 @@ export async function toUrlEntries( documents: Document[], glossaryTerms: Document[], baseUrl: string - // @ts-ignore -): any { - let latestPost = 0; +) { + let latestPostDate = new Date("2020-01-01"); + const pages = documents.flat().map((doc) => { - const postDate = Date.parse(doc.modified); - if (!latestPost || postDate > latestPost) { - latestPost = postDate; + const date = doc.document?.date + ? transformStringDate(doc.document.date) + : new Date(doc.updated_at); + if (date.getTime() > latestPostDate.getTime()) { + latestPostDate = date; } const source = getRouteBySource(doc.source); const priority = - source === SOURCES.EDITORIAL_CONTENT || + source === "information" || + source === "modeles-de-courriers" || (source === "contribution" && !slugStartsWithNumber(doc.slug)) ? 0.7 : 0.5; const projectURL = `${baseUrl}/${source}/${doc.slug}`; - return toUrlEntry(projectURL, doc.modified, priority); + return toUrlEntry(projectURL, date, priority); }); - /** static pages list come from cdtn project */ const staticPages = [ `/a-propos`, `/droit-du-travail`, @@ -51,59 +57,44 @@ export async function toUrlEntries( `/modeles-de-courriers`, `/outils`, `/glossaire`, - ].map((path) => toUrlEntry(`${baseUrl}${path}`)); + ].map((path) => toUrlEntry(`${baseUrl}${path}`, latestPostDate)); - const glossaryPages = glossaryTerms.map(({ slug, modified }) => + const glossaryPages = glossaryTerms.map(({ slug, updated_at }) => toUrlEntry( `${baseUrl}/${getRouteBySource(SOURCES.GLOSSARY)}/${slug}`, - modified + new Date(updated_at) ) ); - return { glossaryPages, latestPost, pages, staticPages }; + return { latestPostDate, glossaryPages, pages, staticPages }; } export default async function Sitemap( req: NextApiRequest, res: NextApiResponse ) { - console.log("[/api/sitemap]", " request: ", req.query); - const startProcessAt = process.hrtime(); const baseUrl = (req.query.baseurl as string) || "https://code.travail.gouv.fr"; const documents = await getDocuments(); const glossaryTerms = await getGlossary(); - const { latestPost, pages, staticPages, glossaryPages } = await toUrlEntries( - documents, - glossaryTerms, - baseUrl - ); + const { latestPostDate, pages, staticPages, glossaryPages } = + await toUrlEntries(documents, glossaryTerms, baseUrl); res.setHeader("Content-Type", "text/xml"); - res.write(` - -${baseUrl}/${new Date(latestPost).toISOString()}0.8 -${pages.concat(staticPages, glossaryPages).join("")} - -`); + res.write( + `${baseUrl}/${formatDateToCustomISO( + latestPostDate + )}0.8${pages + .concat(staticPages, glossaryPages) + .join("")}` + ); res.end(); - const endProcess = process.hrtime(startProcessAt); - console.log("[/api/sitemap]", " end in ", endProcess); } -/** - * Transform url into a sitemap entry - * @param {string} url - * @param {Date} date - * @param {number} priority - */ -function toUrlEntry( - url: string, - date = new Date().toISOString(), - priority = 0.5 -) { - return `${url}${date}${priority} -`; +function toUrlEntry(url: string, date: Date, priority = 0.5) { + return `${url}${formatDateToCustomISO( + date + )}${priority}`; } /** @@ -112,14 +103,20 @@ function toUrlEntry( */ async function getNbTotalDocuments(sources: string[]) { const gqlCountDocument = ` -query countDocuments($sources: [String!]!) { - documents_aggregate(where: {is_available:{_eq: true}, is_published: {_eq: true}, source: {_in: $sources }}){ - aggregate { - count + query countDocuments($sources: [String!]!) { + documents_aggregate( + where: { + is_available: { _eq: true } + is_published: { _eq: true } + source: { _in: $sources } + } + ) { + aggregate { + count + } + } } - } -} -`; + `; const response = await gqlClient() .query(gqlCountDocument, { sources, @@ -155,7 +152,8 @@ async function getDocuments() { ) { slug source - modified: updated_at + updated_at + document } } `; @@ -204,7 +202,7 @@ async function getDocuments() { async function getGlossary() { const gqlListGlossryTerm = `query getGlossary { glossary(order_by: {slug: asc}) { - slug, modified: updated_at + slug, updated_at } }`; const terms = await gqlClient().query(gqlListGlossryTerm, {}).toPromise(); diff --git a/targets/frontend/test/jest.setup.js b/targets/frontend/test/jest.setup.js index 6a6940907..af4bb1455 100644 --- a/targets/frontend/test/jest.setup.js +++ b/targets/frontend/test/jest.setup.js @@ -1 +1,3 @@ import "@testing-library/jest-dom/extend-expect"; + +process.env.TZ = "UTC"; diff --git a/targets/hasura/metadata/databases/default/tables/contribution_answers.yaml b/targets/hasura/metadata/databases/default/tables/contribution_answers.yaml index 57fe8c31c..e609c8817 100644 --- a/targets/hasura/metadata/databases/default/tables/contribution_answers.yaml +++ b/targets/hasura/metadata/databases/default/tables/contribution_answers.yaml @@ -82,6 +82,7 @@ select_permissions: - content_service_public_cdtn_id - content_type - description + - display_date - id - message_block_generic_no_CDT - question_id @@ -97,6 +98,7 @@ update_permissions: - content_service_public_cdtn_id - content_type - description + - display_date - message_block_generic_no_CDT filter: {} check: null diff --git a/targets/hasura/metadata/databases/default/tables/information_informations.yaml b/targets/hasura/metadata/databases/default/tables/information_informations.yaml index ff5c606c1..7a07860c9 100644 --- a/targets/hasura/metadata/databases/default/tables/information_informations.yaml +++ b/targets/hasura/metadata/databases/default/tables/information_informations.yaml @@ -5,6 +5,8 @@ configuration: column_config: dismissal_process: custom_name: dismissalProcess + display_date: + custom_name: displayDate meta_description: custom_name: metaDescription meta_title: @@ -17,6 +19,7 @@ configuration: custom_name: updatedAt custom_column_names: dismissal_process: dismissalProcess + display_date: displayDate meta_description: metaDescription meta_title: metaTitle reference_label: referenceLabel @@ -45,6 +48,7 @@ insert_permissions: columns: - description - dismissal_process + - display_date - id - intro - meta_description @@ -59,6 +63,7 @@ insert_permissions: columns: - description - dismissal_process + - display_date - id - intro - meta_description @@ -73,6 +78,7 @@ select_permissions: columns: - description - dismissal_process + - display_date - id - intro - meta_description @@ -88,6 +94,7 @@ select_permissions: columns: - description - dismissal_process + - display_date - id - intro - meta_description @@ -104,6 +111,7 @@ update_permissions: columns: - description - dismissal_process + - display_date - id - intro - meta_description diff --git a/targets/hasura/metadata/databases/default/tables/model_models.yaml b/targets/hasura/metadata/databases/default/tables/model_models.yaml index 688b14683..600572230 100644 --- a/targets/hasura/metadata/databases/default/tables/model_models.yaml +++ b/targets/hasura/metadata/databases/default/tables/model_models.yaml @@ -5,6 +5,8 @@ configuration: column_config: created_at: custom_name: createdAt + display_date: + custom_name: displayDate file_id: custom_name: fileId meta_description: @@ -17,6 +19,7 @@ configuration: custom_name: updatedAt custom_column_names: created_at: createdAt + display_date: displayDate file_id: fileId meta_description: metaDescription meta_title: metaTitle @@ -47,44 +50,47 @@ insert_permissions: permission: check: {} columns: - - id - created_at - - updated_at - - title + - display_date + - file_id + - id - intro - - meta_title - meta_description - - type + - meta_title - preview_html - - file_id + - title + - type + - updated_at select_permissions: - role: super permission: columns: + - created_at + - display_date + - file_id + - id - intro - meta_description - meta_title - preview_html - title - type - - created_at - updated_at - - file_id - - id filter: {} update_permissions: - role: super permission: columns: - created_at - - intro + - display_date - file_id + - intro - meta_description - meta_title - preview_html - title - type filter: {} - check: null + check: {} set: updated_at: now() diff --git a/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/down.sql b/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/down.sql new file mode 100644 index 000000000..eb0b8e299 --- /dev/null +++ b/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "information"."informations" add column "display_date" date +-- null; diff --git a/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/up.sql b/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/up.sql new file mode 100644 index 000000000..70cc0519e --- /dev/null +++ b/targets/hasura/migrations/default/1723964804960_alter_table_information_informations_add_column_display_date/up.sql @@ -0,0 +1,2 @@ +alter table "information"."informations" add column "display_date" date + null; diff --git a/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/down.sql b/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/down.sql new file mode 100644 index 000000000..f035226bb --- /dev/null +++ b/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "contribution"."answers" add column "display_date" date +-- null; diff --git a/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/up.sql b/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/up.sql new file mode 100644 index 000000000..cc893a891 --- /dev/null +++ b/targets/hasura/migrations/default/1723964851942_alter_table_contribution_answers_add_column_display_date/up.sql @@ -0,0 +1,2 @@ +alter table "contribution"."answers" add column "display_date" date + null; diff --git a/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/down.sql b/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/down.sql new file mode 100644 index 000000000..8255b5763 --- /dev/null +++ b/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "model"."models" add column "display_date" date +-- null; diff --git a/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/up.sql b/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/up.sql new file mode 100644 index 000000000..c7efa6775 --- /dev/null +++ b/targets/hasura/migrations/default/1723964911747_alter_table_model_models_add_column_display_date/up.sql @@ -0,0 +1,2 @@ +alter table "model"."models" add column "display_date" date + null; diff --git a/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/down.sql b/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/down.sql new file mode 100644 index 000000000..95a5255a4 --- /dev/null +++ b/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- UPDATE contribution.answers +-- SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/up.sql b/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/up.sql new file mode 100644 index 000000000..f0e1438d3 --- /dev/null +++ b/targets/hasura/migrations/default/1724228783542_set_display_date_by_default/up.sql @@ -0,0 +1,2 @@ +UPDATE contribution.answers +SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/down.sql b/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/down.sql new file mode 100644 index 000000000..995395896 --- /dev/null +++ b/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- UPDATE information.informations +-- SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/up.sql b/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/up.sql new file mode 100644 index 000000000..3b97c453d --- /dev/null +++ b/targets/hasura/migrations/default/1724228853700_set_display_date_by_default_info/up.sql @@ -0,0 +1,2 @@ +UPDATE information.informations +SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/down.sql b/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/down.sql new file mode 100644 index 000000000..aff0cb27c --- /dev/null +++ b/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/down.sql @@ -0,0 +1,4 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- UPDATE model.models +-- SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/up.sql b/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/up.sql new file mode 100644 index 000000000..43445e50c --- /dev/null +++ b/targets/hasura/migrations/default/1724228912582_set_display_date_by_default_models/up.sql @@ -0,0 +1,2 @@ +UPDATE model.models +SET display_date = updated_at::date; diff --git a/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/down.sql b/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/down.sql new file mode 100644 index 000000000..d4c5c20be --- /dev/null +++ b/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/down.sql @@ -0,0 +1 @@ +alter table "model"."models" alter column "display_date" drop not null; diff --git a/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/up.sql b/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/up.sql new file mode 100644 index 000000000..5ef96acd0 --- /dev/null +++ b/targets/hasura/migrations/default/1724228940364_alter_table_model_models_alter_column_display_date/up.sql @@ -0,0 +1 @@ +alter table "model"."models" alter column "display_date" set not null; diff --git a/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/down.sql b/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/down.sql new file mode 100644 index 000000000..b01f77168 --- /dev/null +++ b/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/down.sql @@ -0,0 +1 @@ +alter table "information"."informations" alter column "display_date" drop not null; diff --git a/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/up.sql b/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/up.sql new file mode 100644 index 000000000..e9931426d --- /dev/null +++ b/targets/hasura/migrations/default/1724228963430_alter_table_information_informations_alter_column_display_date/up.sql @@ -0,0 +1 @@ +alter table "information"."informations" alter column "display_date" set not null; diff --git a/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/down.sql b/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/down.sql new file mode 100644 index 000000000..d11e95941 --- /dev/null +++ b/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/down.sql @@ -0,0 +1 @@ +alter table "contribution"."answers" alter column "display_date" drop not null; diff --git a/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/up.sql b/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/up.sql new file mode 100644 index 000000000..02eba9909 --- /dev/null +++ b/targets/hasura/migrations/default/1724228984964_alter_table_contribution_answers_alter_column_display_date/up.sql @@ -0,0 +1 @@ +alter table "contribution"."answers" alter column "display_date" set not null; diff --git a/yarn.lock b/yarn.lock index a6d1daf69..99e5488c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1618,6 +1618,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.25.0": + version: 7.25.0 + resolution: "@babel/runtime@npm:7.25.0" + dependencies: + regenerator-runtime: ^0.14.0 + checksum: 4a2a374a58eb01aaa65c5762606e90b3a1f448e0c637d42278b6cc0b42a9f5399b5f381ba9f237ee087da2860d14dd2d1de7bddcbe18be6a3cafba97e44bed64 + languageName: node + linkType: hard + "@babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.5 resolution: "@babel/template@npm:7.22.5" @@ -3870,6 +3879,23 @@ __metadata: languageName: node linkType: hard +"@mui/private-theming@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/private-theming@npm:5.16.6" + dependencies: + "@babel/runtime": ^7.23.9 + "@mui/utils": ^5.16.6 + prop-types: ^15.8.1 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 314ba598ab17cd425a36e4cab677ed26fe0939b23e53120da77cfbc3be6dada5428fa8e2a55cb697417599a4e3abfee6d4711de0a7318b9fb2c3a822b2d5b5a8 + languageName: node + linkType: hard + "@mui/styled-engine@npm:^5.14.11": version: 5.14.11 resolution: "@mui/styled-engine@npm:5.14.11" @@ -3891,6 +3917,27 @@ __metadata: languageName: node linkType: hard +"@mui/styled-engine@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/styled-engine@npm:5.16.6" + dependencies: + "@babel/runtime": ^7.23.9 + "@emotion/cache": ^11.11.0 + csstype: ^3.1.3 + prop-types: ^15.8.1 + peerDependencies: + "@emotion/react": ^11.4.1 + "@emotion/styled": ^11.3.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + checksum: 604f83b91801945336db211a8273061132668d01e9f456c30bb811a3b49cc5786b8b7dd8e0b5b89de15f6209abc900d9e679d3ae7a4651a6df45e323b6ed95c5 + languageName: node + linkType: hard + "@mui/system@npm:^5.14.11": version: 5.14.11 resolution: "@mui/system@npm:5.14.11" @@ -3919,6 +3966,46 @@ __metadata: languageName: node linkType: hard +"@mui/system@npm:^5.16.5": + version: 5.16.7 + resolution: "@mui/system@npm:5.16.7" + dependencies: + "@babel/runtime": ^7.23.9 + "@mui/private-theming": ^5.16.6 + "@mui/styled-engine": ^5.16.6 + "@mui/types": ^7.2.15 + "@mui/utils": ^5.16.6 + clsx: ^2.1.0 + csstype: ^3.1.3 + prop-types: ^15.8.1 + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: 86cc11d062645b6742328178ca3a9e2aa2c6d064a559e4fb8c6c6bb8251794959b9dad385f9508fdcab2ae2764503c80f7c3d4f6eb1e0e8aa649f28d4f59133b + languageName: node + linkType: hard + +"@mui/types@npm:^7.2.15": + version: 7.2.15 + resolution: "@mui/types@npm:7.2.15" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 86c7e58a4ead970204b746e3ead71d4b9b3ea1eebe237be88ae0d6b3fda958fb5aa73c238960c8c2b2cdb1cf424a961299a2292e8d7364ddb41bd20059b70993 + languageName: node + linkType: hard + "@mui/types@npm:^7.2.4": version: 7.2.4 resolution: "@mui/types@npm:7.2.4" @@ -3949,6 +4036,73 @@ __metadata: languageName: node linkType: hard +"@mui/utils@npm:^5.16.5, @mui/utils@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/utils@npm:5.16.6" + dependencies: + "@babel/runtime": ^7.23.9 + "@mui/types": ^7.2.15 + "@types/prop-types": ^15.7.12 + clsx: ^2.1.1 + prop-types: ^15.8.1 + react-is: ^18.3.1 + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 6f8068f07f60a842fcb2e2540eecbd9c5f04df695bcc427184720e8ae138ae689fefd3c20147ab7c76e809ede6e10f5e08d1c34cd3a8b09bd22d2020a666a96f + languageName: node + linkType: hard + +"@mui/x-date-pickers@npm:^7.13.0": + version: 7.13.0 + resolution: "@mui/x-date-pickers@npm:7.13.0" + dependencies: + "@babel/runtime": ^7.25.0 + "@mui/system": ^5.16.5 + "@mui/utils": ^5.16.5 + "@types/react-transition-group": ^4.4.10 + clsx: ^2.1.1 + prop-types: ^15.8.1 + react-transition-group: ^4.4.5 + peerDependencies: + "@emotion/react": ^11.9.0 + "@emotion/styled": ^11.8.1 + "@mui/material": ^5.15.14 + date-fns: ^2.25.0 || ^3.2.0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 + dayjs: ^1.10.7 + luxon: ^3.0.2 + moment: ^2.29.4 + moment-hijri: ^2.1.2 + moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + date-fns: + optional: true + date-fns-jalali: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + moment-hijri: + optional: true + moment-jalaali: + optional: true + checksum: 50db036f6d6877e576b9b265f7008bbc7095015bf7d63625e09537e3f5129852c990cc90904e71e20a1edb9dcd4aed9e308d25a0db2e423d43413cf1bee85b92 + languageName: node + linkType: hard + "@next/env@npm:14.1.4": version: 14.1.4 resolution: "@next/env@npm:14.1.4" @@ -6775,6 +6929,13 @@ __metadata: languageName: node linkType: hard +"@types/prop-types@npm:^15.7.12": + version: 15.7.12 + resolution: "@types/prop-types@npm:15.7.12" + checksum: ac16cc3d0a84431ffa5cfdf89579ad1e2269549f32ce0c769321fdd078f84db4fbe1b461ed5a1a496caf09e637c0e367d600c541435716a55b1d9713f5035dfe + languageName: node + linkType: hard + "@types/qs@npm:*": version: 6.9.7 resolution: "@types/qs@npm:6.9.7" @@ -6807,6 +6968,15 @@ __metadata: languageName: node linkType: hard +"@types/react-transition-group@npm:^4.4.10": + version: 4.4.11 + resolution: "@types/react-transition-group@npm:4.4.11" + dependencies: + "@types/react": "*" + checksum: a6e3b2e4363cb019e256ae4f19dadf9d7eb199da1a5e4109bbbf6a132821884044d332e9c74b520b1e5321a7f545502443fd1ce0b18649c8b510fa4220b0e5c2 + languageName: node + linkType: hard + "@types/react-transition-group@npm:^4.4.6": version: 4.4.7 resolution: "@types/react-transition-group@npm:4.4.7" @@ -8966,6 +9136,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.0, clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: acd3e1ab9d8a433ecb3cc2f6a05ab95fe50b4a3cfc5ba47abb6cbf3754585fcb87b84e90c822a1f256c4198e3b41c7f6c391577ffc8678ad587fc0976b24fd57 + languageName: node + linkType: hard + "cmd-shim@npm:^5.0.0": version: 5.0.0 resolution: "cmd-shim@npm:5.0.0" @@ -9604,6 +9781,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 + languageName: node + linkType: hard + "csv-parser@npm:^3.0.0": version: 3.0.0 resolution: "csv-parser@npm:3.0.0" @@ -11984,6 +12168,7 @@ __metadata: "@hookform/resolvers": ^3.3.1 "@mui/icons-material": 5.11.16 "@mui/material": 5.14.11 + "@mui/x-date-pickers": ^7.13.0 "@reach/accordion": ^0.16.1 "@reach/dialog": ^0.16.0 "@reach/menu-button": ^0.16.1 @@ -19628,6 +19813,13 @@ __metadata: languageName: node linkType: hard +"react-is@npm:^18.3.1": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: e20fe84c86ff172fc8d898251b7cc2c43645d108bf96d0b8edf39b98f9a2cae97b40520ee7ed8ee0085ccc94736c4886294456033304151c3f94978cec03df21 + languageName: node + linkType: hard + "react-remove-scroll-bar@npm:^2.3.4": version: 2.3.4 resolution: "react-remove-scroll-bar@npm:2.3.4"