From 707a60f03cfe52a19d973e2139c8d3d7e34c51fd Mon Sep 17 00:00:00 2001 From: Xavier Jp Date: Tue, 28 May 2024 11:32:21 +0200 Subject: [PATCH 01/14] fix: only display marker when coords exist (#1074) --- components/map/check-lat-lng.ts | 27 ++++++++++++ components/map/map-etablissement.tsx | 26 +---------- components/map/map-results.tsx | 65 ++++++++++++++++------------ 3 files changed, 67 insertions(+), 51 deletions(-) create mode 100644 components/map/check-lat-lng.ts diff --git a/components/map/check-lat-lng.ts b/components/map/check-lat-lng.ts new file mode 100644 index 000000000..0db1cb7bb --- /dev/null +++ b/components/map/check-lat-lng.ts @@ -0,0 +1,27 @@ +import { LngLatLike } from 'maplibre-gl'; + +export function checkLatLng( + latitude: string, + longitude: string +): LngLatLike | null { + try { + const lat = parseFloat(latitude); + const lng = parseFloat(longitude); + if ( + isNaN(lat) || + isNaN(lng) || + lat < -90 || + lat > 90 || + lng < -180 || + lng > 180 + ) { + throw new Error('Invalid coords'); + } + + return { + lat, + lng, + }; + } catch {} + return null; +} diff --git a/components/map/map-etablissement.tsx b/components/map/map-etablissement.tsx index a856a7ecf..72aadfc26 100644 --- a/components/map/map-etablissement.tsx +++ b/components/map/map-etablissement.tsx @@ -4,34 +4,12 @@ import constants from '#models/constants'; import { IEtablissement } from '#models/core/types'; import { formatSiret } from '#utils/helpers'; -import maplibregl, { LngLatLike, Map } from 'maplibre-gl'; +import maplibregl, { Map } from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { useEffect, useRef } from 'react'; +import { checkLatLng } from './check-lat-lng'; import './map.css'; -function checkLatLng(latitude: string, longitude: string): LngLatLike | null { - try { - const lat = parseFloat(latitude); - const lng = parseFloat(longitude); - if ( - isNaN(lat) || - isNaN(lng) || - lat < -90 || - lat > 90 || - lng < -180 || - lng > 180 - ) { - throw new Error('Invalid coords'); - } - - return { - lat, - lng, - }; - } catch {} - return null; -} - export function MapEtablissement({ etablissement, }: { diff --git a/components/map/map-results.tsx b/components/map/map-results.tsx index d6eefc636..07d96622e 100644 --- a/components/map/map-results.tsx +++ b/components/map/map-results.tsx @@ -7,6 +7,7 @@ import { formatIntFr, formatSiret } from '#utils/helpers'; import maplibregl, { Map } from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import { useEffect, useRef } from 'react'; +import { checkLatLng } from './check-lat-lng'; import './map.css'; export default function MapWithResults({ @@ -35,42 +36,52 @@ export default function MapWithResults({ }); results.results.forEach((result) => { - var popup = new maplibregl.Popup({ offset: 25 }).setHTML( - `
${formatIntFr( - result.siren - )}
${result.nomComplet}
đź“Ť${ - result.siege.adresse - }
` + const coordsSiege = checkLatLng( + result.siege.latitude, + result.siege.longitude ); - new maplibregl.Marker({ color: constants.colors.frBlue }) - //@ts-ignore - .setLngLat([result.siege.longitude, result.siege.latitude]) - .setPopup(popup) - //@ts-ignore - .addTo(map.current); + if (coordsSiege) { + const popup = new maplibregl.Popup({ offset: 25 }).setHTML( + `
${formatIntFr( + result.siren + )}
${result.nomComplet}
đź“Ť${ + result.siege.adresse + }
` + ); + + new maplibregl.Marker({ color: constants.colors.frBlue }) + //@ts-ignore + .setLngLat([result.siege.longitude, result.siege.latitude]) + .setPopup(popup) + //@ts-ignore + .addTo(map.current); + } result.matchingEtablissements.forEach((match) => { if (match.estSiege) { return null; } - var popup = new maplibregl.Popup({ offset: 25 }).setHTML( - `
${formatSiret( - match.siret - )}
Etablissement secondaire de ${result.nomComplet}
đź“Ť${match.adresse}
` - ); + const coordsEtab = checkLatLng(match.latitude, match.longitude); + if (coordsEtab) { + var popup = new maplibregl.Popup({ offset: 25 }).setHTML( + `
${formatSiret( + match.siret + )}
Etablissement secondaire de ${result.nomComplet}
đź“Ť${match.adresse}
` + ); - new maplibregl.Marker({ - color: shouldColorZipCode ? 'yellow' : constants.colors.pastelBlue, - }) - //@ts-ignore - .setLngLat([match.longitude, match.latitude]) - .setPopup(popup) - //@ts-ignore - .addTo(map.current); + new maplibregl.Marker({ + color: shouldColorZipCode ? 'yellow' : constants.colors.pastelBlue, + }) + //@ts-ignore + .setLngLat([match.longitude, match.latitude]) + .setPopup(popup) + //@ts-ignore + .addTo(map.current); + } }); }); }, [results, shouldColorZipCode]); From f4d89934f602445455f3113755f6c006153af96c Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Tue, 28 May 2024 12:37:45 +0200 Subject: [PATCH 02/14] chore: log separately error that result in a 500 error page displayed --- app/global-error.tsx | 4 ++-- hooks/index.ts | 2 +- hooks/use-log-fatal-error-app-client.ts | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/global-error.tsx b/app/global-error.tsx index e8b830b27..21c2d621f 100644 --- a/app/global-error.tsx +++ b/app/global-error.tsx @@ -1,14 +1,14 @@ 'use client'; import NextError from 'next/error'; -import { useLogFatalErrorAppClient } from 'hooks'; +import { useLog500ErrorAppClient } from 'hooks'; export default function GlobalError({ error, }: { error: Error & { digest?: string }; }) { - useLogFatalErrorAppClient(error); + useLog500ErrorAppClient(error); return ( diff --git a/hooks/index.ts b/hooks/index.ts index cf248c74a..242216172 100644 --- a/hooks/index.ts +++ b/hooks/index.ts @@ -1,7 +1,7 @@ export * from './fetch/index'; export { useHeightTransition } from './use-height-transition'; -export { useLogFatalErrorAppClient } from './use-log-fatal-error-app-client'; +export * from './use-log-fatal-error-app-client'; export { useMeasure } from './use-measure'; export { useOutsideClick } from './use-outside-click'; export { usePrefersReducedMotion } from './use-prefers-reduced-motion'; diff --git a/hooks/use-log-fatal-error-app-client.ts b/hooks/use-log-fatal-error-app-client.ts index 7eaa5e6b8..a5805ad80 100644 --- a/hooks/use-log-fatal-error-app-client.ts +++ b/hooks/use-log-fatal-error-app-client.ts @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { Exception } from '#models/exceptions'; import { logFatalErrorInSentry } from '#utils/sentry'; export type NextAppError = Error & { digest?: string }; + export function useLogFatalErrorAppClient(error: Error & { digest?: string }) { useEffect(() => { // Log the error to Sentry @@ -17,3 +18,19 @@ export function useLogFatalErrorAppClient(error: Error & { digest?: string }) { ); }, [error]); } + +export function useLog500ErrorAppClient(error: Error & { digest?: string }) { + useEffect(() => { + // Log the error to Sentry + logFatalErrorInSentry( + new Exception({ + name: 'Client500PageDisplayed', + cause: error, + context: { + digest: error.digest, + page: window.location.pathname, + }, + }) + ); + }, [error]); +} From de007053f7221a9c8b4090d77d2d3e58a694aa54 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 08:09:45 +0200 Subject: [PATCH 03/14] chore: remove qualibat while their api is DOWN (#1077) (which is the case since may 14th) https://status.entreprise.api.gouv.fr/ --- .../[slug]/_components/protected-certificats.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/(header-default)/labels-certificats/[slug]/_components/protected-certificats.tsx b/app/(header-default)/labels-certificats/[slug]/_components/protected-certificats.tsx index af75940df..d240fc6d8 100644 --- a/app/(header-default)/labels-certificats/[slug]/_components/protected-certificats.tsx +++ b/app/(header-default)/labels-certificats/[slug]/_components/protected-certificats.tsx @@ -1,6 +1,5 @@ import { Suspense } from 'react'; import { OpqibiSection } from '#components/espace-agent-components/certifications/opqibi-section'; -import { QualibatSection } from '#components/espace-agent-components/certifications/qualibat-section'; import { QualifelecSection } from '#components/espace-agent-components/certifications/qualifelec-section'; import { IAPINotRespondingError, @@ -37,7 +36,8 @@ export async function ProtectedCertificats({ )} - + {/* Qualibat API is down. When it's back up, uncomment this line + */} From e3ffcd6ed992fdc61f19d6fc8f3792483877b982 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 08:16:25 +0200 Subject: [PATCH 04/14] fix: mobile header menu (#1070) * fix: mobile header menu fix #1020 * fix: keep style consistent --- components-ui/filter-menu/filter-menu.tsx | 7 +- components-ui/filter-menu/style.module.css | 4 +- components-ui/floating-modal/index.tsx | 16 ++- components-ui/floating-modal/style.module.css | 16 ++- components/header/header-core/index.tsx | 8 +- .../header/header-core/styles.module.css | 12 ++ components/header/menu/index.tsx | 58 +++++---- components/header/menu/styles.module.css | 111 ++++++++++++++++-- cypress/e2e/espace-agent.cy.js | 6 +- 9 files changed, 191 insertions(+), 47 deletions(-) diff --git a/components-ui/filter-menu/filter-menu.tsx b/components-ui/filter-menu/filter-menu.tsx index a144d213e..8e35ba47f 100644 --- a/components-ui/filter-menu/filter-menu.tsx +++ b/components-ui/filter-menu/filter-menu.tsx @@ -2,6 +2,7 @@ import { PropsWithChildren, useId, useState } from 'react'; import ButtonLink from '#components-ui/button'; import ButtonClose from '#components-ui/button/button-close'; +import FloatingModal from '#components-ui/floating-modal'; import { Icon } from '#components-ui/icon/wrapper'; import constants from '#models/constants'; import { @@ -78,9 +79,11 @@ export const FilterMenu: React.FC> = ({ /> )} -
{children}
{addSaveClearButton && ( @@ -99,7 +102,7 @@ export const FilterMenu: React.FC> = ({
)} - + ); diff --git a/components-ui/filter-menu/style.module.css b/components-ui/filter-menu/style.module.css index 93d910c2d..097374d17 100644 --- a/components-ui/filter-menu/style.module.css +++ b/components-ui/filter-menu/style.module.css @@ -64,12 +64,10 @@ span.search-filter-label:hover { .container { box-shadow: 0 0 15px -5px rgba(0, 0, 0, 0.3); top: 100%; + border: none; left: 0; position: absolute; - padding: 15px; margin-top: 5px; - background-color: #fff; - border-radius: 3px; width: 480px; z-index: 1000; } diff --git a/components-ui/floating-modal/index.tsx b/components-ui/floating-modal/index.tsx index f17597024..6663c9595 100644 --- a/components-ui/floating-modal/index.tsx +++ b/components-ui/floating-modal/index.tsx @@ -3,10 +3,18 @@ import styles from './style.module.css'; type IProps = React.HTMLAttributes & { agentColor?: boolean; noMobile?: boolean; + elevation?: 'low' | 'high'; footer?: React.ReactNode; }; export default forwardRef(function FloatingModal( - { children, agentColor = false, footer, noMobile = false, ...props }: IProps, + { + children, + agentColor = false, + footer, + noMobile = false, + elevation = 'high', + ...props + }: IProps, ref: React.Ref ) { return ( @@ -19,7 +27,11 @@ export default forwardRef(function FloatingModal( ' ' + (props.className ?? '') + ' ' + - (noMobile ? styles['no-mobile'] : '') + (noMobile ? styles['no-mobile'] : '') + + ' ' + + (elevation === 'low' + ? styles['elevation-low'] + : styles['elevation-high']) } ref={ref} > diff --git a/components-ui/floating-modal/style.module.css b/components-ui/floating-modal/style.module.css index b86f64361..0e14c0731 100644 --- a/components-ui/floating-modal/style.module.css +++ b/components-ui/floating-modal/style.module.css @@ -2,15 +2,13 @@ --gutter-vertical: 1rem; --gutter-horizontal: 1.5rem; background-color: #fff; - box-shadow: 0 10px 35px rgba(0, 0, 0, 0.3); padding: var(--gutter-vertical) var(--gutter-horizontal); border-radius: 0.25rem; - border: 2px solid var(--annuaire-colors-pastelBlue); + border: 1px solid var(--annuaire-colors-pastelBlue); } .footer { - margin: calc(var(--gutter-vertical) * 2) calc(var(--gutter-horizontal) * -1) - calc(var(--gutter-vertical) * -1); + margin: calc(var(--gutter-vertical) * 2) calc(var(--gutter-horizontal) * -1) calc(var(--gutter-vertical) * -1); padding: var(--gutter-vertical) var(--gutter-horizontal); font-size: 0.9rem; background-color: var(--background-alt-grey); @@ -20,9 +18,19 @@ .no-mobile .footer { background-color: transparent; } + .no-mobile.floating-modal { box-shadow: none; border: none; padding: 0; } } + +.elevation-low { + box-shadow: 0 0 15px -5px rgba(0, 0, 0, 0.3); +} + +.elevation-high { + box-shadow: 0 10px 35px rgba(0, 0, 0, 0.3); + +} \ No newline at end of file diff --git a/components/header/header-core/index.tsx b/components/header/header-core/index.tsx index c09a880e2..15602f806 100644 --- a/components/header/header-core/index.tsx +++ b/components/header/header-core/index.tsx @@ -90,7 +90,13 @@ export const HeaderCore: React.FC = ({ ) : null} -
+
+ +
{useSearchBar ? (
diff --git a/components/header/header-core/styles.module.css b/components/header/header-core/styles.module.css index 3e9be1b74..ab449d972 100644 --- a/components/header/header-core/styles.module.css +++ b/components/header/header-core/styles.module.css @@ -13,3 +13,15 @@ margin-top: 0; } } + +.menuMobile { + order: 3; + padding: 1rem; + margin-left: auto; +} + +@media screen and (min-width: 993px) { + .menuMobile { + display: none; + } +} diff --git a/components/header/menu/index.tsx b/components/header/menu/index.tsx index c371e820c..f33962d3f 100644 --- a/components/header/menu/index.tsx +++ b/components/header/menu/index.tsx @@ -1,3 +1,4 @@ +import FloatingModal from '#components-ui/floating-modal'; import { Icon } from '#components-ui/icon/wrapper'; import constants from '#models/constants'; import { isLoggedIn } from '#models/user/rights'; @@ -10,38 +11,53 @@ const Menu: React.FC<{ useAgentCTA: boolean; }> = ({ session, pathFrom, useAgentCTA }) => { return isLoggedIn(session) ? ( -
+
- {session?.user?.fullName || - session?.user?.email || - 'Utilisateur inconnu'} -  ( - - agent public - - ) + + {session?.user?.fullName || + session?.user?.email || + 'Utilisateur inconnu'} +  ( + + agent public + + ) +
- -
Se déconnecter
-
+ +
Se déconnecter
+
+
) : useAgentCTA ? ( - Espace agent public + + Espace agent public + ) : null; }; diff --git a/components/header/menu/styles.module.css b/components/header/menu/styles.module.css index fa934a207..1563b1ae6 100644 --- a/components/header/menu/styles.module.css +++ b/components/header/menu/styles.module.css @@ -1,25 +1,110 @@ .menuLogout { position: relative; + z-index: 11; + cursor: pointer; } + .menuLogout:hover { - background-color: #eee !important; - cursor: default; + background-color: #eee; } -.menuLogout > a { +.dialog { + position: absolute; + padding: 1rem; + min-width: max-content; position: absolute; - top: 100%; - left: 0; - display: none; - width: 100%; - background-color: #fff; - padding: 5px 15px; - box-shadow: 0 10px 15px -10px rgba(0, 0, 0, 0.5); + z-index: 10; + visibility: hidden; + will-change: transform, opacity; } -.menuLogout > a:hover { - background-color: #f8f8f8 !important; + +.menuLogout:hover>.dialog, +.menuLogout:focus-visible>.dialog, +.dialog:focus-visible, +.dialog:hover { + visibility: visible !important; + animation: appear 0.15s ease-in-out forwards; } -.menuLogout:hover > a { +.dialog a { display: block; + margin: -1rem; + padding: 1rem; + color: var(--annuaire-colors-frBlue); +} + +.dialog a:hover { + text-decoration: underline; + } + +.dialog:hover { + background: #f8f8f8; + +} + +@media only screen and (min-width: 1px) and (max-width: 992px) { + .menuText { + display: none; + } + + .dialog { + top: 0.25rem; + right: 0.5rem; + } + + .menuLogout:hover>.dialog, + .menuLogout:focus-visible>.dialog, + .dialog:focus-visible, + .dialog:hover, + .menuLogout:focus>.dialog, + .dialog:focus { + visibility: visible !important; + animation: appear-right 0.1s ease-in-out forwards; + } + +} + +@media only screen and (min-width: 993px) { + .dialog { + right: 0; + left: 0; + top: 100%; + border: none; + + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + .menuLogout:hover>.dialog, + .menuLogout:focus-visible>.dialog, + .dialog:focus-visible, + .dialog:hover { + visibility: visible !important; + animation: appear-top 0.15s ease-in-out forwards; + } +} + +@keyframes appear-top { + from { + opacity: 0; + transform: translateY(-10%); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes appear-right { + from { + opacity: 0; + transform: translateX(10%); + } + + to { + opacity: 1; + transform: translateX(0); + } +} \ No newline at end of file diff --git a/cypress/e2e/espace-agent.cy.js b/cypress/e2e/espace-agent.cy.js index 561cc0fa1..b3a35e3a1 100644 --- a/cypress/e2e/espace-agent.cy.js +++ b/cypress/e2e/espace-agent.cy.js @@ -13,7 +13,11 @@ describe( }); it("Page d'accueil", () => { cy.visit(`/`); - cy.contains('Espace agent').click(); + cy.contains('Espace agent') + // The element is present twice (mobile and desktop menu). + // The mobile one is hidden but appears first in the DOM, + // so we need to force the click + .click({ force: true }); cy.contains('button', 'AgentConnect'); }); From 86e30088e5c911d32edd03d1bf3bde5ae42295fd Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 08:23:56 +0200 Subject: [PATCH 05/14] feat: use next Link component in tab (#1076) should fix a lot of ChunkLoadError in production fix #969 --- components/load-bar/index.tsx | 8 +++++ components/title-section/tabs/TabLink.tsx | 35 +++++++++++++++++++ components/title-section/tabs/index.tsx | 21 ++++++----- .../title-section/tabs/styles.module.css | 14 ++++---- 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 components/title-section/tabs/TabLink.tsx diff --git a/components/load-bar/index.tsx b/components/load-bar/index.tsx index eccc79e8b..e514fc249 100644 --- a/components/load-bar/index.tsx +++ b/components/load-bar/index.tsx @@ -10,6 +10,8 @@ export default function LoadBar({ session }: { session: ISession | null }) { const loadBar = loadBarFactory(); if (typeof window !== 'undefined') { window.addEventListener('beforeunload', loadBar.run); + window.addEventListener('runloadbar', loadBar.run); + window.addEventListener('cancelloadbar', loadBar.cancel); } }, []); return ( @@ -94,5 +96,11 @@ const loadBarFactory = () => { await wait(200); } }, + cancel: function () { + this._currentJobId = ''; + if (this._loader) { + this._loader.style.width = '0'; + } + }, }; }; diff --git a/components/title-section/tabs/TabLink.tsx b/components/title-section/tabs/TabLink.tsx new file mode 100644 index 000000000..7d1318ef3 --- /dev/null +++ b/components/title-section/tabs/TabLink.tsx @@ -0,0 +1,35 @@ +'use client'; +import Link from 'next/link'; +import { useEffect } from 'react'; +import styles from './styles.module.css'; +type IProps = { + label: string; + href: string; + noFollow?: boolean; + width?: string; + active: boolean; +}; +export default function TabLink({ + active, + href, + label, + noFollow, + width, +}: IProps) { + useEffect(() => { + active === false && window.dispatchEvent(new Event('cancelloadbar')); + }, [active]); + return ( + window.dispatchEvent(new Event('runloadbar'))} + prefetch={false} + > + {active ? label :

{label}

} + + ); +} diff --git a/components/title-section/tabs/index.tsx b/components/title-section/tabs/index.tsx index 1e648ddfe..013a660de 100644 --- a/components/title-section/tabs/index.tsx +++ b/components/title-section/tabs/index.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { PrintNever } from '#components-ui/print-visibility'; import { checkHasLabelsAndCertificates, @@ -10,6 +11,7 @@ import { } from '#models/core/types'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; +import TabLink from './TabLink'; import styles from './styles.module.css'; export enum FICHE { @@ -135,30 +137,27 @@ export const Tabs: React.FC<{ noFollow, width = 'auto', }) => ( - - {currentFicheType === ficheType ? label :

{label}

} -
+ width={width} + /> ) )} {currentFicheType === FICHE.ETABLISSEMENT && ( <> diff --git a/components/title-section/tabs/styles.module.css b/components/title-section/tabs/styles.module.css index 65c81c7bb..dd0484798 100644 --- a/components/title-section/tabs/styles.module.css +++ b/components/title-section/tabs/styles.module.css @@ -20,7 +20,8 @@ border-bottom: 0; } } -.titleTabs > a { + +.titleTabs>a { border-top-left-radius: 3px; border-top-right-radius: 3px; border: 2px solid var(--annuaire-colors-pastelBlue); @@ -35,13 +36,13 @@ margin-bottom: -2px; } -.titleTabs > a > h2 { +.titleTabs>a>h2 { margin: 0; padding: 0; } -.titleTabs > a, -.titleTabs > a > h2 { +.titleTabs>a, +.titleTabs>a>h2 { color: var(--annuaire-colors-frBlue); font-weight: bold; font-size: 0.9rem; @@ -56,6 +57,7 @@ } } -.titleTabs > a:hover { +.titleTabs>a:hover, +.titleTabs>a:focus { background-color: var(--annuaire-colors-pastelBlue); -} +} \ No newline at end of file From 527e000c80d52ca827b24179c7e9daba2ea11664 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 12:01:45 +0200 Subject: [PATCH 06/14] chore: move rne document call to server (#1061) --- .../dirigeants/_component/sections/index.tsx | 2 +- .../justificatif/[slug]/_components/index.tsx | 2 +- .../espace-agent/_helper/index.ts | 38 ------------------- .../espace-agent/documents/[slug]/route.ts | 26 ------------- clients/routes.ts | 1 - .../documents/actes-walled.tsx | 27 +++++++------ .../documents/bilans-walled.tsx | 26 +++++++------ .../documents/data-section/actes.tsx | 13 +++---- .../documents/data-section/bilans.tsx | 14 +++---- cypress/e2e/data-fetching.cy.js | 7 ---- hooks/fetch/actes-RNE.ts | 34 ----------------- models/espace-agent/conformite.ts | 2 + models/immatriculation/rne.ts | 20 +++++++++- 13 files changed, 65 insertions(+), 147 deletions(-) delete mode 100644 app/api/data-fetching/espace-agent/_helper/index.ts delete mode 100644 app/api/data-fetching/espace-agent/documents/[slug]/route.ts delete mode 100644 hooks/fetch/actes-RNE.ts diff --git a/app/(header-default)/dirigeants/_component/sections/index.tsx b/app/(header-default)/dirigeants/_component/sections/index.tsx index 6ab6f9994..e5167882b 100644 --- a/app/(header-default)/dirigeants/_component/sections/index.tsx +++ b/app/(header-default)/dirigeants/_component/sections/index.tsx @@ -3,7 +3,7 @@ import { DonneesPriveesSection } from '#components/donnees-privees-section'; import { estDiffusible } from '#models/core/statut-diffusion'; import { IUniteLegale } from '#models/core/types'; import { getMandatairesRCS } from '#models/espace-agent/mandataires-rcs'; -import getImmatriculationRNE from '#models/immatriculation/rne'; +import { getImmatriculationRNE } from '#models/immatriculation/rne'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; import BeneficiairesSection from './beneficiaires'; diff --git a/app/(header-default)/justificatif/[slug]/_components/index.tsx b/app/(header-default)/justificatif/[slug]/_components/index.tsx index 717f863c7..a1d84fa10 100644 --- a/app/(header-default)/justificatif/[slug]/_components/index.tsx +++ b/app/(header-default)/justificatif/[slug]/_components/index.tsx @@ -1,5 +1,5 @@ import { IUniteLegale } from '#models/core/types'; -import getImmatriculationRNE from '#models/immatriculation/rne'; +import { getImmatriculationRNE } from '#models/immatriculation/rne'; import { ISession } from '#models/user/session'; import { IJustificatifs, ImmatriculationsSection } from './container'; diff --git a/app/api/data-fetching/espace-agent/_helper/index.ts b/app/api/data-fetching/espace-agent/_helper/index.ts deleted file mode 100644 index 137eda437..000000000 --- a/app/api/data-fetching/espace-agent/_helper/index.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { HttpForbiddenError } from '#clients/exceptions'; -import { EAdministration } from '#models/administrations/EAdministration'; -import { FetchRessourceException } from '#models/exceptions'; -import { EScope, hasRights } from '#models/user/rights'; -import { logFatalErrorInSentry } from '#utils/sentry'; -import getSession from '#utils/server-side-helper/app/get-session'; - -export async function ProtectedAPIRoute( - routeLabel: string, - slug: string, - administration: EAdministration, - scope: EScope, - run: (agentSiret: string) => Promise -) { - const session = await getSession(); - try { - if (!hasRights(session, scope)) { - throw new HttpForbiddenError('Unauthorized account'); - } - - const agentSiret = session?.user?.siret || 'Inconnu'; - - const data = await run(agentSiret); - return Response.json(data, { status: 200 }); - } catch (e: any) { - const message = `Failed to get donnees ${routeLabel}`; - logFatalErrorInSentry( - new FetchRessourceException({ - ressource: routeLabel, - context: { slug }, - cause: e, - message, - administration, - }) - ); - return Response.json({ message }, { status: e.status || 500 }); - } -} diff --git a/app/api/data-fetching/espace-agent/documents/[slug]/route.ts b/app/api/data-fetching/espace-agent/documents/[slug]/route.ts deleted file mode 100644 index 72e1d2654..000000000 --- a/app/api/data-fetching/espace-agent/documents/[slug]/route.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { clientDocuments } from '#clients/api-proxy/rne/documents'; -import { EAdministration } from '#models/administrations/EAdministration'; -import { EScope } from '#models/user/rights'; -import { verifySiren } from '#utils/helpers'; -import { ProtectedAPIRoute } from '../../_helper'; - -export async function GET( - _request: Request, - { params }: { params: { slug: string } } -) { - const slug = params.slug; - return ProtectedAPIRoute( - 'RNEDocuments', - slug, - EAdministration.INPI, - EScope.documentsRne, - async () => { - const siren = verifySiren(slug as string); - - const actes = await clientDocuments(siren); - actes.hasBilanConsolide = - actes.bilans.filter((b) => b.typeBilan === 'K').length > 0; - return actes; - } - ); -} diff --git a/clients/routes.ts b/clients/routes.ts index 68218d861..daa38f5fa 100644 --- a/clients/routes.ts +++ b/clients/routes.ts @@ -2,7 +2,6 @@ const routes = { api: { rne: { documents: { - list: '/api/data-fetching/espace-agent/documents/', download: '/api/download/espace-agent/documents/', }, }, diff --git a/components/espace-agent-components/documents/actes-walled.tsx b/components/espace-agent-components/documents/actes-walled.tsx index 7c16dc047..52611a138 100644 --- a/components/espace-agent-components/documents/actes-walled.tsx +++ b/components/espace-agent-components/documents/actes-walled.tsx @@ -1,4 +1,5 @@ import { IUniteLegale } from '#models/core/types'; +import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; import AgentWallDocuments from '../agent-wall/document'; @@ -7,16 +8,18 @@ import { AgentActesSection } from './data-section/actes'; const ActesSection: React.FC<{ uniteLegale: IUniteLegale; session: ISession | null; -}> = ({ uniteLegale, session }) => - !hasRights(session, EScope.actesRne) ? ( - - ) : ( - - ); - +}> = async ({ uniteLegale, session }) => { + if (!hasRights(session, EScope.actesRne)) { + return ( + + ); + } + const documents = getDocumentsRNEProtected(uniteLegale.siren); + return ; +}; export default ActesSection; diff --git a/components/espace-agent-components/documents/bilans-walled.tsx b/components/espace-agent-components/documents/bilans-walled.tsx index 362fbbb24..cd91210f7 100644 --- a/components/espace-agent-components/documents/bilans-walled.tsx +++ b/components/espace-agent-components/documents/bilans-walled.tsx @@ -1,4 +1,5 @@ import { IUniteLegale } from '#models/core/types'; +import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; import AgentWallDocuments from '../agent-wall/document'; @@ -7,16 +8,19 @@ import AgentBilansSection from './data-section/bilans'; const BilansSection: React.FC<{ uniteLegale: IUniteLegale; session: ISession | null; -}> = ({ uniteLegale, session }) => - !hasRights(session, EScope.bilansRne) ? ( - - ) : ( - - ); +}> = async ({ uniteLegale, session }) => { + if (!hasRights(session, EScope.bilansRne)) { + return ( + + ); + } + const documents = getDocumentsRNEProtected(uniteLegale.siren); + return ; +}; export default BilansSection; diff --git a/components/espace-agent-components/documents/data-section/actes.tsx b/components/espace-agent-components/documents/data-section/actes.tsx index 48e725daf..563da177a 100644 --- a/components/espace-agent-components/documents/data-section/actes.tsx +++ b/components/espace-agent-components/documents/data-section/actes.tsx @@ -5,9 +5,10 @@ import { Warning } from '#components-ui/alerts'; import ButtonLink from '#components-ui/button'; import { PrintNever } from '#components-ui/print-visibility'; import ShowMore from '#components-ui/show-more'; -import { AsyncDataSectionClient } from '#components/section/data-section/client'; +import { AsyncDataSectionServer } from '#components/section/data-section/server'; import { FullTable } from '#components/table/full'; import { EAdministration } from '#models/administrations/EAdministration'; +import { IAPINotRespondingError } from '#models/api-not-responding'; import { IUniteLegale, isAssociation, @@ -15,7 +16,6 @@ import { } from '#models/core/types'; import { IActesRNE } from '#models/immatriculation'; import { formatDateLong } from '#utils/helpers'; -import useFetchActesRNE from 'hooks/fetch/actes-RNE'; const NoDocument = () => ( <>Aucun document n’a été retrouvé dans le RNE pour cette entreprise. @@ -23,12 +23,11 @@ const NoDocument = () => ( export const AgentActesSection: React.FC<{ uniteLegale: IUniteLegale; -}> = ({ uniteLegale }) => { - const documents = useFetchActesRNE(uniteLegale); - + documents: Promise; +}> = ({ uniteLegale, documents }) => { return ( - ) } - + ); }; diff --git a/components/espace-agent-components/documents/data-section/bilans.tsx b/components/espace-agent-components/documents/data-section/bilans.tsx index 4bb899923..6b29ad78d 100644 --- a/components/espace-agent-components/documents/data-section/bilans.tsx +++ b/components/espace-agent-components/documents/data-section/bilans.tsx @@ -6,17 +6,18 @@ import ButtonLink from '#components-ui/button'; import FAQLink from '#components-ui/faq-link'; import { PrintNever } from '#components-ui/print-visibility'; import { Tag } from '#components-ui/tag'; -import { AsyncDataSectionClient } from '#components/section/data-section/client'; +import { AsyncDataSectionServer } from '#components/section/data-section/server'; import { FullTable } from '#components/table/full'; import { EAdministration } from '#models/administrations/EAdministration'; +import { IAPINotRespondingError } from '#models/api-not-responding'; import { IUniteLegale, isAssociation, isServicePublic, } from '#models/core/types'; +import { IActesRNE } from '#models/immatriculation'; import { formatDateLong } from '#utils/helpers'; import { getFiscalYear } from '#utils/helpers/formatting/format-fiscal-year'; -import useFetchActesRNE from 'hooks/fetch/actes-RNE'; const NoBilans = () => ( <>Aucun comptes n’a été déposé au RNE pour cette entreprise. @@ -24,12 +25,11 @@ const NoBilans = () => ( const AgentBilansSection: React.FC<{ uniteLegale: IUniteLegale; -}> = ({ uniteLegale }) => { - const documents = useFetchActesRNE(uniteLegale); - + documents: Promise; +}> = ({ uniteLegale, documents }) => { return ( - ) } - + ); }; diff --git a/cypress/e2e/data-fetching.cy.js b/cypress/e2e/data-fetching.cy.js index 588a00c6f..a3e248fc7 100644 --- a/cypress/e2e/data-fetching.cy.js +++ b/cypress/e2e/data-fetching.cy.js @@ -1,12 +1,5 @@ describe('Data fetching routes', () => { it('Agent-only routes are forbidden', () => { - cy.request({ - url: '/api/data-fetching/espace-agent/documents/552032534', - failOnStatusCode: false, - }).then((resp) => { - expect(resp.status).to.eq(403); - }); - cy.request({ url: '/api/download/espace-agent/documents/552032534', failOnStatusCode: false, diff --git a/hooks/fetch/actes-RNE.ts b/hooks/fetch/actes-RNE.ts deleted file mode 100644 index 9c78bd78f..000000000 --- a/hooks/fetch/actes-RNE.ts +++ /dev/null @@ -1,34 +0,0 @@ -import routes from '#clients/routes'; -import { EAdministration } from '#models/administrations/EAdministration'; -import { FetchRessourceException } from '#models/exceptions'; -import { IActesRNE } from '#models/immatriculation'; -import { IUniteLegale } from '#models/core/types'; -import { httpGet } from '#utils/network'; -import logErrorInSentry from '#utils/sentry'; -import { useFetchData } from './use-fetch-data'; - -export function useFetchActesRNE(uniteLegale: IUniteLegale) { - const { siren } = uniteLegale; - return useFetchData( - { - fetchData: () => - httpGet(routes.api.rne.documents.list + siren), - administration: EAdministration.INPI, - logError: (e: any) => { - if (e.status) { - return; - } - logErrorInSentry( - new FetchRessourceException({ - ressource: 'ActesRNE', - administration: EAdministration.INPI, - cause: e, - }) - ); - }, - }, - [siren] - ); -} - -export default useFetchActesRNE; diff --git a/models/espace-agent/conformite.ts b/models/espace-agent/conformite.ts index 3aec15620..91297770c 100644 --- a/models/espace-agent/conformite.ts +++ b/models/espace-agent/conformite.ts @@ -22,6 +22,8 @@ export const getConformiteEntreprise = async ( siret: Siret, recipientSiret?: string ): Promise => { + await new Promise((resolve) => setTimeout(resolve, 20000)); + const [fiscale, vigilance, msa] = await Promise.all([ clientApiEntrepriseConformiteFiscale(siren, recipientSiret).catch((error) => handleApiEntrepriseError(error, { siren, siret }) diff --git a/models/immatriculation/rne.ts b/models/immatriculation/rne.ts index db170994e..e1d3f51eb 100644 --- a/models/immatriculation/rne.ts +++ b/models/immatriculation/rne.ts @@ -1,4 +1,5 @@ import { fetchRNEImmatriculation } from '#clients/api-proxy/rne'; +import { clientDocuments } from '#clients/api-proxy/rne/documents'; import { HttpNotFound } from '#clients/exceptions'; import routes from '#clients/routes'; import { EAdministration } from '#models/administrations/EAdministration'; @@ -13,7 +14,7 @@ import { IImmatriculationRNE } from '.'; * Request Immatriculation from INPI's RNE * @param siren */ -const getImmatriculationRNE = async ( +export const getImmatriculationRNE = async ( siren: Siren ): Promise => { try { @@ -47,4 +48,19 @@ const getImmatriculationRNE = async ( } }; -export default getImmatriculationRNE; +export async function getDocumentsRNEProtected(siren: Siren) { + try { + const actes = await clientDocuments(siren); + actes.hasBilanConsolide = + actes.bilans.filter((b) => b.typeBilan === 'K').length > 0; + return actes; + } catch (e: any) { + if (e instanceof HttpNotFound) { + return APINotRespondingFactory(EAdministration.INPI, 404); + } + + // no need to log an error as API-Proxy already logged it + + return APINotRespondingFactory(EAdministration.INPI, 500); + } +} From 382a075346c5d621b3d5e670dfa5522bbf7e84d7 Mon Sep 17 00:00:00 2001 From: Xavier Jp Date: Wed, 29 May 2024 12:20:33 +0200 Subject: [PATCH 07/14] Revert "feat: use next Link component in tab" (#1080) This reverts commit 86e30088e5c911d32edd03d1bf3bde5ae42295fd. --- components/load-bar/index.tsx | 8 ----- components/title-section/tabs/TabLink.tsx | 35 ------------------- components/title-section/tabs/index.tsx | 21 +++++------ .../title-section/tabs/styles.module.css | 14 ++++---- 4 files changed, 17 insertions(+), 61 deletions(-) delete mode 100644 components/title-section/tabs/TabLink.tsx diff --git a/components/load-bar/index.tsx b/components/load-bar/index.tsx index e514fc249..eccc79e8b 100644 --- a/components/load-bar/index.tsx +++ b/components/load-bar/index.tsx @@ -10,8 +10,6 @@ export default function LoadBar({ session }: { session: ISession | null }) { const loadBar = loadBarFactory(); if (typeof window !== 'undefined') { window.addEventListener('beforeunload', loadBar.run); - window.addEventListener('runloadbar', loadBar.run); - window.addEventListener('cancelloadbar', loadBar.cancel); } }, []); return ( @@ -96,11 +94,5 @@ const loadBarFactory = () => { await wait(200); } }, - cancel: function () { - this._currentJobId = ''; - if (this._loader) { - this._loader.style.width = '0'; - } - }, }; }; diff --git a/components/title-section/tabs/TabLink.tsx b/components/title-section/tabs/TabLink.tsx deleted file mode 100644 index 7d1318ef3..000000000 --- a/components/title-section/tabs/TabLink.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; -import Link from 'next/link'; -import { useEffect } from 'react'; -import styles from './styles.module.css'; -type IProps = { - label: string; - href: string; - noFollow?: boolean; - width?: string; - active: boolean; -}; -export default function TabLink({ - active, - href, - label, - noFollow, - width, -}: IProps) { - useEffect(() => { - active === false && window.dispatchEvent(new Event('cancelloadbar')); - }, [active]); - return ( - window.dispatchEvent(new Event('runloadbar'))} - prefetch={false} - > - {active ? label :

{label}

} - - ); -} diff --git a/components/title-section/tabs/index.tsx b/components/title-section/tabs/index.tsx index 013a660de..1e648ddfe 100644 --- a/components/title-section/tabs/index.tsx +++ b/components/title-section/tabs/index.tsx @@ -1,4 +1,3 @@ -import Link from 'next/link'; import { PrintNever } from '#components-ui/print-visibility'; import { checkHasLabelsAndCertificates, @@ -11,7 +10,6 @@ import { } from '#models/core/types'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; -import TabLink from './TabLink'; import styles from './styles.module.css'; export enum FICHE { @@ -137,27 +135,30 @@ export const Tabs: React.FC<{ noFollow, width = 'auto', }) => ( - + style={{ width }} + > + {currentFicheType === ficheType ? label :

{label}

} + ) )} {currentFicheType === FICHE.ETABLISSEMENT && ( <>
-

Fiche Ă©tablissement

- + )}
diff --git a/components/title-section/tabs/styles.module.css b/components/title-section/tabs/styles.module.css index dd0484798..65c81c7bb 100644 --- a/components/title-section/tabs/styles.module.css +++ b/components/title-section/tabs/styles.module.css @@ -20,8 +20,7 @@ border-bottom: 0; } } - -.titleTabs>a { +.titleTabs > a { border-top-left-radius: 3px; border-top-right-radius: 3px; border: 2px solid var(--annuaire-colors-pastelBlue); @@ -36,13 +35,13 @@ margin-bottom: -2px; } -.titleTabs>a>h2 { +.titleTabs > a > h2 { margin: 0; padding: 0; } -.titleTabs>a, -.titleTabs>a>h2 { +.titleTabs > a, +.titleTabs > a > h2 { color: var(--annuaire-colors-frBlue); font-weight: bold; font-size: 0.9rem; @@ -57,7 +56,6 @@ } } -.titleTabs>a:hover, -.titleTabs>a:focus { +.titleTabs > a:hover { background-color: var(--annuaire-colors-pastelBlue); -} \ No newline at end of file +} From 0eb844757a29db6e5b85c551a08afe5fc736cf2a Mon Sep 17 00:00:00 2001 From: Xavier Jp Date: Wed, 29 May 2024 15:33:58 +0200 Subject: [PATCH 08/14] fix: only rely on DGEFP for qualiopi certification (#1082) * fix: only rely on DGEFP for qualiopi certification * refactor: use different variable for DGEFP response and recherche * fix: build * fix: test e2e --- .../labels-certificats/[slug]/page.tsx | 1 - .../open-data-soft/clients/qualiopi/index.ts | 3 + .../organismes-de-formation/index.tsx | 132 +++++++++--------- cypress/e2e/certifications.cy.js | 3 +- .../certifications/organismes-de-formation.ts | 1 + 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/app/(header-default)/labels-certificats/[slug]/page.tsx b/app/(header-default)/labels-certificats/[slug]/page.tsx index e5c3551a6..8c23a9b08 100644 --- a/app/(header-default)/labels-certificats/[slug]/page.tsx +++ b/app/(header-default)/labels-certificats/[slug]/page.tsx @@ -104,7 +104,6 @@ const LabelsAndCertificatsPage = async (props: AppRouterProps) => { {estOrganismeFormation && ( )} {egaproRenseignee && } diff --git a/clients/open-data-soft/clients/qualiopi/index.ts b/clients/open-data-soft/clients/qualiopi/index.ts index eed190848..b335ead4c 100644 --- a/clients/open-data-soft/clients/qualiopi/index.ts +++ b/clients/open-data-soft/clients/qualiopi/index.ts @@ -23,6 +23,9 @@ const clientOrganismeFormation = async ( const fields = response.records as IOrganismesFormationRecord[]; return { records: fields.map(mapToDomainObject), + qualiopiCertified: !!fields.find( + (f) => (f?.certifications || []).length > 0 + ), lastModified: response.lastModified, }; }; diff --git a/components/labels-and-certificates/organismes-de-formation/index.tsx b/components/labels-and-certificates/organismes-de-formation/index.tsx index 0bb24ad65..ffdb78fc5 100644 --- a/components/labels-and-certificates/organismes-de-formation/index.tsx +++ b/components/labels-and-certificates/organismes-de-formation/index.tsx @@ -6,84 +6,84 @@ import { FullTable } from '#components/table/full'; import { EAdministration } from '#models/administrations/EAdministration'; import { IAPINotRespondingError } from '#models/api-not-responding'; import { IOrganismeFormation } from '#models/certifications/organismes-de-formation'; -import { IUniteLegale } from '#models/core/types'; type OrganismeDeFormationSectionProps = { organismesDeFormation: IOrganismeFormation | IAPINotRespondingError; - uniteLegale: IUniteLegale; }; export const OrganismeDeFormationSection = ({ organismesDeFormation, - uniteLegale, }: OrganismeDeFormationSectionProps) => { - const estQualiopi = uniteLegale.complements.estQualiopi; - - const head = [ - 'Numéro Déclaration Activité (NDA)', - 'Détails', - ...(estQualiopi ? ['Certification(s) Qualiopi'] : []), - ]; - - const title = `Organisme de formation${ - estQualiopi ? ' certifié Qualiopi' : '' - }`; - return ( } + // if 404 from DGEFP we can assume this organism is NOT qualiopi + notFoundInfo={} > - {(organismesDeFormation) => ( - <> - - [ - {fields.nda ? fields.nda : 'Inconnu'}, - <> - {fields.specialite && ( - <> - Spécialité : {fields.specialite} -
- - )} - {fields.formateurs && ( - <> - Effectifs formateurs : {fields.formateurs} -
- - )} - {fields.stagiaires && ( - <> - Effectifs stagiaires : {fields.stagiaires} -
- - )} - {fields.dateDeclaration && ( - <> - Déclaration : le {fields.dateDeclaration} - {fields.region && <>, en région {fields.region}} -
- - )} - , - ...(estQualiopi - ? [ - fields.certifications.map((certification) => ( - - {certification} - - )), - ] - : []), - ])} - />{' '} - - )} + {(organismesDeFormation) => { + // we use definition from DGEFP rather than recherche entreprise which can be outdated + const qualiopiCertified = organismesDeFormation.qualiopiCertified; + + const head = [ + 'Numéro Déclaration Activité (NDA)', + 'Détails', + ...(qualiopiCertified ? ['Certification(s) Qualiopi'] : []), + ]; + + return ( + <> + + [ + {fields.nda ? fields.nda : 'Inconnu'}, + <> + {fields.specialite && ( + <> + Spécialité : {fields.specialite} +
+ + )} + {fields.formateurs && ( + <> + Effectifs formateurs :{' '} + {fields.formateurs} +
+ + )} + {fields.stagiaires && ( + <> + Effectifs stagiaires :{' '} + {fields.stagiaires} +
+ + )} + {fields.dateDeclaration && ( + <> + Déclaration : le{' '} + {fields.dateDeclaration} + {fields.region && <>, en région {fields.region}} +
+ + )} + , + ...(qualiopiCertified + ? [ + fields.certifications.map((certification) => ( + + {certification} + + )), + ] + : []), + ])} + />{' '} + + ); + }}
); }; @@ -98,7 +98,7 @@ const FAQQaliopi = () => ( ); -const OrganismeFormationLabel = ({ estQualiopi = false }) => ( +const OrganismeFormationLabel = ({ qualiopiCertified = false }) => ( <> Cette structure est un organisme de formation,{' '} @@ -107,7 +107,7 @@ const OrganismeFormationLabel = ({ estQualiopi = false }) => ( PĂ©dagogique et Financier. . - {estQualiopi ? ( + {qualiopiCertified ? ( <>

diff --git a/cypress/e2e/certifications.cy.js b/cypress/e2e/certifications.cy.js index 06ccc45d0..c0a14bd05 100644 --- a/cypress/e2e/certifications.cy.js +++ b/cypress/e2e/certifications.cy.js @@ -33,7 +33,8 @@ describe('Certifications', () => { describe('Qualiopi', () => { it('Should display Qualiopi', () => { cy.visit(`/labels-certificats/356000000`); - cy.contains('Organisme de formation certifié Qualiopi'); + cy.contains('Organisme de formation'); + cy.contains('certifiée Qualiopi'); cy.contains('Numéro Déclaration Activité'); cy.contains('11755565775'); }); diff --git a/models/certifications/organismes-de-formation.ts b/models/certifications/organismes-de-formation.ts index 4b41f6af8..018d8a278 100644 --- a/models/certifications/organismes-de-formation.ts +++ b/models/certifications/organismes-de-formation.ts @@ -20,6 +20,7 @@ export type IOrganismeFormation = { dateDeclaration: string | null; region: string | null; }[]; + qualiopiCertified: boolean; lastModified: string | null; }; From 12aedc4b29f0c4437d4a18f118e64083e0d98477 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 17:14:19 +0200 Subject: [PATCH 09/14] =?UTF-8?q?R=C3=A9tabli=20les=20link=20et=20utilise?= =?UTF-8?q?=20les=20routes=20parall=C3=A8les=20pour=20/documents=20(#1083)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: use next Link component in tab (#1076) should fix a lot of ChunkLoadError in production fix #969 * chore: use parallel route for documents instead react async component wrapped in suspense --- .../documents/[slug]/@actes/common.tsx | 8 ++ .../documents/[slug]/@actes/layout.tsx | 27 +++++ .../documents/[slug]/@actes/loading.tsx | 11 ++ .../documents/[slug]/@actes/page.tsx | 95 ++++++++++++++++ .../[slug]/@carteProfessionnelleTP/layout.tsx | 14 +++ .../@carteProfessionnelleTP/loading.tsx | 3 + .../[slug]/@carteProfessionnelleTP/page.tsx | 29 +++++ .../documents/[slug]/@conformite/common.tsx | 8 ++ .../documents/[slug]/@conformite/layout.tsx | 20 ++++ .../documents/[slug]/@conformite/loading.tsx | 11 ++ .../documents/[slug]/@conformite/page.tsx | 46 ++++++++ .../documents/[slug]/{page.tsx => layout.tsx} | 43 ++----- .../agent-wall/document.tsx | 1 - .../conformite-section.tsx | 57 ---------- .../documents/actes-walled.tsx | 25 ----- .../documents/bilans-walled.tsx | 1 - .../documents/data-section/actes.tsx | 106 ------------------ components/load-bar/index.tsx | 8 ++ components/title-section/tabs/TabLink.tsx | 35 ++++++ components/title-section/tabs/index.tsx | 21 ++-- .../title-section/tabs/styles.module.css | 14 ++- 21 files changed, 344 insertions(+), 239 deletions(-) create mode 100644 app/(header-default)/documents/[slug]/@actes/common.tsx create mode 100644 app/(header-default)/documents/[slug]/@actes/layout.tsx create mode 100644 app/(header-default)/documents/[slug]/@actes/loading.tsx create mode 100644 app/(header-default)/documents/[slug]/@actes/page.tsx create mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx create mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx create mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx create mode 100644 app/(header-default)/documents/[slug]/@conformite/common.tsx create mode 100644 app/(header-default)/documents/[slug]/@conformite/layout.tsx create mode 100644 app/(header-default)/documents/[slug]/@conformite/loading.tsx create mode 100644 app/(header-default)/documents/[slug]/@conformite/page.tsx rename app/(header-default)/documents/[slug]/{page.tsx => layout.tsx} (50%) delete mode 100644 components/espace-agent-components/conformite-section.tsx delete mode 100644 components/espace-agent-components/documents/actes-walled.tsx delete mode 100644 components/espace-agent-components/documents/data-section/actes.tsx create mode 100644 components/title-section/tabs/TabLink.tsx diff --git a/app/(header-default)/documents/[slug]/@actes/common.tsx b/app/(header-default)/documents/[slug]/@actes/common.tsx new file mode 100644 index 000000000..e14b25ab9 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@actes/common.tsx @@ -0,0 +1,8 @@ +import { EAdministration } from '#models/administrations/EAdministration'; + +export const sectionInfo = { + title: 'Actes et statuts', + id: 'actes', + isProtected: true, + sources: [EAdministration.INPI], +}; diff --git a/app/(header-default)/documents/[slug]/@actes/layout.tsx b/app/(header-default)/documents/[slug]/@actes/layout.tsx new file mode 100644 index 000000000..dfa5cff19 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@actes/layout.tsx @@ -0,0 +1,27 @@ +import { PropsWithChildren } from 'react'; +import { PrintNever } from '#components-ui/print-visibility'; +import AgentWallDocuments from '#components/espace-agent-components/agent-wall/document'; +import { EScope, hasRights } from '#models/user/rights'; +import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; +import extractParamsAppRouter, { + AppRouterProps, +} from '#utils/server-side-helper/app/extract-params'; +import getSession from '#utils/server-side-helper/app/get-session'; + +export default async function Layout(props: PropsWithChildren) { + const session = await getSession(); + const { slug, isBot } = extractParamsAppRouter(props); + const uniteLegale = await cachedGetUniteLegale(slug, isBot); + + if (!hasRights(session, EScope.actesRne)) { + return ( + + ); + } + + return {props.children}; +} diff --git a/app/(header-default)/documents/[slug]/@actes/loading.tsx b/app/(header-default)/documents/[slug]/@actes/loading.tsx new file mode 100644 index 000000000..10b7f3a94 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@actes/loading.tsx @@ -0,0 +1,11 @@ +import { Section } from '#components/section'; +import { DataSectionLoader } from '#components/section/data-section/loader'; +import { sectionInfo } from './common'; + +export default function Loader() { + return ( +
+ +
+ ); +} diff --git a/app/(header-default)/documents/[slug]/@actes/page.tsx b/app/(header-default)/documents/[slug]/@actes/page.tsx new file mode 100644 index 000000000..b5fe324b8 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@actes/page.tsx @@ -0,0 +1,95 @@ +import routes from '#clients/routes'; +import { Warning } from '#components-ui/alerts'; +import ButtonLink from '#components-ui/button'; +import ShowMore from '#components-ui/show-more'; +import { DataSection } from '#components/section/data-section'; +import { FullTable } from '#components/table/full'; +import { isAssociation, isServicePublic } from '#models/core/types'; +import { IActesRNE } from '#models/immatriculation'; +import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; +import { formatDateLong } from '#utils/helpers'; +import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; +import extractParamsAppRouter, { + AppRouterProps, +} from '#utils/server-side-helper/app/extract-params'; +import { sectionInfo } from './common'; + +const NoDocument = () => ( + <>Aucun document n’a été retrouvé dans le RNE pour cette entreprise. +); + +export default async function AgentActesSection(props: AppRouterProps) { + const { slug, isBot } = extractParamsAppRouter(props); + const uniteLegale = await cachedGetUniteLegale(slug, isBot); + const documents = await getDocumentsRNEProtected(uniteLegale.siren); + + return ( + + {(isAssociation(uniteLegale) || isServicePublic(uniteLegale)) && ( + <> + + Les associations et les services publics ne sont pas + immatriculés au RNE. + +
+ + )} + + + } + > + {(documents) => ( + <> +

+ Cette entreprise possède {documents.actes.length} document(s) au + RNE. Chaque document peut contenir un ou plusieurs actes : +

+ {documents.actes.length >= 5 ? ( + + + + ) : ( + + )} + + )} +
+ ); +} + +type IActesTableProps = { + actes: IActesRNE['actes']; +}; +function ActesTable({ actes }: IActesTableProps) { + return ( + [ + formatDateLong(a.dateDepot), + a.actes && ( +
    + {(a?.actes || []).map((acteName) => ( +
  • {acteName}
  • + ))} +
+ ), + + Télécharger + , + ])} + /> + ); +} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx new file mode 100644 index 000000000..9c129dcda --- /dev/null +++ b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx @@ -0,0 +1,14 @@ +import { PropsWithChildren } from 'react'; +import { PrintNever } from '#components-ui/print-visibility'; +import { EScope, hasRights } from '#models/user/rights'; +import getSession from '#utils/server-side-helper/app/get-session'; + +export default async function Layout(props: PropsWithChildren) { + const session = await getSession(); + + if (!hasRights(session, EScope.carteProfessionnelleTravauxPublics)) { + return null; + } + + return {props.children}; +} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx new file mode 100644 index 000000000..351081a7f --- /dev/null +++ b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx @@ -0,0 +1,3 @@ +export default function Loader() { + return null; +} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx new file mode 100644 index 000000000..7c7fa6875 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx @@ -0,0 +1,29 @@ +import { CarteProfessionnelleTravauxPublicsSection } from '#components/carte-professionnelle-travaux-publics-section'; +import { isAPI404 } from '#models/api-not-responding'; +import { getCarteProfessionnelleTravauxPublic } from '#models/espace-agent/carte-professionnelle-travaux-publics'; +import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; +import extractParamsAppRouter, { + AppRouterProps, +} from '#utils/server-side-helper/app/extract-params'; +import getSession from '#utils/server-side-helper/app/get-session'; + +export default async function Page(props: AppRouterProps) { + const session = await getSession(); + const { slug, isBot } = extractParamsAppRouter(props); + const uniteLegale = await cachedGetUniteLegale(slug, isBot); + + const carteProfessionnelleTravauxPublics = + await getCarteProfessionnelleTravauxPublic( + uniteLegale.siren, + session?.user + ); + if (isAPI404(carteProfessionnelleTravauxPublics)) { + return null; + } + + return ( + + ); +} diff --git a/app/(header-default)/documents/[slug]/@conformite/common.tsx b/app/(header-default)/documents/[slug]/@conformite/common.tsx new file mode 100644 index 000000000..b47a84d1a --- /dev/null +++ b/app/(header-default)/documents/[slug]/@conformite/common.tsx @@ -0,0 +1,8 @@ +import { EAdministration } from '#models/administrations/EAdministration'; + +export const sectionInfo = { + title: 'Conformité', + id: 'conformite', + isProtected: true, + sources: [EAdministration.DGFIP, EAdministration.URSSAF, EAdministration.MSA], +}; diff --git a/app/(header-default)/documents/[slug]/@conformite/layout.tsx b/app/(header-default)/documents/[slug]/@conformite/layout.tsx new file mode 100644 index 000000000..5aa5c6528 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@conformite/layout.tsx @@ -0,0 +1,20 @@ +import { PropsWithChildren } from 'react'; +import { HorizontalSeparator } from '#components-ui/horizontal-separator'; +import { PrintNever } from '#components-ui/print-visibility'; +import { EScope, hasRights } from '#models/user/rights'; +import getSession from '#utils/server-side-helper/app/get-session'; + +export default async function Layout(props: PropsWithChildren) { + const session = await getSession(); + + if (!hasRights(session, EScope.conformite)) { + return null; + } + + return ( + + {props.children} + + + ); +} diff --git a/app/(header-default)/documents/[slug]/@conformite/loading.tsx b/app/(header-default)/documents/[slug]/@conformite/loading.tsx new file mode 100644 index 000000000..10b7f3a94 --- /dev/null +++ b/app/(header-default)/documents/[slug]/@conformite/loading.tsx @@ -0,0 +1,11 @@ +import { Section } from '#components/section'; +import { DataSectionLoader } from '#components/section/data-section/loader'; +import { sectionInfo } from './common'; + +export default function Loader() { + return ( +
+ +
+ ); +} diff --git a/app/(header-default)/documents/[slug]/@conformite/page.tsx b/app/(header-default)/documents/[slug]/@conformite/page.tsx new file mode 100644 index 000000000..83bc77c1f --- /dev/null +++ b/app/(header-default)/documents/[slug]/@conformite/page.tsx @@ -0,0 +1,46 @@ +import Conformite from '#components/espace-agent-components/conformite'; +import { DataSection } from '#components/section/data-section'; +import { TwoColumnTable } from '#components/table/simple'; +import { getConformiteEntreprise } from '#models/espace-agent/conformite'; +import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; +import extractParamsAppRouter, { + AppRouterProps, +} from '#utils/server-side-helper/app/extract-params'; +import getSession from '#utils/server-side-helper/app/get-session'; +import { sectionInfo } from './common'; + +async function ConformiteSection(props: AppRouterProps) { + const { slug, isBot } = extractParamsAppRouter(props); + const uniteLegale = await cachedGetUniteLegale(slug, isBot); + const session = await getSession(); + const conformite = await getConformiteEntreprise( + uniteLegale.siren, + uniteLegale.siege.siret, + session?.user?.siret + ); + + return ( + + {(conformite) => ( + ], + [ + 'Conformité sociale', + <> + +
+ + , + ], + ]} + /> + )} +
+ ); +} + +export default ConformiteSection; diff --git a/app/(header-default)/documents/[slug]/page.tsx b/app/(header-default)/documents/[slug]/layout.tsx similarity index 50% rename from app/(header-default)/documents/[slug]/page.tsx rename to app/(header-default)/documents/[slug]/layout.tsx index 3313a4c1a..6f27aad15 100644 --- a/app/(header-default)/documents/[slug]/page.tsx +++ b/app/(header-default)/documents/[slug]/layout.tsx @@ -1,13 +1,6 @@ import { Metadata } from 'next'; -import { HorizontalSeparator } from '#components-ui/horizontal-separator'; -import { CarteProfessionnelleTravauxPublicsSection } from '#components/carte-professionnelle-travaux-publics-section'; -import ConformiteSection from '#components/espace-agent-components/conformite-section'; -import ActesSection from '#components/espace-agent-components/documents/actes-walled'; import Title from '#components/title-section'; import { FICHE } from '#components/title-section/tabs'; -import { isAPI404 } from '#models/api-not-responding'; -import { getCarteProfessionnelleTravauxPublic } from '#models/espace-agent/carte-professionnelle-travaux-publics'; -import { EScope, hasRights } from '#models/user/rights'; import { uniteLegalePageDescription, uniteLegalePageTitle, @@ -41,20 +34,17 @@ export const generateMetadata = async ( }; }; -const UniteLegaleDocumentPage = async (props: AppRouterProps) => { +const UniteLegaleDocumentPage = async ( + props: AppRouterProps & { + conformite: React.ReactNode; + carteProfessionnelleTP: React.ReactNode; + actes: React.ReactNode; + } +) => { const session = await getSession(); const { slug, isBot } = extractParamsAppRouter(props); const uniteLegale = await cachedGetUniteLegale(slug, isBot); - const carteProfessionnelleTravauxPublics = hasRights( - session, - EScope.carteProfessionnelleTravauxPublics - ) - ? await getCarteProfessionnelleTravauxPublic( - uniteLegale.siren, - session?.user - ) - : null; return ( <>
@@ -63,21 +53,10 @@ const UniteLegaleDocumentPage = async (props: AppRouterProps) => { ficheType={FICHE.DOCUMENTS} session={session} /> - {hasRights(session, EScope.conformite) && ( - <> - - - - )} - - {carteProfessionnelleTravauxPublics && - !isAPI404(carteProfessionnelleTravauxPublics) && ( - - )} + + {props.conformite} + {props.actes} + {props.carteProfessionnelleTP}
); diff --git a/components/espace-agent-components/agent-wall/document.tsx b/components/espace-agent-components/agent-wall/document.tsx index b4738515a..d3ae21b0b 100644 --- a/components/espace-agent-components/agent-wall/document.tsx +++ b/components/espace-agent-components/agent-wall/document.tsx @@ -8,7 +8,6 @@ const AgentWallDocuments: React.FC< title: string; id: string; uniteLegale: IUniteLegale; - condition: boolean; }> > = ({ uniteLegale, id, title }) => ( - - {(conformite) => ( - ], - [ - 'Conformité sociale', - <> - -
- - , - ], - ]} - /> - )} -
- - ); -} - -export default ConformiteSection; diff --git a/components/espace-agent-components/documents/actes-walled.tsx b/components/espace-agent-components/documents/actes-walled.tsx deleted file mode 100644 index 52611a138..000000000 --- a/components/espace-agent-components/documents/actes-walled.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { IUniteLegale } from '#models/core/types'; -import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; -import { EScope, hasRights } from '#models/user/rights'; -import { ISession } from '#models/user/session'; -import AgentWallDocuments from '../agent-wall/document'; -import { AgentActesSection } from './data-section/actes'; - -const ActesSection: React.FC<{ - uniteLegale: IUniteLegale; - session: ISession | null; -}> = async ({ uniteLegale, session }) => { - if (!hasRights(session, EScope.actesRne)) { - return ( - - ); - } - const documents = getDocumentsRNEProtected(uniteLegale.siren); - return ; -}; -export default ActesSection; diff --git a/components/espace-agent-components/documents/bilans-walled.tsx b/components/espace-agent-components/documents/bilans-walled.tsx index cd91210f7..9ac295dd8 100644 --- a/components/espace-agent-components/documents/bilans-walled.tsx +++ b/components/espace-agent-components/documents/bilans-walled.tsx @@ -15,7 +15,6 @@ const BilansSection: React.FC<{ title="Bilans" id="bilans" uniteLegale={uniteLegale} - condition={!hasRights(session, EScope.bilansRne)} /> ); } diff --git a/components/espace-agent-components/documents/data-section/actes.tsx b/components/espace-agent-components/documents/data-section/actes.tsx deleted file mode 100644 index 563da177a..000000000 --- a/components/espace-agent-components/documents/data-section/actes.tsx +++ /dev/null @@ -1,106 +0,0 @@ -'use client'; - -import routes from '#clients/routes'; -import { Warning } from '#components-ui/alerts'; -import ButtonLink from '#components-ui/button'; -import { PrintNever } from '#components-ui/print-visibility'; -import ShowMore from '#components-ui/show-more'; -import { AsyncDataSectionServer } from '#components/section/data-section/server'; -import { FullTable } from '#components/table/full'; -import { EAdministration } from '#models/administrations/EAdministration'; -import { IAPINotRespondingError } from '#models/api-not-responding'; -import { - IUniteLegale, - isAssociation, - isServicePublic, -} from '#models/core/types'; -import { IActesRNE } from '#models/immatriculation'; -import { formatDateLong } from '#utils/helpers'; - -const NoDocument = () => ( - <>Aucun document n’a été retrouvé dans le RNE pour cette entreprise. -); - -export const AgentActesSection: React.FC<{ - uniteLegale: IUniteLegale; - documents: Promise; -}> = ({ uniteLegale, documents }) => { - return ( - - - {(isAssociation(uniteLegale) || isServicePublic(uniteLegale)) && ( - <> - - Les associations et les services publics ne sont pas - immatriculés au RNE. - -
- - )} - - - } - > - {(documents) => - documents.actes?.length === 0 ? ( - - ) : ( - <> -

- Cette entreprise possède {documents.actes.length} document(s) au - RNE. Chaque document peut contenir un ou plusieurs actes : -

- {documents.actes.length >= 5 ? ( - - - - ) : ( - - )} - - ) - } -
-
- ); -}; - -type IActesTableProps = { - actes: IActesRNE['actes']; -}; -function ActesTable({ actes }: IActesTableProps) { - return ( - [ - formatDateLong(a.dateDepot), - a.actes && ( -
    - {(a?.actes || []).map((acteName) => ( -
  • {acteName}
  • - ))} -
- ), - - Télécharger - , - ])} - /> - ); -} diff --git a/components/load-bar/index.tsx b/components/load-bar/index.tsx index eccc79e8b..e514fc249 100644 --- a/components/load-bar/index.tsx +++ b/components/load-bar/index.tsx @@ -10,6 +10,8 @@ export default function LoadBar({ session }: { session: ISession | null }) { const loadBar = loadBarFactory(); if (typeof window !== 'undefined') { window.addEventListener('beforeunload', loadBar.run); + window.addEventListener('runloadbar', loadBar.run); + window.addEventListener('cancelloadbar', loadBar.cancel); } }, []); return ( @@ -94,5 +96,11 @@ const loadBarFactory = () => { await wait(200); } }, + cancel: function () { + this._currentJobId = ''; + if (this._loader) { + this._loader.style.width = '0'; + } + }, }; }; diff --git a/components/title-section/tabs/TabLink.tsx b/components/title-section/tabs/TabLink.tsx new file mode 100644 index 000000000..7d1318ef3 --- /dev/null +++ b/components/title-section/tabs/TabLink.tsx @@ -0,0 +1,35 @@ +'use client'; +import Link from 'next/link'; +import { useEffect } from 'react'; +import styles from './styles.module.css'; +type IProps = { + label: string; + href: string; + noFollow?: boolean; + width?: string; + active: boolean; +}; +export default function TabLink({ + active, + href, + label, + noFollow, + width, +}: IProps) { + useEffect(() => { + active === false && window.dispatchEvent(new Event('cancelloadbar')); + }, [active]); + return ( + window.dispatchEvent(new Event('runloadbar'))} + prefetch={false} + > + {active ? label :

{label}

} + + ); +} diff --git a/components/title-section/tabs/index.tsx b/components/title-section/tabs/index.tsx index 1e648ddfe..013a660de 100644 --- a/components/title-section/tabs/index.tsx +++ b/components/title-section/tabs/index.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import { PrintNever } from '#components-ui/print-visibility'; import { checkHasLabelsAndCertificates, @@ -10,6 +11,7 @@ import { } from '#models/core/types'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; +import TabLink from './TabLink'; import styles from './styles.module.css'; export enum FICHE { @@ -135,30 +137,27 @@ export const Tabs: React.FC<{ noFollow, width = 'auto', }) => ( - - {currentFicheType === ficheType ? label :

{label}

} -
+ width={width} + /> ) )} {currentFicheType === FICHE.ETABLISSEMENT && ( <> diff --git a/components/title-section/tabs/styles.module.css b/components/title-section/tabs/styles.module.css index 65c81c7bb..dd0484798 100644 --- a/components/title-section/tabs/styles.module.css +++ b/components/title-section/tabs/styles.module.css @@ -20,7 +20,8 @@ border-bottom: 0; } } -.titleTabs > a { + +.titleTabs>a { border-top-left-radius: 3px; border-top-right-radius: 3px; border: 2px solid var(--annuaire-colors-pastelBlue); @@ -35,13 +36,13 @@ margin-bottom: -2px; } -.titleTabs > a > h2 { +.titleTabs>a>h2 { margin: 0; padding: 0; } -.titleTabs > a, -.titleTabs > a > h2 { +.titleTabs>a, +.titleTabs>a>h2 { color: var(--annuaire-colors-frBlue); font-weight: bold; font-size: 0.9rem; @@ -56,6 +57,7 @@ } } -.titleTabs > a:hover { +.titleTabs>a:hover, +.titleTabs>a:focus { background-color: var(--annuaire-colors-pastelBlue); -} +} \ No newline at end of file From fddd7ce6ae5f0d1e51c01004df96f5b3294afd76 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 17:30:37 +0200 Subject: [PATCH 10/14] =?UTF-8?q?Revert=20"R=C3=A9tabli=20les=20link=20et?= =?UTF-8?q?=20utilise=20les=20routes=20parall=C3=A8les=20pour=20/documents?= =?UTF-8?q?=20(#1=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 12aedc4b29f0c4437d4a18f118e64083e0d98477. --- .../documents/[slug]/@actes/common.tsx | 8 -- .../documents/[slug]/@actes/layout.tsx | 27 ----- .../documents/[slug]/@actes/loading.tsx | 11 -- .../documents/[slug]/@actes/page.tsx | 95 ---------------- .../[slug]/@carteProfessionnelleTP/layout.tsx | 14 --- .../@carteProfessionnelleTP/loading.tsx | 3 - .../[slug]/@carteProfessionnelleTP/page.tsx | 29 ----- .../documents/[slug]/@conformite/common.tsx | 8 -- .../documents/[slug]/@conformite/layout.tsx | 20 ---- .../documents/[slug]/@conformite/loading.tsx | 11 -- .../documents/[slug]/@conformite/page.tsx | 46 -------- .../documents/[slug]/{layout.tsx => page.tsx} | 43 +++++-- .../agent-wall/document.tsx | 1 + .../conformite-section.tsx | 57 ++++++++++ .../documents/actes-walled.tsx | 25 +++++ .../documents/bilans-walled.tsx | 1 + .../documents/data-section/actes.tsx | 106 ++++++++++++++++++ components/load-bar/index.tsx | 8 -- components/title-section/tabs/TabLink.tsx | 35 ------ components/title-section/tabs/index.tsx | 21 ++-- .../title-section/tabs/styles.module.css | 14 +-- 21 files changed, 239 insertions(+), 344 deletions(-) delete mode 100644 app/(header-default)/documents/[slug]/@actes/common.tsx delete mode 100644 app/(header-default)/documents/[slug]/@actes/layout.tsx delete mode 100644 app/(header-default)/documents/[slug]/@actes/loading.tsx delete mode 100644 app/(header-default)/documents/[slug]/@actes/page.tsx delete mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx delete mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx delete mode 100644 app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx delete mode 100644 app/(header-default)/documents/[slug]/@conformite/common.tsx delete mode 100644 app/(header-default)/documents/[slug]/@conformite/layout.tsx delete mode 100644 app/(header-default)/documents/[slug]/@conformite/loading.tsx delete mode 100644 app/(header-default)/documents/[slug]/@conformite/page.tsx rename app/(header-default)/documents/[slug]/{layout.tsx => page.tsx} (50%) create mode 100644 components/espace-agent-components/conformite-section.tsx create mode 100644 components/espace-agent-components/documents/actes-walled.tsx create mode 100644 components/espace-agent-components/documents/data-section/actes.tsx delete mode 100644 components/title-section/tabs/TabLink.tsx diff --git a/app/(header-default)/documents/[slug]/@actes/common.tsx b/app/(header-default)/documents/[slug]/@actes/common.tsx deleted file mode 100644 index e14b25ab9..000000000 --- a/app/(header-default)/documents/[slug]/@actes/common.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { EAdministration } from '#models/administrations/EAdministration'; - -export const sectionInfo = { - title: 'Actes et statuts', - id: 'actes', - isProtected: true, - sources: [EAdministration.INPI], -}; diff --git a/app/(header-default)/documents/[slug]/@actes/layout.tsx b/app/(header-default)/documents/[slug]/@actes/layout.tsx deleted file mode 100644 index dfa5cff19..000000000 --- a/app/(header-default)/documents/[slug]/@actes/layout.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { PrintNever } from '#components-ui/print-visibility'; -import AgentWallDocuments from '#components/espace-agent-components/agent-wall/document'; -import { EScope, hasRights } from '#models/user/rights'; -import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; -import extractParamsAppRouter, { - AppRouterProps, -} from '#utils/server-side-helper/app/extract-params'; -import getSession from '#utils/server-side-helper/app/get-session'; - -export default async function Layout(props: PropsWithChildren) { - const session = await getSession(); - const { slug, isBot } = extractParamsAppRouter(props); - const uniteLegale = await cachedGetUniteLegale(slug, isBot); - - if (!hasRights(session, EScope.actesRne)) { - return ( - - ); - } - - return {props.children}; -} diff --git a/app/(header-default)/documents/[slug]/@actes/loading.tsx b/app/(header-default)/documents/[slug]/@actes/loading.tsx deleted file mode 100644 index 10b7f3a94..000000000 --- a/app/(header-default)/documents/[slug]/@actes/loading.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Section } from '#components/section'; -import { DataSectionLoader } from '#components/section/data-section/loader'; -import { sectionInfo } from './common'; - -export default function Loader() { - return ( -
- -
- ); -} diff --git a/app/(header-default)/documents/[slug]/@actes/page.tsx b/app/(header-default)/documents/[slug]/@actes/page.tsx deleted file mode 100644 index b5fe324b8..000000000 --- a/app/(header-default)/documents/[slug]/@actes/page.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import routes from '#clients/routes'; -import { Warning } from '#components-ui/alerts'; -import ButtonLink from '#components-ui/button'; -import ShowMore from '#components-ui/show-more'; -import { DataSection } from '#components/section/data-section'; -import { FullTable } from '#components/table/full'; -import { isAssociation, isServicePublic } from '#models/core/types'; -import { IActesRNE } from '#models/immatriculation'; -import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; -import { formatDateLong } from '#utils/helpers'; -import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; -import extractParamsAppRouter, { - AppRouterProps, -} from '#utils/server-side-helper/app/extract-params'; -import { sectionInfo } from './common'; - -const NoDocument = () => ( - <>Aucun document n’a été retrouvé dans le RNE pour cette entreprise. -); - -export default async function AgentActesSection(props: AppRouterProps) { - const { slug, isBot } = extractParamsAppRouter(props); - const uniteLegale = await cachedGetUniteLegale(slug, isBot); - const documents = await getDocumentsRNEProtected(uniteLegale.siren); - - return ( - - {(isAssociation(uniteLegale) || isServicePublic(uniteLegale)) && ( - <> - - Les associations et les services publics ne sont pas - immatriculés au RNE. - -
- - )} - - - } - > - {(documents) => ( - <> -

- Cette entreprise possède {documents.actes.length} document(s) au - RNE. Chaque document peut contenir un ou plusieurs actes : -

- {documents.actes.length >= 5 ? ( - - - - ) : ( - - )} - - )} -
- ); -} - -type IActesTableProps = { - actes: IActesRNE['actes']; -}; -function ActesTable({ actes }: IActesTableProps) { - return ( - [ - formatDateLong(a.dateDepot), - a.actes && ( -
    - {(a?.actes || []).map((acteName) => ( -
  • {acteName}
  • - ))} -
- ), - - Télécharger - , - ])} - /> - ); -} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx deleted file mode 100644 index 9c129dcda..000000000 --- a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/layout.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { PrintNever } from '#components-ui/print-visibility'; -import { EScope, hasRights } from '#models/user/rights'; -import getSession from '#utils/server-side-helper/app/get-session'; - -export default async function Layout(props: PropsWithChildren) { - const session = await getSession(); - - if (!hasRights(session, EScope.carteProfessionnelleTravauxPublics)) { - return null; - } - - return {props.children}; -} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx deleted file mode 100644 index 351081a7f..000000000 --- a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/loading.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Loader() { - return null; -} diff --git a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx b/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx deleted file mode 100644 index 7c7fa6875..000000000 --- a/app/(header-default)/documents/[slug]/@carteProfessionnelleTP/page.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { CarteProfessionnelleTravauxPublicsSection } from '#components/carte-professionnelle-travaux-publics-section'; -import { isAPI404 } from '#models/api-not-responding'; -import { getCarteProfessionnelleTravauxPublic } from '#models/espace-agent/carte-professionnelle-travaux-publics'; -import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; -import extractParamsAppRouter, { - AppRouterProps, -} from '#utils/server-side-helper/app/extract-params'; -import getSession from '#utils/server-side-helper/app/get-session'; - -export default async function Page(props: AppRouterProps) { - const session = await getSession(); - const { slug, isBot } = extractParamsAppRouter(props); - const uniteLegale = await cachedGetUniteLegale(slug, isBot); - - const carteProfessionnelleTravauxPublics = - await getCarteProfessionnelleTravauxPublic( - uniteLegale.siren, - session?.user - ); - if (isAPI404(carteProfessionnelleTravauxPublics)) { - return null; - } - - return ( - - ); -} diff --git a/app/(header-default)/documents/[slug]/@conformite/common.tsx b/app/(header-default)/documents/[slug]/@conformite/common.tsx deleted file mode 100644 index b47a84d1a..000000000 --- a/app/(header-default)/documents/[slug]/@conformite/common.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { EAdministration } from '#models/administrations/EAdministration'; - -export const sectionInfo = { - title: 'Conformité', - id: 'conformite', - isProtected: true, - sources: [EAdministration.DGFIP, EAdministration.URSSAF, EAdministration.MSA], -}; diff --git a/app/(header-default)/documents/[slug]/@conformite/layout.tsx b/app/(header-default)/documents/[slug]/@conformite/layout.tsx deleted file mode 100644 index 5aa5c6528..000000000 --- a/app/(header-default)/documents/[slug]/@conformite/layout.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { HorizontalSeparator } from '#components-ui/horizontal-separator'; -import { PrintNever } from '#components-ui/print-visibility'; -import { EScope, hasRights } from '#models/user/rights'; -import getSession from '#utils/server-side-helper/app/get-session'; - -export default async function Layout(props: PropsWithChildren) { - const session = await getSession(); - - if (!hasRights(session, EScope.conformite)) { - return null; - } - - return ( - - {props.children} - - - ); -} diff --git a/app/(header-default)/documents/[slug]/@conformite/loading.tsx b/app/(header-default)/documents/[slug]/@conformite/loading.tsx deleted file mode 100644 index 10b7f3a94..000000000 --- a/app/(header-default)/documents/[slug]/@conformite/loading.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Section } from '#components/section'; -import { DataSectionLoader } from '#components/section/data-section/loader'; -import { sectionInfo } from './common'; - -export default function Loader() { - return ( -
- -
- ); -} diff --git a/app/(header-default)/documents/[slug]/@conformite/page.tsx b/app/(header-default)/documents/[slug]/@conformite/page.tsx deleted file mode 100644 index 83bc77c1f..000000000 --- a/app/(header-default)/documents/[slug]/@conformite/page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import Conformite from '#components/espace-agent-components/conformite'; -import { DataSection } from '#components/section/data-section'; -import { TwoColumnTable } from '#components/table/simple'; -import { getConformiteEntreprise } from '#models/espace-agent/conformite'; -import { cachedGetUniteLegale } from '#utils/server-side-helper/app/cached-methods'; -import extractParamsAppRouter, { - AppRouterProps, -} from '#utils/server-side-helper/app/extract-params'; -import getSession from '#utils/server-side-helper/app/get-session'; -import { sectionInfo } from './common'; - -async function ConformiteSection(props: AppRouterProps) { - const { slug, isBot } = extractParamsAppRouter(props); - const uniteLegale = await cachedGetUniteLegale(slug, isBot); - const session = await getSession(); - const conformite = await getConformiteEntreprise( - uniteLegale.siren, - uniteLegale.siege.siret, - session?.user?.siret - ); - - return ( - - {(conformite) => ( - ], - [ - 'Conformité sociale', - <> - -
- - , - ], - ]} - /> - )} -
- ); -} - -export default ConformiteSection; diff --git a/app/(header-default)/documents/[slug]/layout.tsx b/app/(header-default)/documents/[slug]/page.tsx similarity index 50% rename from app/(header-default)/documents/[slug]/layout.tsx rename to app/(header-default)/documents/[slug]/page.tsx index 6f27aad15..3313a4c1a 100644 --- a/app/(header-default)/documents/[slug]/layout.tsx +++ b/app/(header-default)/documents/[slug]/page.tsx @@ -1,6 +1,13 @@ import { Metadata } from 'next'; +import { HorizontalSeparator } from '#components-ui/horizontal-separator'; +import { CarteProfessionnelleTravauxPublicsSection } from '#components/carte-professionnelle-travaux-publics-section'; +import ConformiteSection from '#components/espace-agent-components/conformite-section'; +import ActesSection from '#components/espace-agent-components/documents/actes-walled'; import Title from '#components/title-section'; import { FICHE } from '#components/title-section/tabs'; +import { isAPI404 } from '#models/api-not-responding'; +import { getCarteProfessionnelleTravauxPublic } from '#models/espace-agent/carte-professionnelle-travaux-publics'; +import { EScope, hasRights } from '#models/user/rights'; import { uniteLegalePageDescription, uniteLegalePageTitle, @@ -34,17 +41,20 @@ export const generateMetadata = async ( }; }; -const UniteLegaleDocumentPage = async ( - props: AppRouterProps & { - conformite: React.ReactNode; - carteProfessionnelleTP: React.ReactNode; - actes: React.ReactNode; - } -) => { +const UniteLegaleDocumentPage = async (props: AppRouterProps) => { const session = await getSession(); const { slug, isBot } = extractParamsAppRouter(props); const uniteLegale = await cachedGetUniteLegale(slug, isBot); + const carteProfessionnelleTravauxPublics = hasRights( + session, + EScope.carteProfessionnelleTravauxPublics + ) + ? await getCarteProfessionnelleTravauxPublic( + uniteLegale.siren, + session?.user + ) + : null; return ( <>
@@ -53,10 +63,21 @@ const UniteLegaleDocumentPage = async ( ficheType={FICHE.DOCUMENTS} session={session} /> - - {props.conformite} - {props.actes} - {props.carteProfessionnelleTP} + {hasRights(session, EScope.conformite) && ( + <> + + + + )} + + {carteProfessionnelleTravauxPublics && + !isAPI404(carteProfessionnelleTravauxPublics) && ( + + )}
); diff --git a/components/espace-agent-components/agent-wall/document.tsx b/components/espace-agent-components/agent-wall/document.tsx index d3ae21b0b..b4738515a 100644 --- a/components/espace-agent-components/agent-wall/document.tsx +++ b/components/espace-agent-components/agent-wall/document.tsx @@ -8,6 +8,7 @@ const AgentWallDocuments: React.FC< title: string; id: string; uniteLegale: IUniteLegale; + condition: boolean; }> > = ({ uniteLegale, id, title }) => ( + + {(conformite) => ( + ], + [ + 'Conformité sociale', + <> + +
+ + , + ], + ]} + /> + )} +
+ + ); +} + +export default ConformiteSection; diff --git a/components/espace-agent-components/documents/actes-walled.tsx b/components/espace-agent-components/documents/actes-walled.tsx new file mode 100644 index 000000000..52611a138 --- /dev/null +++ b/components/espace-agent-components/documents/actes-walled.tsx @@ -0,0 +1,25 @@ +import { IUniteLegale } from '#models/core/types'; +import { getDocumentsRNEProtected } from '#models/immatriculation/rne'; +import { EScope, hasRights } from '#models/user/rights'; +import { ISession } from '#models/user/session'; +import AgentWallDocuments from '../agent-wall/document'; +import { AgentActesSection } from './data-section/actes'; + +const ActesSection: React.FC<{ + uniteLegale: IUniteLegale; + session: ISession | null; +}> = async ({ uniteLegale, session }) => { + if (!hasRights(session, EScope.actesRne)) { + return ( + + ); + } + const documents = getDocumentsRNEProtected(uniteLegale.siren); + return ; +}; +export default ActesSection; diff --git a/components/espace-agent-components/documents/bilans-walled.tsx b/components/espace-agent-components/documents/bilans-walled.tsx index 9ac295dd8..cd91210f7 100644 --- a/components/espace-agent-components/documents/bilans-walled.tsx +++ b/components/espace-agent-components/documents/bilans-walled.tsx @@ -15,6 +15,7 @@ const BilansSection: React.FC<{ title="Bilans" id="bilans" uniteLegale={uniteLegale} + condition={!hasRights(session, EScope.bilansRne)} /> ); } diff --git a/components/espace-agent-components/documents/data-section/actes.tsx b/components/espace-agent-components/documents/data-section/actes.tsx new file mode 100644 index 000000000..563da177a --- /dev/null +++ b/components/espace-agent-components/documents/data-section/actes.tsx @@ -0,0 +1,106 @@ +'use client'; + +import routes from '#clients/routes'; +import { Warning } from '#components-ui/alerts'; +import ButtonLink from '#components-ui/button'; +import { PrintNever } from '#components-ui/print-visibility'; +import ShowMore from '#components-ui/show-more'; +import { AsyncDataSectionServer } from '#components/section/data-section/server'; +import { FullTable } from '#components/table/full'; +import { EAdministration } from '#models/administrations/EAdministration'; +import { IAPINotRespondingError } from '#models/api-not-responding'; +import { + IUniteLegale, + isAssociation, + isServicePublic, +} from '#models/core/types'; +import { IActesRNE } from '#models/immatriculation'; +import { formatDateLong } from '#utils/helpers'; + +const NoDocument = () => ( + <>Aucun document n’a été retrouvé dans le RNE pour cette entreprise. +); + +export const AgentActesSection: React.FC<{ + uniteLegale: IUniteLegale; + documents: Promise; +}> = ({ uniteLegale, documents }) => { + return ( + + + {(isAssociation(uniteLegale) || isServicePublic(uniteLegale)) && ( + <> + + Les associations et les services publics ne sont pas + immatriculés au RNE. + +
+ + )} + + + } + > + {(documents) => + documents.actes?.length === 0 ? ( + + ) : ( + <> +

+ Cette entreprise possède {documents.actes.length} document(s) au + RNE. Chaque document peut contenir un ou plusieurs actes : +

+ {documents.actes.length >= 5 ? ( + + + + ) : ( + + )} + + ) + } +
+
+ ); +}; + +type IActesTableProps = { + actes: IActesRNE['actes']; +}; +function ActesTable({ actes }: IActesTableProps) { + return ( + [ + formatDateLong(a.dateDepot), + a.actes && ( +
    + {(a?.actes || []).map((acteName) => ( +
  • {acteName}
  • + ))} +
+ ), + + Télécharger + , + ])} + /> + ); +} diff --git a/components/load-bar/index.tsx b/components/load-bar/index.tsx index e514fc249..eccc79e8b 100644 --- a/components/load-bar/index.tsx +++ b/components/load-bar/index.tsx @@ -10,8 +10,6 @@ export default function LoadBar({ session }: { session: ISession | null }) { const loadBar = loadBarFactory(); if (typeof window !== 'undefined') { window.addEventListener('beforeunload', loadBar.run); - window.addEventListener('runloadbar', loadBar.run); - window.addEventListener('cancelloadbar', loadBar.cancel); } }, []); return ( @@ -96,11 +94,5 @@ const loadBarFactory = () => { await wait(200); } }, - cancel: function () { - this._currentJobId = ''; - if (this._loader) { - this._loader.style.width = '0'; - } - }, }; }; diff --git a/components/title-section/tabs/TabLink.tsx b/components/title-section/tabs/TabLink.tsx deleted file mode 100644 index 7d1318ef3..000000000 --- a/components/title-section/tabs/TabLink.tsx +++ /dev/null @@ -1,35 +0,0 @@ -'use client'; -import Link from 'next/link'; -import { useEffect } from 'react'; -import styles from './styles.module.css'; -type IProps = { - label: string; - href: string; - noFollow?: boolean; - width?: string; - active: boolean; -}; -export default function TabLink({ - active, - href, - label, - noFollow, - width, -}: IProps) { - useEffect(() => { - active === false && window.dispatchEvent(new Event('cancelloadbar')); - }, [active]); - return ( - window.dispatchEvent(new Event('runloadbar'))} - prefetch={false} - > - {active ? label :

{label}

} - - ); -} diff --git a/components/title-section/tabs/index.tsx b/components/title-section/tabs/index.tsx index 013a660de..1e648ddfe 100644 --- a/components/title-section/tabs/index.tsx +++ b/components/title-section/tabs/index.tsx @@ -1,4 +1,3 @@ -import Link from 'next/link'; import { PrintNever } from '#components-ui/print-visibility'; import { checkHasLabelsAndCertificates, @@ -11,7 +10,6 @@ import { } from '#models/core/types'; import { EScope, hasRights } from '#models/user/rights'; import { ISession } from '#models/user/session'; -import TabLink from './TabLink'; import styles from './styles.module.css'; export enum FICHE { @@ -137,27 +135,30 @@ export const Tabs: React.FC<{ noFollow, width = 'auto', }) => ( - + style={{ width }} + > + {currentFicheType === ficheType ? label :

{label}

} + ) )} {currentFicheType === FICHE.ETABLISSEMENT && ( <>
-

Fiche Ă©tablissement

- + )}
diff --git a/components/title-section/tabs/styles.module.css b/components/title-section/tabs/styles.module.css index dd0484798..65c81c7bb 100644 --- a/components/title-section/tabs/styles.module.css +++ b/components/title-section/tabs/styles.module.css @@ -20,8 +20,7 @@ border-bottom: 0; } } - -.titleTabs>a { +.titleTabs > a { border-top-left-radius: 3px; border-top-right-radius: 3px; border: 2px solid var(--annuaire-colors-pastelBlue); @@ -36,13 +35,13 @@ margin-bottom: -2px; } -.titleTabs>a>h2 { +.titleTabs > a > h2 { margin: 0; padding: 0; } -.titleTabs>a, -.titleTabs>a>h2 { +.titleTabs > a, +.titleTabs > a > h2 { color: var(--annuaire-colors-frBlue); font-weight: bold; font-size: 0.9rem; @@ -57,7 +56,6 @@ } } -.titleTabs>a:hover, -.titleTabs>a:focus { +.titleTabs > a:hover { background-color: var(--annuaire-colors-pastelBlue); -} \ No newline at end of file +} From 6c2dc9b4ab5ab590599c76f799584b8e4a51e01a Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Wed, 29 May 2024 19:07:29 +0200 Subject: [PATCH 11/14] =?UTF-8?q?fix:=20remove=20timeout=20before=20showin?= =?UTF-8?q?g=20conformit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- models/espace-agent/conformite.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/models/espace-agent/conformite.ts b/models/espace-agent/conformite.ts index 91297770c..3aec15620 100644 --- a/models/espace-agent/conformite.ts +++ b/models/espace-agent/conformite.ts @@ -22,8 +22,6 @@ export const getConformiteEntreprise = async ( siret: Siret, recipientSiret?: string ): Promise => { - await new Promise((resolve) => setTimeout(resolve, 20000)); - const [fiscale, vigilance, msa] = await Promise.all([ clientApiEntrepriseConformiteFiscale(siren, recipientSiret).catch((error) => handleApiEntrepriseError(error, { siren, siret }) From bb775526875c20e41a1198b62940a5f6a731ee4e Mon Sep 17 00:00:00 2001 From: Xavier Jp Date: Fri, 31 May 2024 09:50:40 +0200 Subject: [PATCH 12/14] Sub services in dirigeant page for service public (#1089) * chore: remove useCache from forbidden config on frontend client * feat: add hierarchy in dirigeant page for public services --- .../dirigeants/[slug]/page.tsx | 11 ++- ...ic.tsx => service-public-responsables.tsx} | 1 + .../sections/service-public-subservices.tsx | 97 +++++++++++++++++++ .../clients/annuaire-service-public/index.ts | 39 ++++++++ hooks/fetch/service-public-subservices.ts | 35 +++++++ models/service-public/index.ts | 1 + utils/network/frontend/index.ts | 4 +- 7 files changed, 185 insertions(+), 3 deletions(-) rename app/(header-default)/dirigeants/_component/sections/{responsables-service-public.tsx => service-public-responsables.tsx} (99%) create mode 100644 app/(header-default)/dirigeants/_component/sections/service-public-subservices.tsx create mode 100644 hooks/fetch/service-public-subservices.ts diff --git a/app/(header-default)/dirigeants/[slug]/page.tsx b/app/(header-default)/dirigeants/[slug]/page.tsx index 2393a9dca..74cc14889 100644 --- a/app/(header-default)/dirigeants/[slug]/page.tsx +++ b/app/(header-default)/dirigeants/[slug]/page.tsx @@ -13,8 +13,9 @@ import extractParamsAppRouter, { AppRouterProps, } from '#utils/server-side-helper/app/extract-params'; import getSession from '#utils/server-side-helper/app/get-session'; -import ResponsableSection from 'app/(header-default)/dirigeants/_component/sections/responsables-service-public'; +import ResponsableSection from 'app/(header-default)/dirigeants/_component/sections/service-public-responsables'; import { DirigeantInformation } from '../_component/sections'; +import SubServicesSection from '../_component/sections/service-public-subservices'; export const generateMetadata = async ( props: AppRouterProps @@ -56,7 +57,13 @@ const DirigeantsPage = async (props: AppRouterProps) => { session={session} /> {servicePublic ? ( - + <> + + + ) : ( !isBot && ( }> diff --git a/app/(header-default)/dirigeants/_component/sections/responsables-service-public.tsx b/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx similarity index 99% rename from app/(header-default)/dirigeants/_component/sections/responsables-service-public.tsx rename to app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx index 0ba453743..860172aa6 100644 --- a/app/(header-default)/dirigeants/_component/sections/responsables-service-public.tsx +++ b/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx @@ -7,6 +7,7 @@ import { IAPINotRespondingError } from '#models/api-not-responding'; import { IServicePublic } from '#models/service-public'; type IProps = { servicePublic: IServicePublic | IAPINotRespondingError }; + export default function ResponsableSection({ servicePublic }: IProps) { return ( + {(subServices) => ( + <> +

+ Ce service public se compose de {subServices.length} départements : +

+ { + return [ + service.urlServicePublic ? ( + + {service.nom} + + ) : ( + service.nom + ), + service.affectationPersonne?.length === 0 ? ( + + ) : service?.affectationPersonne && + service?.affectationPersonne.length > 0 ? ( + service?.affectationPersonne.map((personne, index) => ( + <> + {index !== 0 && ', '} + {personne.nom} {personne.fonction} + + )) + ) : ( + + ), + ]; + })} + /> + + )} + + ); +} + +export default function SubServicesSection({ + servicePublic, + uniteLegale, +}: { + servicePublic: IServicePublic | IAPINotRespondingError; + uniteLegale: IUniteLegale; +}) { + if ( + isAPINotResponding(servicePublic) || + servicePublic.subServicesId?.length === 0 + ) { + return null; + } + + return ( + + ); +} diff --git a/clients/open-data-soft/clients/annuaire-service-public/index.ts b/clients/open-data-soft/clients/annuaire-service-public/index.ts index 38c206d94..28b57876c 100644 --- a/clients/open-data-soft/clients/annuaire-service-public/index.ts +++ b/clients/open-data-soft/clients/annuaire-service-public/index.ts @@ -19,8 +19,11 @@ interface IServicePublicRecord { telephone: string; type_organisme: string; url_service_public: string; + hierarchie: string; } +type IServices = { type_hierarchie: string; service: string }; + type IAffectationRecord = { personne: { prenom: string; @@ -75,6 +78,20 @@ const clientAnnuaireServicePublicByName = async ( }; }; +const clientAnnuaireServicePublicByIds = async ( + ids: string[] +): Promise => { + const query = `id="${ids.join('" OR id="')}"`; + const useCache = false; + let response = await queryAnnuaireServicePublic(query); + + if (!response.records.length) { + throw new HttpNotFound(`Ids = ${ids.join(', ')}`); + } + + return response.records.map(mapToDomainObject); +}; + const clientAnnuaireServicePublicBySiret = async ( siret: Siret ): Promise => { @@ -107,9 +124,26 @@ const mapToDomainObject = ( typeOrganisme: (record.type_organisme as IServicePublic['typeOrganisme']) || null, nom: mapToNom(record), + subServicesId: mapToServicesId(record.hierarchie), }; }; +/** + * List of sub services + * @param hierarchieSerialized + * @returns + */ +function mapToServicesId(hierarchieSerialized: string): string[] { + const hierarchie = JSON.parse( + hierarchieSerialized || 'null' + ) as Array | null; + + if (!hierarchie || !hierarchie.length) { + return []; + } + return hierarchie.map((service) => service.service); +} + type IAffectationPersonne = IServicePublic['affectationPersonne']; function mapToAffectationPersonne( affectationRecord: string @@ -232,6 +266,10 @@ function mapToLiens(record: IServicePublicRecord) { return liens; } +const stubbedClientAnnuaireServicePublicByIds = stubClient({ + clientAnnuaireServicePublicByIds, +}); + const stubbedClientAnnuaireServicePublicBySiret = stubClient({ clientAnnuaireServicePublicBySiret, }); @@ -240,6 +278,7 @@ const stubbedClientAnnuaireServicePublicByName = stubClient({ clientAnnuaireServicePublicByName, }); export { + stubbedClientAnnuaireServicePublicByIds as clientAnnuaireServicePublicByIds, stubbedClientAnnuaireServicePublicByName as clientAnnuaireServicePublicByName, stubbedClientAnnuaireServicePublicBySiret as clientAnnuaireServicePublicBySiret, }; diff --git a/hooks/fetch/service-public-subservices.ts b/hooks/fetch/service-public-subservices.ts new file mode 100644 index 000000000..3b1a67607 --- /dev/null +++ b/hooks/fetch/service-public-subservices.ts @@ -0,0 +1,35 @@ +import { clientAnnuaireServicePublicByIds } from '#clients/open-data-soft/clients/annuaire-service-public'; +import { EAdministration } from '#models/administrations/EAdministration'; +import { IUniteLegale } from '#models/core/types'; +import { FetchRessourceException } from '#models/exceptions'; +import { IServicePublic } from '#models/service-public'; +import logErrorInSentry from '#utils/sentry'; +import { useFetchData } from './use-fetch-data'; + +export function useFetchServicePublicSubServices( + servicePublic: IServicePublic, + uniteLegale: IUniteLegale +) { + return useFetchData( + { + fetchData: () => + clientAnnuaireServicePublicByIds(servicePublic.subServicesId), + administration: EAdministration.DILA, + logError: (e: any) => { + if (e.status === 404) { + return; + } + const exception = new FetchRessourceException({ + ressource: 'AnnuaireServicePublic', + administration: EAdministration.DILA, + cause: e, + context: { + siren: uniteLegale.siren, + }, + }); + logErrorInSentry(exception); + }, + }, + [uniteLegale] + ); +} diff --git a/models/service-public/index.ts b/models/service-public/index.ts index e8048fbb6..f9758d94a 100644 --- a/models/service-public/index.ts +++ b/models/service-public/index.ts @@ -56,6 +56,7 @@ export interface IServicePublic { | 'Syndicat mixte' | "Établissement d'enseignement"; urlServicePublic: string | null; + subServicesId: string[]; } type ILien = { libelle: string; diff --git a/utils/network/frontend/index.ts b/utils/network/frontend/index.ts index 5a79afd67..f9bf41c1d 100644 --- a/utils/network/frontend/index.ts +++ b/utils/network/frontend/index.ts @@ -32,7 +32,9 @@ export async function httpFrontClient(config: IDefaultRequestConfig) { if (!config.url) { throw new InternalError({ message: 'Url is required' }); } - if (config.useCache || config.method || config.data || config.headers) { + // use cache is ignore on frontend as it is handled by browser + + if (config.method || config.data || config.headers) { throw new InternalError({ message: 'Feature not yet supported on frontend client', }); From 7bd2adfea846d7ee6bdd48aa307af7e6a651f98e Mon Sep 17 00:00:00 2001 From: xavier jouppe Date: Fri, 31 May 2024 11:59:09 +0200 Subject: [PATCH 13/14] content: use responsable rather than dirigeants in service public --- .../sections/service-public-responsables.tsx | 10 +++++----- .../_component/sections/service-public-subservices.tsx | 2 +- components/service-public-section/index.tsx | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx b/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx index 860172aa6..fe7b392b6 100644 --- a/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx +++ b/app/(header-default)/dirigeants/_component/sections/service-public-responsables.tsx @@ -12,7 +12,7 @@ export default function ResponsableSection({ servicePublic }: IProps) { return ( } data={servicePublic} @@ -21,15 +21,15 @@ export default function ResponsableSection({ servicePublic }: IProps) { <> {!servicePublic.affectationPersonne ? (

- Ce service public n’a pas d’équipe dirigeante enregistrée auprès - de la . + Ce service public n’a pas de responsable enregistré auprès de la{' '} + .

) : ( <>

Ce service public possède{' '} - {servicePublic.affectationPersonne.length} dirigeant(es) - enregistré(es) auprès de la + {servicePublic.affectationPersonne.length} responsable(s) + enregistré(s) auprès de la {servicePublic.liens.annuaireServicePublic && ( <> {' '} diff --git a/app/(header-default)/dirigeants/_component/sections/service-public-subservices.tsx b/app/(header-default)/dirigeants/_component/sections/service-public-subservices.tsx index a8a7b1b1e..b17fee07c 100644 --- a/app/(header-default)/dirigeants/_component/sections/service-public-subservices.tsx +++ b/app/(header-default)/dirigeants/_component/sections/service-public-subservices.tsx @@ -37,7 +37,7 @@ function SubServicesDataSection({ Ce service public se compose de {subServices.length} départements :

{ return [ service.urlServicePublic ? ( diff --git a/components/service-public-section/index.tsx b/components/service-public-section/index.tsx index c013e42ab..cad460914 100644 --- a/components/service-public-section/index.tsx +++ b/components/service-public-section/index.tsx @@ -26,6 +26,7 @@ export default function ServicePublicSection({ > {(servicePublic) => ( <> + {servicePublic.mission &&

{servicePublic.mission}

} {servicePublic.liens.annuaireServicePublic && (

From ab9da9b529498df4714a936765497626f8963dab Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Fri, 31 May 2024 12:03:41 +0200 Subject: [PATCH 14/14] fix: scalingo deploy (#1078) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d46fceb3d..4eee3e03f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "engines": { - "node": ">=20", + "node": "20", "npm": ">=10" }, "scripts": {