diff --git a/packages/ui/src/components/titre.stories_snapshots_TitreAvecUnOctroiEnConstructionEtUnTravaux.html b/packages/ui/src/components/titre.stories_snapshots_TitreAvecUnOctroiEnConstructionEtUnTravaux.html index d3be05d16d24725bc23e0519884ac501be1002c5..d514dbc3ac3cedae34266f03dc50ffa151cb0605 100644 --- a/packages/ui/src/components/titre.stories_snapshots_TitreAvecUnOctroiEnConstructionEtUnTravaux.html +++ b/packages/ui/src/components/titre.stories_snapshots_TitreAvecUnOctroiEnConstructionEtUnTravaux.html @@ -40,7 +40,16 @@ </div> <div class="fr-mb-3w fr-mt-3w" style="height: 1px; width: 100%; background-color: var(--grey-900-175);"></div> <!----> - <!----> + <div class="fr-pt-4w fr-pb-4w"> + <h2>Démarches</h2> + <div class="fr-alert fr-alert--warning"> + <p class="fr-alert__title fr-h4">Plusieurs démarches sans phase</p>Nous affichons toutes les démarches en mode dégradé, normalement, on ne peut avoir plusieurs démarches que si au moins l'une d'entre elle a une phase.<br><strong>Il faudrait très probablement que l'Octroi aille jusqu'au bout avant de créer d'autres démarches.</strong> + </div> + <ul> + <li><a href="/mocked-href" title="oct" aria-label="oct">Octroi</a></li> + <li><a href="/mocked-href" title="dam" aria-label="dam">Déclaration d'arrêt définitif des travaux</a></li> + </ul> + </div> <div> <div class="fr-grid-row fr-grid-row--middle"> <h2 style="margin: 0px;">Octroi</h2> diff --git a/packages/ui/src/components/titre.tsx b/packages/ui/src/components/titre.tsx index 93f2cea3da4bd81bf2f881bae6c61d7167160fec..311cb2146609afb99ce574f216209d90c9d6ef46 100644 --- a/packages/ui/src/components/titre.tsx +++ b/packages/ui/src/components/titre.tsx @@ -164,7 +164,11 @@ export const PureTitre = defineComponent<Props>(props => { let demarcheSlug = props.currentDemarcheSlug - if ((isNullOrUndefinedOrEmpty(demarcheSlug) || isNullOrUndefined(data.demarches.find(({ slug }) => slug === props.currentDemarcheSlug))) && phases.value.length > 0) { + if ( + (isNullOrUndefinedOrEmpty(demarcheSlug) || isNullOrUndefined(data.demarches.find(({ slug }) => slug === props.currentDemarcheSlug))) && + !('erreur' in phases.value) && + phases.value.length > 0 + ) { demarcheSlug = phases.value[phases.value.length - 1][phases.value[phases.value.length - 1].length - 1].slug } @@ -324,7 +328,7 @@ export const PureTitre = defineComponent<Props>(props => { } const hasNoPhases = computed<boolean>(() => { - return phases.value.length === 0 + return !('erreur' in phases.value) && phases.value.length === 0 }) const addDemarchePopup = ref<boolean>(false) diff --git a/packages/ui/src/components/titre/phase.test.ts b/packages/ui/src/components/titre/phase.test.ts index aa2e9c16c0b77250894be67e0e17c1e1e27b37e1..7d5269c6f5ca4b4fc0964de9f805533bd7b9f636 100644 --- a/packages/ui/src/components/titre/phase.test.ts +++ b/packages/ui/src/components/titre/phase.test.ts @@ -5,6 +5,62 @@ import { demarcheIdValidator, demarcheSlugValidator } from 'camino-common/src/de import { TitreGetDemarche } from 'camino-common/src/titres' import { ETAPE_IS_NOT_BROUILLON, etapeIdValidator, etapeSlugValidator } from 'camino-common/src/etape' +test('phase en erreur', () => { + const demarches: TitreGetDemarche[] = [ + { + id: demarcheIdValidator.parse('idMut'), + slug: demarcheSlugValidator.parse('slug-mut'), + description: null, + etapes: [], + demarche_type_id: 'mut', + demarche_statut_id: 'acp', + demarche_date_debut: null, + demarche_date_fin: null, + ordre: 1, + }, + { + id: demarcheIdValidator.parse('idDemPro'), + slug: demarcheSlugValidator.parse('dem-slug-pro'), + description: null, + etapes: [], + demarche_type_id: 'pro', + demarche_statut_id: 'acp', + demarche_date_debut: null, + demarche_date_fin: null, + ordre: 2, + }, + ] + const actual = phaseWithAlterations(demarches, toCaminoDate('2024-07-11')) + expect(actual).toMatchInlineSnapshot(` + { + "demarches": [ + { + "demarche_date_debut": null, + "demarche_date_fin": null, + "demarche_statut_id": "acp", + "demarche_type_id": "mut", + "description": null, + "etapes": [], + "id": "idMut", + "ordre": 1, + "slug": "slug-mut", + }, + { + "demarche_date_debut": null, + "demarche_date_fin": null, + "demarche_statut_id": "acp", + "demarche_type_id": "pro", + "description": null, + "etapes": [], + "id": "idDemPro", + "ordre": 2, + "slug": "dem-slug-pro", + }, + ], + "erreur": "plusieurs démarches sans phase", + } +`) +}) test('phase acceptée et publiée', () => { const demarches: TitreGetDemarche[] = [ { @@ -63,7 +119,6 @@ test('phase acceptée et publiée', () => { }, ] const actual = phaseWithAlterations(demarches, toCaminoDate('2024-07-11')) - expect(actual[0]).toHaveLength(2) expect(actual).toMatchInlineSnapshot(` [ [ diff --git a/packages/ui/src/components/titre/phase.ts b/packages/ui/src/components/titre/phase.ts index b075f6623e59003658ffb8277573ca0db00cbd1c..5eb476c614b0b0580c0e981a44904bec11eff45e 100644 --- a/packages/ui/src/components/titre/phase.ts +++ b/packages/ui/src/components/titre/phase.ts @@ -14,7 +14,9 @@ type PhaseWithDateDebut = OmitDistributive<TitreGetDemarche, 'demarche_date_debu type DemarcheAlteration = TitreGetDemarche & { date_etape_decision_ok: CaminoDate; events: TitreTimelineEvents[] } -export type PhaseWithAlterations = [PhaseWithDateDebut, ...DemarcheAlteration[]][] | [[OmitDistributive<TitreGetDemarche, 'demarche_date_debut'> & { demarche_date_debut: null }]] +const erreurPlusieursDemarches = 'plusieurs démarches sans phase' as const +export type PhaseErreurs = { erreur: string; demarches: Pick<TitreGetDemarche, 'slug' | 'demarche_type_id'>[] } +export type PhaseWithAlterations = [PhaseWithDateDebut, ...DemarcheAlteration[]][] | [[OmitDistributive<TitreGetDemarche, 'demarche_date_debut'> & { demarche_date_debut: null }]] | PhaseErreurs export const phaseWithAlterations = (demarches: TitreGetDemarche[], currentDate: CaminoDate): PhaseWithAlterations => { if (isNullOrUndefinedOrEmpty(demarches)) { return [] @@ -27,7 +29,10 @@ export const phaseWithAlterations = (demarches: TitreGetDemarche[], currentDate: const demarchesUsed: DemarcheSlug[] = simplePhases.map(({ slug }) => slug) if (isNullOrUndefinedOrEmpty(simplePhases)) { if (demarches.length > 1) { - console.error('Le titre a plusieurs démarches sans phase') + return { + erreur: erreurPlusieursDemarches, + demarches, + } } return [[{ ...demarches[0], demarche_date_debut: null }]] diff --git a/packages/ui/src/components/titre/titre-timeline.stories.tsx b/packages/ui/src/components/titre/titre-timeline.stories.tsx index 7cacdd4810c740290810d2c170a1a68328c653d5..1d5ca401a2429c070d28755007b5e907ff82d6f9 100644 --- a/packages/ui/src/components/titre/titre-timeline.stories.tsx +++ b/packages/ui/src/components/titre/titre-timeline.stories.tsx @@ -3,6 +3,7 @@ import { demarcheSlugValidator } from 'camino-common/src/demarche' import { toCaminoDate } from 'camino-common/src/date' import { Phase, TitreTimeline } from './titre-timeline' import { titreSlugValidator } from 'camino-common/src/validators/titres' +import { DEMARCHES_TYPES_IDS } from 'camino-common/src/static/demarchesTypes' const meta: Meta = { title: 'Components/Titre/Timeline', @@ -261,6 +262,22 @@ export const OneDemarcheNoPhase: StoryFn = () => ( </div> ) +export const MultipleDemarcheNoPhase: StoryFn = () => ( + <div> + <TitreTimeline + titreSlug={titreSlugValidator.parse('slug-titre')} + phasesWithAlterations={{ + erreur: 'plusieurs démarches sans phase', + demarches: [ + { slug: demarcheSlugValidator.parse('slug-id1'), demarche_type_id: DEMARCHES_TYPES_IDS.Octroi }, + { slug: demarcheSlugValidator.parse('slug-id2'), demarche_type_id: DEMARCHES_TYPES_IDS.Prolongation }, + ], + }} + currentDemarcheSlug={demarcheSlugValidator.parse('slug-demarche2')} + /> + </div> +) + export const NoDemarcheNoPhase: StoryFn = () => ( <div> <TitreTimeline titreSlug={titreSlugValidator.parse('slug-titre')} phasesWithAlterations={[]} currentDemarcheSlug={demarcheSlugValidator.parse('slug-demarche2')} /> diff --git a/packages/ui/src/components/titre/titre-timeline.stories_snapshots_MultipleDemarcheNoPhase.html b/packages/ui/src/components/titre/titre-timeline.stories_snapshots_MultipleDemarcheNoPhase.html new file mode 100644 index 0000000000000000000000000000000000000000..a6e862c04f44848460f6617672c80fdf3af197a0 --- /dev/null +++ b/packages/ui/src/components/titre/titre-timeline.stories_snapshots_MultipleDemarcheNoPhase.html @@ -0,0 +1,12 @@ +<div> + <div> + <h2>Démarches</h2> + <div class="fr-alert fr-alert--warning"> + <p class="fr-alert__title fr-h4">Plusieurs démarches sans phase</p>Nous affichons toutes les démarches en mode dégradé, normalement, on ne peut avoir plusieurs démarches que si au moins l'une d'entre elle a une phase.<br><strong>Il faudrait très probablement que l'Octroi aille jusqu'au bout avant de créer d'autres démarches.</strong> + </div> + <ul> + <li><a href="/mocked-href" title="oct" aria-label="oct">Octroi</a></li> + <li><a href="/mocked-href" title="pro" aria-label="pro">Prolongation</a></li> + </ul> + </div> +</div> \ No newline at end of file diff --git a/packages/ui/src/components/titre/titre-timeline.tsx b/packages/ui/src/components/titre/titre-timeline.tsx index 78a7d348e3730e55fc6eadb640fa2c705fe9816e..dd971d593b4c6a01de739bbc999be3c39502b9b5 100644 --- a/packages/ui/src/components/titre/titre-timeline.tsx +++ b/packages/ui/src/components/titre/titre-timeline.tsx @@ -11,13 +11,15 @@ import { TitreSlug } from 'camino-common/src/validators/titres' import { TravauxIcone } from './travaux-icone' import { DsfrSeparator } from '../_ui/dsfr-separator' import { capitalize } from 'camino-common/src/strings' +import { PhaseErreurs } from './phase' +import { Alert } from '../_ui/alert' type NoPhase = [[Pick<PhaseWithDateDebut, 'slug' | 'demarche_type_id'> & { demarche_date_debut: null }]] export type Phase = [PhaseWithDateDebut, ...DemarcheAlteration[]][] type TitreTimelineEvents = Pick<TitreGetDemarche, 'slug' | 'demarche_type_id'> & { first_etape_date: CaminoDate | null } type Props = { titreSlug: TitreSlug - phasesWithAlterations: Phase | NoPhase + phasesWithAlterations: Phase | NoPhase | PhaseErreurs currentDemarcheSlug: DemarcheSlug class?: HTMLAttributes['class'] } @@ -39,6 +41,33 @@ const isNoPhase = (phase: Phase | NoPhase): phase is NoPhase => { const minWidth = 200 export const TitreTimeline: FunctionalComponent<Props> = props => { + if ('erreur' in props.phasesWithAlterations) { + return ( + <div> + <h2>Démarches</h2> + <Alert + type="warning" + title={'Plusieurs démarches sans phase'} + description={ + <> + Nous affichons toutes les démarches en mode dégradé, normalement, on ne peut avoir plusieurs démarches que si au moins l'une d'entre elle a une phase. + <br /> + <strong>Il faudrait très probablement que l'Octroi aille jusqu'au bout avant de créer d'autres démarches.</strong> + </> + } + /> + <ul> + {props.phasesWithAlterations.demarches.map(demarche => ( + <li> + <CaminoRouterLink isDisabled={false} to={{ name: 'titre', params: { id: props.titreSlug }, query: { demarcheSlug: demarche.slug } }} title={demarche.demarche_type_id}> + {capitalize(DemarchesTypes[demarche.demarche_type_id].nom)} + </CaminoRouterLink> + </li> + ))} + </ul> + </div> + ) + } if (props.phasesWithAlterations.length === 0 || isNoPhase(props.phasesWithAlterations)) { return null } @@ -154,7 +183,7 @@ export const TitreTimeline: FunctionalComponent<Props> = props => { </Fragment> ))} </div> - {index !== props.phasesWithAlterations.length - 1 ? <div style={{ border: '2px solid black' }}></div> : null} + {!('erreur' in props.phasesWithAlterations) && index !== props.phasesWithAlterations.length - 1 ? <div style={{ border: '2px solid black' }}></div> : null} </Fragment> ))} </div>