diff --git a/frontend/cypress/e2e/side_window/mission_form/action_list.spec.ts b/frontend/cypress/e2e/side_window/mission_form/action_list.spec.ts index 696f731ffd..f99878957e 100644 --- a/frontend/cypress/e2e/side_window/mission_form/action_list.spec.ts +++ b/frontend/cypress/e2e/side_window/mission_form/action_list.spec.ts @@ -156,5 +156,9 @@ context('Side Window > Mission Form > Action List', () => { cy.get('.Element-Tag').contains('2 INF AVEC PV').should('be.visible') cy.get('.Element-Tag').contains('1 INF EN ATTENTE').should('be.visible') cy.get('.Element-Tag').contains('3 NATINF: 23581, 23588, 23584').should('be.visible') + // The infractions label from natinfs should be rendered + cy.get( + '[title="23581 - Taille de maille non réglementaire, 23588 - Chalutage dans la zone des 3 milles, 23584 - Défaut AIS"]' + ).should('exist') }) }) diff --git a/frontend/cypress/e2e/side_window/mission_form/sea_control.spec.ts b/frontend/cypress/e2e/side_window/mission_form/sea_control.spec.ts index 078a70cb7f..58bf26e7f3 100644 --- a/frontend/cypress/e2e/side_window/mission_form/sea_control.spec.ts +++ b/frontend/cypress/e2e/side_window/mission_form/sea_control.spec.ts @@ -498,7 +498,8 @@ context('Side Window > Mission Form > Sea Control', () => { cy.contains('Infraction obligations déclaratives et autorisations 1').should('exist') cy.contains('Avec PV').should('exist') - cy.contains('NATINF : 23581').should('exist') + cy.contains('NATINF : 23581 - Taille de maille non réglementaire').should('exist') + cy.contains("Une observation sur l'infraction").should('exist') // ------------------------------------------------------------------------- @@ -519,7 +520,7 @@ context('Side Window > Mission Form > Sea Control', () => { cy.contains('Infraction obligations déclaratives et autorisations 1').should('exist') cy.contains('Sans PV').should('exist') - cy.contains('NATINF : 23588').should('exist') + cy.contains('NATINF : 23588 - Chalutage dans la zone des 3 milles').should('exist') cy.contains("Une autre observation sur l'infraction").should('exist') // ------------------------------------------------------------------------- diff --git a/frontend/cypress/e2e/vessel_sidebar/controls.spec.ts b/frontend/cypress/e2e/vessel_sidebar/controls.spec.ts index f9ea39ecc0..54472d3093 100644 --- a/frontend/cypress/e2e/vessel_sidebar/controls.spec.ts +++ b/frontend/cypress/e2e/vessel_sidebar/controls.spec.ts @@ -46,6 +46,10 @@ context('Vessel sidebar controls tab', () => { // Check the control content displayed cy.get('*[data-cy="vessel-control-title"]').first().contains(`CONTRÔLE EN MER DU ${date}`) + + // The infractions label from natinfs should be rendered + cy.get('[title="23584 - Défaut AIS"]').should('exist') + cy.get('*[data-cy="vessel-control"]') .first() .should('contain', 'Appréhension espèce') diff --git a/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/Infraction.tsx b/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/Infraction.tsx index 277ea0372f..c61e1f24c7 100644 --- a/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/Infraction.tsx +++ b/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/Infraction.tsx @@ -7,7 +7,7 @@ import { MissionAction } from '../../../../../../domain/types/missionAction' import type { Promisable } from 'type-fest' export type InfractionProps = { - data: AnyInfraction + data: AnyInfraction & { label: string | undefined } index: number label: string | undefined onDelete: (index: number) => Promisable @@ -31,7 +31,7 @@ export function Infraction( {MissionAction.INFRACTION_TYPE_LABEL[data.infractionType]} {data.infractionType !== MissionAction.InfractionType.PENDING && ( - NATINF : {data.natinf} + NATINF : {data.label || data.natinf} )} diff --git a/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/index.tsx b/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/index.tsx index 77e67ef33d..7a2f271864 100644 --- a/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/index.tsx +++ b/frontend/src/features/SideWindow/MissionForm/ActionForm/shared/FormikMultiInfractionPicker/index.tsx @@ -1,20 +1,21 @@ import { Accent, Button, FormikTextarea, Icon } from '@mtes-mct/monitor-ui' import { useField } from 'formik' +import { find } from 'lodash' import { remove as ramdaRemove, update as ramdaUpdate } from 'ramda' import { Fragment, useCallback, useMemo, useState } from 'react' import styled from 'styled-components' import { Infraction } from './Infraction' import { InfractionForm } from './InfractionForm' -import { useGetInfractionsQuery } from '../../../../../../api/infraction' import { FrontendError } from '../../../../../../libs/FrontendError' import { FrontendErrorBoundary } from '../../../../../../ui/FrontendErrorBoundary' +import { useGetNatinfsAsOptions } from '../../../hooks/useGetNatinfsAsOptions' import { FieldsetGroup, FieldsetGroupSpinner } from '../../../shared/FieldsetGroup' import { FieldsetGroupSeparator } from '../../../shared/FieldsetGroupSeparator' import type { MissionAction } from '../../../../../../domain/types/missionAction' import type { MissionActionFormValues } from '../../../types' -import type { Option, FormikTextareaProps } from '@mtes-mct/monitor-ui' +import type { FormikTextareaProps } from '@mtes-mct/monitor-ui' import type { ReactNode } from 'react' export type FormikMultiInfractionPickerProps = { @@ -40,18 +41,19 @@ export function FormikMultiInfractionPicker({ const [editedIndex, setEditedIndex] = useState(undefined) const [isNewInfractionFormOpen, setIsNewInfractionFormOpen] = useState(false) - const getInfractionsApiQuery = useGetInfractionsQuery() + const natinfsAsOptions = useGetNatinfsAsOptions() - const natinfsAsOptions: Option[] = useMemo(() => { - if (!getInfractionsApiQuery.data) { + const infractionsWithLabel = useMemo(() => { + if (!input.value) { return [] } - return getInfractionsApiQuery.data.map(({ infraction, natinfCode }) => ({ - label: `${natinfCode} - ${infraction}`, - value: Number(natinfCode) - })) - }, [getInfractionsApiQuery.data]) + return input.value.map(infraction => { + const nextInfractionLabel = find(natinfsAsOptions, { value: infraction.natinf })?.label + + return { ...infraction, label: nextInfractionLabel } + }) + }, [input.value, natinfsAsOptions]) const closeInfractionForm = useCallback(() => { setEditedIndex(undefined) @@ -140,9 +142,9 @@ export function FormikMultiInfractionPicker({ {addButtonLabel} - {input.value && input.value.length > 0 && ( + {infractionsWithLabel.length > 0 && ( - {input.value.map((infraction, index) => ( + {infractionsWithLabel.map((infraction, index) => ( // eslint-disable-next-line react/no-array-index-key diff --git a/frontend/src/features/SideWindow/MissionForm/ActionList/Item.tsx b/frontend/src/features/SideWindow/MissionForm/ActionList/Item.tsx index 16626d2a54..6d1129a706 100644 --- a/frontend/src/features/SideWindow/MissionForm/ActionList/Item.tsx +++ b/frontend/src/features/SideWindow/MissionForm/ActionList/Item.tsx @@ -1,14 +1,15 @@ import { Accent, + FieldError, getLocalizedDayjs, Icon, IconButton, Tag, - TagGroup, - THEME, TagBullet, - FieldError + TagGroup, + THEME } from '@mtes-mct/monitor-ui' +import { find } from 'lodash' import { useMemo } from 'react' import styled, { css } from 'styled-components' @@ -17,6 +18,7 @@ import { UNKNOWN_VESSEL } from '../../../../domain/entities/vessel/vessel' import { MissionAction } from '../../../../domain/types/missionAction' import { useMainAppSelector } from '../../../../hooks/useMainAppSelector' import { FrontendError } from '../../../../libs/FrontendError' +import { useGetNatinfsAsOptions } from '../hooks/useGetNatinfsAsOptions' import type { MissionActionFormValues } from '../types' import type { Promisable } from 'type-fest' @@ -31,6 +33,8 @@ export type ItemProps = { export function Item({ initialValues, isSelected, onDuplicate, onRemove, onSelect }: ItemProps) { const mission = useMainAppSelector(state => state.mission) + const natinfsAsOptions = useGetNatinfsAsOptions() + const [actionLabel, ActionIcon] = useMemo(() => { const vesselName = initialValues.vesselName === UNKNOWN_VESSEL.vesselName ? 'INCONNU' : initialValues.vesselName @@ -75,18 +79,29 @@ export function Item({ initialValues, isSelected, onDuplicate, onRemove, onSelec ) const infractionsNatinfs = nonPendingInfractions.map(({ natinf }) => natinf) - return [ + const infractionsRecapTags = [ ...(withRecordInfractions.length > 0 ? [`${withRecordInfractions.length} INF AVEC PV`] : []), - ...(pendingInfractions.length > 0 ? [`${pendingInfractions.length} INF EN ATTENTE`] : []), - ...(infractionsNatinfs.length > 0 - ? [`${infractionsNatinfs.length} NATINF: ${infractionsNatinfs.join(', ')}`] - : []) + ...(pendingInfractions.length > 0 ? [`${pendingInfractions.length} INF EN ATTENTE`] : []) ].map(label => ( {label} )) - }, [initialValues]) + + const infractionsTitle = infractionsNatinfs.map(natinf => { + const infractionLabel = find(natinfsAsOptions, { value: natinf })?.label + + return infractionLabel || natinf.toString() + }) + const infractionsLabel = `${infractionsNatinfs.length} NATINF: ${infractionsNatinfs.join(', ')}` + const infractionsTag = ( + + {infractionsLabel} + + ) + + return [...infractionsRecapTags, infractionsTag] + }, [initialValues, natinfsAsOptions]) const redTags = useMemo( () => diff --git a/frontend/src/features/SideWindow/MissionForm/hooks/useGetNatinfsAsOptions.ts b/frontend/src/features/SideWindow/MissionForm/hooks/useGetNatinfsAsOptions.ts new file mode 100644 index 0000000000..d175251402 --- /dev/null +++ b/frontend/src/features/SideWindow/MissionForm/hooks/useGetNatinfsAsOptions.ts @@ -0,0 +1,22 @@ +import { useMemo } from 'react' + +import { useGetInfractionsQuery } from '../../../../api/infraction' + +import type { Option } from '@mtes-mct/monitor-ui' + +export function useGetNatinfsAsOptions() { + const getInfractionsApiQuery = useGetInfractionsQuery() + + const natinfsAsOptions: Option[] = useMemo(() => { + if (!getInfractionsApiQuery.data) { + return [] + } + + return getInfractionsApiQuery.data.map(({ infraction, natinfCode }) => ({ + label: `${natinfCode} - ${infraction}`, + value: Number(natinfCode) + })) + }, [getInfractionsApiQuery.data]) + + return natinfsAsOptions +} diff --git a/frontend/src/features/VesselSidebar/Controls/Infraction.tsx b/frontend/src/features/VesselSidebar/Controls/Infraction.tsx index 67429a6e9f..af0be5f811 100644 --- a/frontend/src/features/VesselSidebar/Controls/Infraction.tsx +++ b/frontend/src/features/VesselSidebar/Controls/Infraction.tsx @@ -1,8 +1,10 @@ +import { find } from 'lodash' import { useMemo } from 'react' import styled from 'styled-components' import { COLORS } from '../../../constants/constants' import { MissionAction } from '../../../domain/types/missionAction' +import { useGetNatinfsAsOptions } from '../../SideWindow/MissionForm/hooks/useGetNatinfsAsOptions' type InfractionProps = { index: number @@ -14,6 +16,14 @@ type InfractionProps = { infractionDomain: MissionAction.InfractionDomain } export function Infraction({ index, infraction, infractionDomain }: InfractionProps) { + const natinfsAsOptions = useGetNatinfsAsOptions() + + const infractionWithLabel = useMemo(() => { + const infractionLabel = find(natinfsAsOptions, { value: infraction.natinf })?.label + + return { ...infraction, infractionLabel } + }, [natinfsAsOptions, infraction]) + const infractionDomainText = useMemo(() => { switch (infractionDomain) { case MissionAction.InfractionDomain.GEAR: @@ -50,7 +60,9 @@ export function Infraction({ index, infraction, infractionDomain }: InfractionPr - NATINF {infraction.natinf} + + NATINF {infractionWithLabel.natinf} + )