From 659bf7d938cf8cf7c660118716bf2a0531986db7 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 20 May 2024 10:58:40 +0200 Subject: [PATCH 1/3] Do not update mission zone when another action is newer --- .../MissionForm/ActionList/EnvActionCard.tsx | 2 +- .../components/MissionForm/formikUsecases.ts | 213 ------------------ .../hooks/useGetMainFormFormikUsecases.ts | 9 +- .../useGetMissionActionFormikUsecases.ts | 23 +- .../__tests__/__mocks__/dummyAction.ts | 86 +++++++ .../__tests__/updateMissionLocation.test.ts | 55 +++++ .../components/MissionForm/useCases/index.ts | 17 ++ .../useCases/initMissionLocation.ts | 16 ++ .../MissionForm/useCases/updateFAOAreas.ts | 20 ++ .../useCases/updateGearsOnboard.ts | 56 +++++ .../useCases/updateMissionLocation.ts | 63 ++++++ .../useCases/updateOtherControlsCheckboxes.ts | 20 ++ .../MissionForm/useCases/updateSegments.ts | 28 +++ .../useCases/updateSpeciesOnboard.ts | 41 ++++ .../Mission/useCases/deleteMissionAction.ts | 27 ++- 15 files changed, 439 insertions(+), 237 deletions(-) delete mode 100644 frontend/src/features/Mission/components/MissionForm/formikUsecases.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/index.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts create mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts diff --git a/frontend/src/features/Mission/components/MissionForm/ActionList/EnvActionCard.tsx b/frontend/src/features/Mission/components/MissionForm/ActionList/EnvActionCard.tsx index ab177f1f61..dd77323e7c 100644 --- a/frontend/src/features/Mission/components/MissionForm/ActionList/EnvActionCard.tsx +++ b/frontend/src/features/Mission/components/MissionForm/ActionList/EnvActionCard.tsx @@ -23,7 +23,7 @@ export function EnvActionCard({ missionAction }: EnvActionCardProps) { ] case EnvMissionAction.MissionActionType.NOTE: - return [{missionAction.observations}, Icon.Note] + return [{missionAction.observations}, Icon.Note] case EnvMissionAction.MissionActionType.SURVEILLANCE: return [ diff --git a/frontend/src/features/Mission/components/MissionForm/formikUsecases.ts b/frontend/src/features/Mission/components/MissionForm/formikUsecases.ts deleted file mode 100644 index 4d42a64df8..0000000000 --- a/frontend/src/features/Mission/components/MissionForm/formikUsecases.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { faoAreasApi } from '@api/faoAreas' -import { getSummedSpeciesOnBoard } from '@features/Logbook/utils' -import { missionFormActions } from '@features/Mission/components/MissionForm/slice' -import { EnvMissionAction } from '@features/Mission/envMissionAction.types' -import { MissionAction } from '@features/Mission/missionAction.types' -import { getLastControlCircleGeometry } from '@features/Mission/useCases/getLastControlCircleGeometry' -import { vesselApi } from '@features/Vessel/apis' -import { FrontendError } from '@libs/FrontendError' -import { getFleetSegments } from 'domain/use_cases/vessel/getFleetSegments' -import { MultiPolygon } from 'ol/geom' - -import { PAMControlUnitIds } from './constants' -import { convertToGeoJSONGeometryObject } from '../../../../domain/entities/layers' - -import type { MissionActionFormValues, MissionMainFormValues } from './types' -import type { Option } from '@mtes-mct/monitor-ui' -import type { MainRootState } from '@store' -import type { RiskFactor } from 'domain/entities/vessel/riskFactor/types' -import type { Gear } from 'domain/types/Gear' -import type { GeoJSON } from 'domain/types/GeoJSON' -import type { Port } from 'domain/types/port' -import type { AnyAction } from 'redux' -import type { ThunkDispatch } from 'redux-thunk' - -const updateSegments = - ( - dispatch, - setFieldValue: (field: string, value: any) => void, - fleetSegmentsAsOptions: Option[] - ) => - async (missionAction: MissionActionFormValues) => { - if (missionAction.actionType === MissionAction.MissionActionType.AIR_CONTROL) { - return - } - - const computedFleetSegments = await dispatch( - getFleetSegments(missionAction.faoAreas, missionAction.gearOnboard, missionAction.speciesOnboard) - ) - - const nextFleetSegments = fleetSegmentsAsOptions - .filter(({ value }) => computedFleetSegments?.find(fleetSegment => fleetSegment.segment === value.segment)) - .map(({ value }) => value) - - setFieldValue('segments', nextFleetSegments) - } - -const updateFAOAreas = - (dispatch, setFieldValue: (field: string, value: any) => void) => - async (missionAction: MissionActionFormValues): Promise => { - const { data: computedVesselFaoAreas } = await dispatch( - faoAreasApi.endpoints.computeVesselFaoAreas.initiate({ - internalReferenceNumber: missionAction.internalReferenceNumber, - latitude: missionAction.latitude, - longitude: missionAction.longitude, - portLocode: missionAction.portLocode - }) - ) - - setFieldValue('faoAreas', computedVesselFaoAreas) - - return computedVesselFaoAreas - } - -const updateGearsOnboard = - ( - dispatch: ThunkDispatch, - setFieldValue: (field: string, value: any) => void, - gearsByCode: Record | undefined - ) => - async (missionAction: MissionActionFormValues): Promise => { - if (!gearsByCode || !missionAction.internalReferenceNumber) { - return [] - } - - const { data: riskFactor } = await dispatch( - vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) - ) - if (!riskFactor) { - return [] - } - - const { gearOnboard } = riskFactor - if (!gearOnboard?.length) { - return [] - } - - const nextGears = gearOnboard - .map(gear => { - const gearByCode = gearsByCode[gear.gear] - if (!gearByCode) { - throw new FrontendError('`gearByCode` is undefined.') - } - - return { ...gearByCode, declaredMesh: gear.mesh } - }) - .map(gear => ({ - comments: undefined, - controlledMesh: undefined, - declaredMesh: gear.declaredMesh, - gearCode: gear.code, - gearName: gear.name, - gearWasControlled: undefined, - hasUncontrolledMesh: false - })) - - setFieldValue('gearOnboard', nextGears) - - return nextGears - } - -const updateSpeciesOnboard = - (dispatch, setFieldValue: (field: string, value: any) => void) => - async (missionAction: MissionActionFormValues): Promise => { - if (!missionAction.internalReferenceNumber) { - return [] - } - - const { data: riskFactor } = await dispatch( - vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) - ) - if (!riskFactor) { - return [] - } - - const { speciesOnboard } = riskFactor as RiskFactor - if (!speciesOnboard?.length) { - return [] - } - - const summedSpeciesOnboard = getSummedSpeciesOnBoard(speciesOnboard) - const nextSpeciesOnboard = summedSpeciesOnboard - .sort((a, b) => b.weight - a.weight) - .map(specy => ({ - controlledWeight: undefined, - declaredWeight: specy.weight, - nbFish: undefined, - speciesCode: specy.species, - underSized: false - })) - - setFieldValue('speciesOnboard', nextSpeciesOnboard) - - return nextSpeciesOnboard - } - -const updateMissionLocation = - (dispatch, ports: Port.Port[] | undefined, envActions: EnvMissionAction.MissionAction[]) => - async ( - isGeometryComputedFromControls: boolean | undefined, - missionAction: MissionActionFormValues | MissionAction.MissionAction | undefined - ) => { - if (!missionAction || !ports || !isGeometryComputedFromControls) { - return - } - - const lastEnvActionDate = envActions - .filter( - action => - action.actionType === EnvMissionAction.MissionActionType.CONTROL || - action.actionType === EnvMissionAction.MissionActionType.SURVEILLANCE - ) - .map(action => action.actionStartDateTimeUtc) - .filter((actionStartDateTimeUtc): actionStartDateTimeUtc is string => actionStartDateTimeUtc !== null) - .sort((a, b) => b.localeCompare(a))[0] - - if (lastEnvActionDate && lastEnvActionDate > missionAction.actionDatetimeUtc) { - // As a action from Env is newer, we do not update the mission location - return - } - - const nextMissionGeometry = await dispatch(getLastControlCircleGeometry(ports, missionAction)) - if (!nextMissionGeometry) { - return - } - - dispatch(missionFormActions.setGeometryComputedFromControls(nextMissionGeometry)) - } - -const initMissionLocation = dispatch => async (isGeometryComputedFromControls: boolean | undefined) => { - if (!isGeometryComputedFromControls) { - return - } - - const emptyMissionGeometry = convertToGeoJSONGeometryObject(new MultiPolygon([])) as GeoJSON.MultiPolygon - - dispatch(missionFormActions.setGeometryComputedFromControls(emptyMissionGeometry)) -} - -const updateOtherControlsCheckboxes = - dispatch => async (mission: MissionMainFormValues, previousIsControlUnitPAM: boolean) => { - const isControlUnitPAM = mission.controlUnits?.some( - controlUnit => controlUnit.id && PAMControlUnitIds.includes(controlUnit.id) - ) - - /** - * If a PAM was already in the control units, we do not reset the other controls - */ - if (previousIsControlUnitPAM && isControlUnitPAM) { - return - } - - dispatch(missionFormActions.mustResetOtherControlsCheckboxes(true)) - } - -export const formikUsecase = { - initMissionLocation, - updateFAOAreas, - updateGearsOnboard, - updateMissionLocation, - updateOtherControlsCheckboxes, - updateSegments, - updateSpeciesOnboard -} diff --git a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts index c511f86897..2a09e8c5d0 100644 --- a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts +++ b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts @@ -6,7 +6,7 @@ import { useMainAppSelector } from '@hooks/useMainAppSelector' import { skipToken } from '@reduxjs/toolkit/query' import { sortBy } from 'lodash' -import { formikUsecase } from '../formikUsecases' +import { formUsecase } from '../useCases' import type { MissionMainFormValues } from '../types' @@ -22,7 +22,7 @@ export function useGetMainFormFormikUsecases() { * When updating a control unit, we must reset the "Other controls" field checkboxes */ updateMissionActionOtherControlsCheckboxes: (mission: MissionMainFormValues, previousIsControlUnitPAM: boolean) => - formikUsecase.updateOtherControlsCheckboxes(dispatch)(mission, previousIsControlUnitPAM), + formUsecase.updateOtherControlsCheckboxes(dispatch)(mission, previousIsControlUnitPAM), /** * When updating the mission location from the mission, we use the `RTK-Query` cache object to access the `missionAction` form. @@ -53,10 +53,11 @@ export function useGetMainFormFormikUsecases() { return false } - await formikUsecase.updateMissionLocation( + await formUsecase.updateMissionLocation( dispatch, getPortsApiQuery.data, - getMissionApiQuery.data?.envActions ?? [] + getMissionApiQuery.data?.envActions ?? [], + getMissionApiQuery.data?.actions ?? [] )(isGeometryComputedFromControls, lastControl) return true diff --git a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts index f33870f7fc..af12bc0773 100644 --- a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts +++ b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts @@ -9,7 +9,7 @@ import { useFormikContext } from 'formik' import { useMemo } from 'react' import { getFleetSegmentsAsOption } from '../ActionForm/shared/utils' -import { formikUsecase } from '../formikUsecases' +import { formUsecase } from '../useCases' import type { MissionActionFormValues } from '../types' import type { Option } from '@mtes-mct/monitor-ui' @@ -32,15 +32,15 @@ export function useGetMissionActionFormikUsecases() { ) const updateSegments = (missionActionValues: MissionActionFormValues) => - formikUsecase.updateSegments(dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions)(missionActionValues) + formUsecase.updateSegments(dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions)(missionActionValues) /** * Update FAO Areas and segments from the control coordinates or port input */ async function updateFAOAreasAndSegments(missionActionValues: MissionActionFormValues) { - const faoAreas = await formikUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) + const faoAreas = await formUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) - await formikUsecase.updateSegments( + await formUsecase.updateSegments( dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions @@ -62,20 +62,20 @@ export function useGetMissionActionFormikUsecases() { return } - const gearOnboard = await formikUsecase.updateGearsOnboard( + const gearOnboard = await formUsecase.updateGearsOnboard( dispatch, setMissionActionFieldValue, gearsByCode )(missionActionValues) - const speciesOnboard = await formikUsecase.updateSpeciesOnboard( + const speciesOnboard = await formUsecase.updateSpeciesOnboard( dispatch, setMissionActionFieldValue )(missionActionValues) - const faoAreas = await formikUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) + const faoAreas = await formUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) - await formikUsecase.updateSegments( + await formUsecase.updateSegments( dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions @@ -92,17 +92,18 @@ export function useGetMissionActionFormikUsecases() { * The mission location is equal to the current action geometry modified. */ const updateMissionLocation = (missionActionValues: MissionActionFormValues) => - formikUsecase.updateMissionLocation( + formUsecase.updateMissionLocation( dispatch, getPortsApiQuery.data, - getMissionApiQuery.data?.envActions ?? [] + getMissionApiQuery.data?.envActions ?? [], + getMissionApiQuery.data?.actions ?? [] )(draft?.mainFormValues.isGeometryComputedFromControls, missionActionValues) /** * When updating the mission location from an action, we use the `RTK-Query` cache object to access the `mission` form. */ const initMissionLocation = () => - formikUsecase.initMissionLocation(dispatch)(draft?.mainFormValues.isGeometryComputedFromControls) + formUsecase.initMissionLocation(dispatch)(draft?.mainFormValues.isGeometryComputedFromControls) return { initMissionLocation, diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction.ts b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction.ts new file mode 100644 index 0000000000..b25bb5de1c --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction.ts @@ -0,0 +1,86 @@ +import { MissionAction } from '@features/Mission/missionAction.types' + +import MissionActionType = MissionAction.MissionActionType +import CompletionStatus = MissionAction.CompletionStatus + +export const dummyAction = { + actionDatetimeUtc: '2023-12-08T08:27:00Z', + actionType: MissionActionType.SEA_CONTROL, + completedBy: undefined, + completion: CompletionStatus.COMPLETED, + controlQualityComments: undefined, + controlUnits: [], + districtCode: 'AY', + emitsAis: undefined, + emitsVms: undefined, + externalReferenceNumber: 'DONTSINK', + facade: 'NAMO', + faoAreas: ['27.8.b', '27.8.c'], + feedbackSheetRequired: false, + flagState: 'FR', + flightGoals: [], + gearInfractions: [], + gearOnboard: [ + { + comments: undefined, + controlledMesh: undefined, + declaredMesh: 70.0, + gearCode: 'OTB', + gearName: 'Chaluts de fond à panneaux', + gearWasControlled: undefined, + hasUncontrolledMesh: false + } + ], + hasSomeGearsSeized: false, + hasSomeSpeciesSeized: false, + id: 123456, + internalReferenceNumber: 'FAK000999999', + ircs: 'CALLME', + isAdministrativeControl: undefined, + isComplianceWithWaterRegulationsControl: undefined, + isFromPoseidon: false, + isSafetyEquipmentAndStandardsComplianceControl: undefined, + isSeafarersControl: undefined, + isValid: true, + latitude: 47.648401281163814, + licencesAndLogbookObservations: undefined, + licencesMatchActivity: undefined, + logbookInfractions: [], + logbookMatchesActivity: undefined, + longitude: -4.281934312813745, + missionId: 123, + numberOfVesselsFlownOver: undefined, + otherComments: undefined, + otherInfractions: [], + portLocode: undefined, + portName: undefined, + segments: [{ segment: 'SWW01/02/03', segmentName: 'Bottom trawls' }], + seizureAndDiversion: false, + seizureAndDiversionComments: undefined, + separateStowageOfPreservedSpecies: undefined, + speciesInfractions: [], + speciesObservations: undefined, + speciesOnboard: [ + { + controlledWeight: undefined, + declaredWeight: 471.2, + nbFish: undefined, + speciesCode: 'HKE', + underSized: false + }, + { + controlledWeight: undefined, + declaredWeight: 13.46, + nbFish: undefined, + speciesCode: 'BLI', + underSized: false + } + ], + speciesSizeControlled: undefined, + speciesWeightControlled: undefined, + unitWithoutOmegaGauge: false, + userTrigram: 'LT', + vesselId: 1, + vesselName: 'PHENOMENE', + vesselTargeted: undefined +} diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts new file mode 100644 index 0000000000..7e37130831 --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts @@ -0,0 +1,55 @@ +import { dummyAction } from '@features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction' +import { updateMissionLocation } from '@features/Mission/components/MissionForm/useCases/updateMissionLocation' +import { EnvMissionAction } from '@features/Mission/envMissionAction.types' +import { expect, jest } from '@jest/globals' +import { customDayjs } from '@mtes-mct/monitor-ui' + +const mockDispatch = jest.fn() + +describe('features/Mission/components/MissionForm/useCases.updateMissionLocation()', () => { + it('Should update the mission location When there is no other actions', () => { + // When + updateMissionLocation(mockDispatch, [], [], [])(true, dummyAction) + + // Then + expect(mockDispatch).toHaveBeenCalled() + }) + + it('Should update the mission location When there is a older action', () => { + // Given + const olderAction = { ...dummyAction, actionDatetimeUtc: '2018-12-08T08:27:00Z' } + + // When + updateMissionLocation(mockDispatch, [], [], [olderAction])(true, dummyAction) + + // Then + expect(mockDispatch).toHaveBeenCalled() + }) + + it('Should not update the mission location When there is a newer action', () => { + // Given + const newerAction = { ...dummyAction, actionDatetimeUtc: customDayjs().toISOString() } + + // When + updateMissionLocation(mockDispatch, [], [], [newerAction])(true, dummyAction) + + // Then + expect(mockDispatch).toHaveBeenCalledTimes(0) + }) + + it('Should not update the mission location When there is a newer Env action', () => { + // Given + const newerEnvAction = { + actionStartDateTimeUtc: customDayjs().toISOString(), + actionType: EnvMissionAction.MissionActionType.CONTROL, + id: 123, + observations: undefined + } + + // When + updateMissionLocation(mockDispatch, [], [newerEnvAction], [])(true, dummyAction) + + // Then + expect(mockDispatch).toHaveBeenCalledTimes(0) + }) +}) diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/index.ts b/frontend/src/features/Mission/components/MissionForm/useCases/index.ts new file mode 100644 index 0000000000..2550f12a40 --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/index.ts @@ -0,0 +1,17 @@ +import { initMissionLocation } from '@features/Mission/components/MissionForm/useCases/initMissionLocation' +import { updateFAOAreas } from '@features/Mission/components/MissionForm/useCases/updateFAOAreas' +import { updateGearsOnboard } from '@features/Mission/components/MissionForm/useCases/updateGearsOnboard' +import { updateMissionLocation } from '@features/Mission/components/MissionForm/useCases/updateMissionLocation' +import { updateOtherControlsCheckboxes } from '@features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes' +import { updateSegments } from '@features/Mission/components/MissionForm/useCases/updateSegments' +import { updateSpeciesOnboard } from '@features/Mission/components/MissionForm/useCases/updateSpeciesOnboard' + +export const formUsecase = { + initMissionLocation, + updateFAOAreas, + updateGearsOnboard, + updateMissionLocation, + updateOtherControlsCheckboxes, + updateSegments, + updateSpeciesOnboard +} diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts b/frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts new file mode 100644 index 0000000000..ce8328cb60 --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts @@ -0,0 +1,16 @@ +import { missionFormActions } from '@features/Mission/components/MissionForm/slice' +import { MultiPolygon } from 'ol/geom' + +import { convertToGeoJSONGeometryObject } from '../../../../../domain/entities/layers' + +import type { GeoJSON } from '../../../../../domain/types/GeoJSON' + +export const initMissionLocation = dispatch => async (isGeometryComputedFromControls: boolean | undefined) => { + if (!isGeometryComputedFromControls) { + return + } + + const emptyMissionGeometry = convertToGeoJSONGeometryObject(new MultiPolygon([])) as GeoJSON.MultiPolygon + + dispatch(missionFormActions.setGeometryComputedFromControls(emptyMissionGeometry)) +} diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts new file mode 100644 index 0000000000..6c002cb8ee --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts @@ -0,0 +1,20 @@ +import { faoAreasApi } from '@api/faoAreas' + +import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' + +export const updateFAOAreas = + (dispatch, setFieldValue: (field: string, value: any) => void) => + async (missionAction: MissionActionFormValues): Promise => { + const { data: computedVesselFaoAreas } = await dispatch( + faoAreasApi.endpoints.computeVesselFaoAreas.initiate({ + internalReferenceNumber: missionAction.internalReferenceNumber, + latitude: missionAction.latitude, + longitude: missionAction.longitude, + portLocode: missionAction.portLocode + }) + ) + + setFieldValue('faoAreas', computedVesselFaoAreas) + + return computedVesselFaoAreas + } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts new file mode 100644 index 0000000000..1f9e44e0f3 --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts @@ -0,0 +1,56 @@ +import { MissionAction } from '@features/Mission/missionAction.types' +import { vesselApi } from '@features/Vessel/apis' +import { FrontendError } from '@libs/FrontendError' + +import type { Gear } from '../../../../../domain/types/Gear' +import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' +import type { MainRootState } from '@store' +import type { AnyAction } from 'redux' +import type { ThunkDispatch } from 'redux-thunk' + +export const updateGearsOnboard = + ( + dispatch: ThunkDispatch, + setFieldValue: (field: string, value: any) => void, + gearsByCode: Record | undefined + ) => + async (missionAction: MissionActionFormValues): Promise => { + if (!gearsByCode || !missionAction.internalReferenceNumber) { + return [] + } + + const { data: riskFactor } = await dispatch( + vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) + ) + if (!riskFactor) { + return [] + } + + const { gearOnboard } = riskFactor + if (!gearOnboard?.length) { + return [] + } + + const nextGears = gearOnboard + .map(gear => { + const gearByCode = gearsByCode[gear.gear] + if (!gearByCode) { + throw new FrontendError('`gearByCode` is undefined.') + } + + return { ...gearByCode, declaredMesh: gear.mesh } + }) + .map(gear => ({ + comments: undefined, + controlledMesh: undefined, + declaredMesh: gear.declaredMesh, + gearCode: gear.code, + gearName: gear.name, + gearWasControlled: undefined, + hasUncontrolledMesh: false + })) + + setFieldValue('gearOnboard', nextGears) + + return nextGears + } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts new file mode 100644 index 0000000000..50d05bd01f --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts @@ -0,0 +1,63 @@ +import { missionFormActions } from '@features/Mission/components/MissionForm/slice' +import { EnvMissionAction } from '@features/Mission/envMissionAction.types' +import { MissionAction } from '@features/Mission/missionAction.types' +import { getLastControlCircleGeometry } from '@features/Mission/useCases/getLastControlCircleGeometry' +import { first, orderBy } from 'lodash' + +import type { Port } from '../../../../../domain/types/port' +import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' + +export const updateMissionLocation = + ( + dispatch, + ports: Port.Port[] | undefined, + envActions: EnvMissionAction.MissionAction[], + actions: MissionAction.MissionAction[] + ) => + async ( + isGeometryComputedFromControls: boolean | undefined, + missionAction: MissionActionFormValues | MissionAction.MissionAction | undefined + ) => { + if (!missionAction || !ports || !isGeometryComputedFromControls) { + return + } + + const actionDateTimes = actions + .filter( + action => + action.actionType === MissionAction.MissionActionType.LAND_CONTROL || + action.actionType === MissionAction.MissionActionType.SEA_CONTROL || + action.actionType === MissionAction.MissionActionType.AIR_CONTROL + ) + .map(action => action.actionDatetimeUtc) + + const lastFishActionDate = first(orderBy(actionDateTimes, dateTime => dateTime, ['desc'])) + + if (lastFishActionDate && lastFishActionDate > missionAction.actionDatetimeUtc) { + // As another action is newer, we do not update the mission location + return + } + + const envActionDateTimes = envActions + .filter( + action => + action.actionType === EnvMissionAction.MissionActionType.CONTROL || + action.actionType === EnvMissionAction.MissionActionType.SURVEILLANCE + ) + .map(action => action.actionStartDateTimeUtc) + .filter((actionStartDateTimeUtc): actionStartDateTimeUtc is string => actionStartDateTimeUtc !== null) + + const lastEnvActionDate = first(orderBy(envActionDateTimes, dateTime => dateTime, ['desc'])) + + if (lastEnvActionDate && lastEnvActionDate > missionAction.actionDatetimeUtc) { + // As another action from Env is newer, we do not update the mission location + return + } + + const nextMissionGeometry = await dispatch(getLastControlCircleGeometry(ports, missionAction)) + if (!nextMissionGeometry) { + return + } + + dispatch(missionFormActions.setGeometryComputedFromControls(nextMissionGeometry)) + } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes.ts new file mode 100644 index 0000000000..1c65476db5 --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes.ts @@ -0,0 +1,20 @@ +import { PAMControlUnitIds } from '@features/Mission/components/MissionForm/constants' +import { missionFormActions } from '@features/Mission/components/MissionForm/slice' + +import type { MissionMainFormValues } from '@features/Mission/components/MissionForm/types' + +export const updateOtherControlsCheckboxes = + dispatch => async (mission: MissionMainFormValues, previousIsControlUnitPAM: boolean) => { + const isControlUnitPAM = mission.controlUnits?.some( + controlUnit => controlUnit.id && PAMControlUnitIds.includes(controlUnit.id) + ) + + /** + * If a PAM was already in the control units, we do not reset the other controls + */ + if (previousIsControlUnitPAM && isControlUnitPAM) { + return + } + + dispatch(missionFormActions.mustResetOtherControlsCheckboxes(true)) + } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts new file mode 100644 index 0000000000..fcf676722f --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts @@ -0,0 +1,28 @@ +import { MissionAction } from '@features/Mission/missionAction.types' + +import { getFleetSegments } from '../../../../../domain/use_cases/vessel/getFleetSegments' + +import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' +import type { Option } from '@mtes-mct/monitor-ui' + +export const updateSegments = + ( + dispatch, + setFieldValue: (field: string, value: any) => void, + fleetSegmentsAsOptions: Option[] + ) => + async (missionAction: MissionActionFormValues) => { + if (missionAction.actionType === MissionAction.MissionActionType.AIR_CONTROL) { + return + } + + const computedFleetSegments = await dispatch( + getFleetSegments(missionAction.faoAreas, missionAction.gearOnboard, missionAction.speciesOnboard) + ) + + const nextFleetSegments = fleetSegmentsAsOptions + .filter(({ value }) => computedFleetSegments?.find(fleetSegment => fleetSegment.segment === value.segment)) + .map(({ value }) => value) + + setFieldValue('segments', nextFleetSegments) + } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts new file mode 100644 index 0000000000..0099ca987a --- /dev/null +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts @@ -0,0 +1,41 @@ +import { getSummedSpeciesOnBoard } from '@features/Logbook/utils' +import { MissionAction } from '@features/Mission/missionAction.types' +import { vesselApi } from '@features/Vessel/apis' + +import type { RiskFactor } from '../../../../../domain/entities/vessel/riskFactor/types' +import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' + +export const updateSpeciesOnboard = + (dispatch, setFieldValue: (field: string, value: any) => void) => + async (missionAction: MissionActionFormValues): Promise => { + if (!missionAction.internalReferenceNumber) { + return [] + } + + const { data: riskFactor } = await dispatch( + vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) + ) + if (!riskFactor) { + return [] + } + + const { speciesOnboard } = riskFactor as RiskFactor + if (!speciesOnboard?.length) { + return [] + } + + const summedSpeciesOnboard = getSummedSpeciesOnBoard(speciesOnboard) + const nextSpeciesOnboard = summedSpeciesOnboard + .sort((a, b) => b.weight - a.weight) + .map(specy => ({ + controlledWeight: undefined, + declaredWeight: specy.weight, + nbFish: undefined, + speciesCode: specy.species, + underSized: false + })) + + setFieldValue('speciesOnboard', nextSpeciesOnboard) + + return nextSpeciesOnboard + } diff --git a/frontend/src/features/Mission/useCases/deleteMissionAction.ts b/frontend/src/features/Mission/useCases/deleteMissionAction.ts index ec6c130a62..6946523863 100644 --- a/frontend/src/features/Mission/useCases/deleteMissionAction.ts +++ b/frontend/src/features/Mission/useCases/deleteMissionAction.ts @@ -1,8 +1,9 @@ import { missionActionApi } from '@api/missionAction' import { portApi } from '@api/port' -import { formikUsecase } from '@features/Mission/components/MissionForm/formikUsecases' import { missionFormActions } from '@features/Mission/components/MissionForm/slice' +import { formUsecase } from '@features/Mission/components/MissionForm/useCases' import { validateMissionForms } from '@features/Mission/components/MissionForm/utils/validateMissionForms' +import { EnvMissionAction } from '@features/Mission/envMissionAction.types' import { monitorfishMissionApi } from '@features/Mission/monitorfishMissionApi' import { MissionAction } from '../missionAction.types' @@ -50,16 +51,17 @@ export const deleteMissionAction = ) if (nextControlActionsWithGeometry.length === 0) { - await formikUsecase.initMissionLocation(dispatch)(isGeometryComputedFromControls) + await formUsecase.initMissionLocation(dispatch)(isGeometryComputedFromControls) } else { const { data: ports } = await dispatch(portApi.endpoints.getPorts.initiate()) const missionId = getState().missionForm.draft?.mainFormValues?.id - const envActions = await getEnvActions(missionId) + const { actions, envActions } = await getActions(missionId) - await formikUsecase.updateMissionLocation( + await formUsecase.updateMissionLocation( dispatch, ports, - envActions + envActions, + actions )(isGeometryComputedFromControls, nextControlActionsWithGeometry[0]) } @@ -76,13 +78,22 @@ export const deleteMissionAction = } } - async function getEnvActions(missionId: number | undefined) { + async function getActions(missionId: number | undefined): Promise<{ + actions: MissionAction.MissionAction[] + envActions: EnvMissionAction.MissionAction[] + }> { if (!missionId) { - return [] + return { + actions: [], + envActions: [] + } } const { data: mission } = await dispatch(monitorfishMissionApi.endpoints.getMission.initiate(missionId)) - return mission?.envActions ?? [] + return { + actions: mission?.actions ?? [], + envActions: mission?.envActions ?? [] + } } } From 236aada40aa7b3f5d44df5a0e57e90a4ede96415 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 20 May 2024 11:11:50 +0200 Subject: [PATCH 2/3] Exclude controls done by AECP --- .../mission_actions/GetActivityReports.kt | 4 ++ .../GetActivityReportsUTests.kt | 71 +++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReports.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReports.kt index bdd1c8b28d..5040ae96d8 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReports.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReports.kt @@ -64,6 +64,10 @@ class GetActivityReports( else -> throw IllegalArgumentException("Bad control type: ${control.actionType}") } + }.filter { control -> + // All AECP reports are excluded from the response + // see: https://github.com/MTES-MCT/monitorfish/issues/3194 + return@filter !control.controlUnits.any { controlUnit -> controlUnit.administration == "AECP" } } logger.info("Found ${filteredControls.size} controls to report.") diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReportsUTests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReportsUTests.kt index 685812b564..efea57d974 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReportsUTests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/mission/mission_actions/GetActivityReportsUTests.kt @@ -4,6 +4,7 @@ import com.neovisionaries.i18n.CountryCode import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.given +import fr.gouv.cnsp.monitorfish.domain.entities.mission.ControlUnit import fr.gouv.cnsp.monitorfish.domain.entities.mission.Mission import fr.gouv.cnsp.monitorfish.domain.entities.mission.MissionSource import fr.gouv.cnsp.monitorfish.domain.entities.mission.MissionType @@ -382,4 +383,74 @@ class GetActivityReportsUTests { // Then assertThat(activityReports.activityReports).hasSize(0) } + + @Test + fun `execute Should filter a control done by an AECP unit`() { + // Given + val controls = listOf( + MissionAction( + id = 1, + vesselId = 1, + missionId = 1, + actionDatetimeUtc = ZonedDateTime.now(), + portLocode = "AEFAT", + faoAreas = listOf("27.4.b", "27.4.c"), + actionType = MissionActionType.LAND_CONTROL, + gearOnboard = listOf(), + controlUnits = listOf(ControlUnit(123, "AECP", false, "Unit AECP", listOf())), + speciesOnboard = listOf(), + seizureAndDiversion = true, + isDeleted = false, + hasSomeGearsSeized = false, + hasSomeSpeciesSeized = false, + isFromPoseidon = false, + completion = Completion.TO_COMPLETE, + ), + ) + given(missionActionsRepository.findControlsInDates(any(), any())).willReturn(controls) + + val vessels = listOf( + Vessel( + id = 1, + internalReferenceNumber = "FR00022680", + vesselName = "MY AWESOME VESSEL", + flagState = CountryCode.FR, + declaredFishingGears = listOf("Trémails"), + vesselType = "Fishing", + districtCode = "AY", + ), + ) + given(vesselRepository.findVesselsByIds(eq(listOf(1)))).willReturn(vessels) + + val missions = listOf( + Mission( + 1, + missionTypes = listOf(MissionType.LAND), + missionSource = MissionSource.MONITORFISH, + isUnderJdp = false, + isGeometryComputedFromControls = false, + startDateTimeUtc = ZonedDateTime.of(2020, 5, 5, 3, 4, 5, 3, ZoneOffset.UTC), + ), + + ) + // The mission id 2 is not returned + given(missionRepository.findByIds(listOf(1))).willReturn(missions) + given(portRepository.findByLocode(eq("AEFAT"))).willReturn(Port("AEFAT", "Al Jazeera Port")) + + // When + val activityReports = GetActivityReports( + missionActionsRepository, + portRepository, + vesselRepository, + missionRepository, + ).execute( + ZonedDateTime.now(), + ZonedDateTime.now().minusDays(1), + JointDeploymentPlan.NORTH_SEA, + ) + + // Then + assertThat(activityReports.jdpSpecies).hasSize(38) + assertThat(activityReports.activityReports).hasSize(0) + } } From 83d26046767a9e7d9a4b7b33f5ae14b8e0780986 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 21 May 2024 11:39:05 +0200 Subject: [PATCH 3/3] Remove index file --- .../hooks/useGetMainFormFormikUsecases.ts | 8 ++--- .../useGetMissionActionFormikUsecases.ts | 29 ++++++++++--------- .../__tests__/updateMissionLocation.test.ts | 10 +++---- .../components/MissionForm/useCases/index.ts | 17 ----------- ...sionLocation.ts => initMissionGeometry.ts} | 2 +- ...ateFAOAreas.ts => updateActionFAOAreas.ts} | 2 +- ...Onboard.ts => updateActionGearsOnboard.ts} | 2 +- ...ateSegments.ts => updateActionSegments.ts} | 2 +- ...board.ts => updateActionSpeciesOnboard.ts} | 2 +- ...onLocation.ts => updateMissionGeometry.ts} | 2 +- .../Mission/useCases/deleteMissionAction.ts | 11 +++---- 11 files changed, 36 insertions(+), 51 deletions(-) delete mode 100644 frontend/src/features/Mission/components/MissionForm/useCases/index.ts rename frontend/src/features/Mission/components/MissionForm/useCases/{initMissionLocation.ts => initMissionGeometry.ts} (90%) rename frontend/src/features/Mission/components/MissionForm/useCases/{updateFAOAreas.ts => updateActionFAOAreas.ts} (95%) rename frontend/src/features/Mission/components/MissionForm/useCases/{updateGearsOnboard.ts => updateActionGearsOnboard.ts} (97%) rename frontend/src/features/Mission/components/MissionForm/useCases/{updateSegments.ts => updateActionSegments.ts} (96%) rename frontend/src/features/Mission/components/MissionForm/useCases/{updateSpeciesOnboard.ts => updateActionSpeciesOnboard.ts} (97%) rename frontend/src/features/Mission/components/MissionForm/useCases/{updateMissionLocation.ts => updateMissionGeometry.ts} (98%) diff --git a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts index 2a09e8c5d0..c104af64d6 100644 --- a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts +++ b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMainFormFormikUsecases.ts @@ -1,4 +1,6 @@ import { useGetPortsQuery } from '@api/port' +import { updateMissionGeometry } from '@features/Mission/components/MissionForm/useCases/updateMissionGeometry' +import { updateOtherControlsCheckboxes } from '@features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes' import { useGetMissionQuery } from '@features/Mission/monitorfishMissionApi' import { isAirOrSeaControl, isLandControl } from '@features/Mission/useCases/getLastControlCircleGeometry' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' @@ -6,8 +8,6 @@ import { useMainAppSelector } from '@hooks/useMainAppSelector' import { skipToken } from '@reduxjs/toolkit/query' import { sortBy } from 'lodash' -import { formUsecase } from '../useCases' - import type { MissionMainFormValues } from '../types' export function useGetMainFormFormikUsecases() { @@ -22,7 +22,7 @@ export function useGetMainFormFormikUsecases() { * When updating a control unit, we must reset the "Other controls" field checkboxes */ updateMissionActionOtherControlsCheckboxes: (mission: MissionMainFormValues, previousIsControlUnitPAM: boolean) => - formUsecase.updateOtherControlsCheckboxes(dispatch)(mission, previousIsControlUnitPAM), + updateOtherControlsCheckboxes(dispatch)(mission, previousIsControlUnitPAM), /** * When updating the mission location from the mission, we use the `RTK-Query` cache object to access the `missionAction` form. @@ -53,7 +53,7 @@ export function useGetMainFormFormikUsecases() { return false } - await formUsecase.updateMissionLocation( + await updateMissionGeometry( dispatch, getPortsApiQuery.data, getMissionApiQuery.data?.envActions ?? [], diff --git a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts index af12bc0773..29feb20607 100644 --- a/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts +++ b/frontend/src/features/Mission/components/MissionForm/hooks/useGetMissionActionFormikUsecases.ts @@ -1,5 +1,11 @@ import { useGetPortsQuery } from '@api/port' import { useGetFleetSegmentsQuery } from '@features/FleetSegment/apis' +import { initMissionGeometry } from '@features/Mission/components/MissionForm/useCases/initMissionGeometry' +import { updateActionFAOAreas } from '@features/Mission/components/MissionForm/useCases/updateActionFAOAreas' +import { updateActionGearsOnboard } from '@features/Mission/components/MissionForm/useCases/updateActionGearsOnboard' +import { updateActionSegments } from '@features/Mission/components/MissionForm/useCases/updateActionSegments' +import { updateActionSpeciesOnboard } from '@features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard' +import { updateMissionGeometry } from '@features/Mission/components/MissionForm/useCases/updateMissionGeometry' import { MissionAction } from '@features/Mission/missionAction.types' import { useGetMissionQuery } from '@features/Mission/monitorfishMissionApi' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' @@ -9,7 +15,6 @@ import { useFormikContext } from 'formik' import { useMemo } from 'react' import { getFleetSegmentsAsOption } from '../ActionForm/shared/utils' -import { formUsecase } from '../useCases' import type { MissionActionFormValues } from '../types' import type { Option } from '@mtes-mct/monitor-ui' @@ -32,15 +37,15 @@ export function useGetMissionActionFormikUsecases() { ) const updateSegments = (missionActionValues: MissionActionFormValues) => - formUsecase.updateSegments(dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions)(missionActionValues) + updateActionSegments(dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions)(missionActionValues) /** * Update FAO Areas and segments from the control coordinates or port input */ async function updateFAOAreasAndSegments(missionActionValues: MissionActionFormValues) { - const faoAreas = await formUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) + const faoAreas = await updateActionFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) - await formUsecase.updateSegments( + await updateActionSegments( dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions @@ -62,20 +67,17 @@ export function useGetMissionActionFormikUsecases() { return } - const gearOnboard = await formUsecase.updateGearsOnboard( + const gearOnboard = await updateActionGearsOnboard( dispatch, setMissionActionFieldValue, gearsByCode )(missionActionValues) - const speciesOnboard = await formUsecase.updateSpeciesOnboard( - dispatch, - setMissionActionFieldValue - )(missionActionValues) + const speciesOnboard = await updateActionSpeciesOnboard(dispatch, setMissionActionFieldValue)(missionActionValues) - const faoAreas = await formUsecase.updateFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) + const faoAreas = await updateActionFAOAreas(dispatch, setMissionActionFieldValue)(missionActionValues) - await formUsecase.updateSegments( + await updateActionSegments( dispatch, setMissionActionFieldValue, fleetSegmentsAsOptions @@ -92,7 +94,7 @@ export function useGetMissionActionFormikUsecases() { * The mission location is equal to the current action geometry modified. */ const updateMissionLocation = (missionActionValues: MissionActionFormValues) => - formUsecase.updateMissionLocation( + updateMissionGeometry( dispatch, getPortsApiQuery.data, getMissionApiQuery.data?.envActions ?? [], @@ -102,8 +104,7 @@ export function useGetMissionActionFormikUsecases() { /** * When updating the mission location from an action, we use the `RTK-Query` cache object to access the `mission` form. */ - const initMissionLocation = () => - formUsecase.initMissionLocation(dispatch)(draft?.mainFormValues.isGeometryComputedFromControls) + const initMissionLocation = () => initMissionGeometry(dispatch)(draft?.mainFormValues.isGeometryComputedFromControls) return { initMissionLocation, diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts index 7e37130831..1c24779c26 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/__tests__/updateMissionLocation.test.ts @@ -1,5 +1,5 @@ import { dummyAction } from '@features/Mission/components/MissionForm/useCases/__tests__/__mocks__/dummyAction' -import { updateMissionLocation } from '@features/Mission/components/MissionForm/useCases/updateMissionLocation' +import { updateMissionGeometry } from '@features/Mission/components/MissionForm/useCases/updateMissionGeometry' import { EnvMissionAction } from '@features/Mission/envMissionAction.types' import { expect, jest } from '@jest/globals' import { customDayjs } from '@mtes-mct/monitor-ui' @@ -9,7 +9,7 @@ const mockDispatch = jest.fn() describe('features/Mission/components/MissionForm/useCases.updateMissionLocation()', () => { it('Should update the mission location When there is no other actions', () => { // When - updateMissionLocation(mockDispatch, [], [], [])(true, dummyAction) + updateMissionGeometry(mockDispatch, [], [], [])(true, dummyAction) // Then expect(mockDispatch).toHaveBeenCalled() @@ -20,7 +20,7 @@ describe('features/Mission/components/MissionForm/useCases.updateMissionLocation const olderAction = { ...dummyAction, actionDatetimeUtc: '2018-12-08T08:27:00Z' } // When - updateMissionLocation(mockDispatch, [], [], [olderAction])(true, dummyAction) + updateMissionGeometry(mockDispatch, [], [], [olderAction])(true, dummyAction) // Then expect(mockDispatch).toHaveBeenCalled() @@ -31,7 +31,7 @@ describe('features/Mission/components/MissionForm/useCases.updateMissionLocation const newerAction = { ...dummyAction, actionDatetimeUtc: customDayjs().toISOString() } // When - updateMissionLocation(mockDispatch, [], [], [newerAction])(true, dummyAction) + updateMissionGeometry(mockDispatch, [], [], [newerAction])(true, dummyAction) // Then expect(mockDispatch).toHaveBeenCalledTimes(0) @@ -47,7 +47,7 @@ describe('features/Mission/components/MissionForm/useCases.updateMissionLocation } // When - updateMissionLocation(mockDispatch, [], [newerEnvAction], [])(true, dummyAction) + updateMissionGeometry(mockDispatch, [], [newerEnvAction], [])(true, dummyAction) // Then expect(mockDispatch).toHaveBeenCalledTimes(0) diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/index.ts b/frontend/src/features/Mission/components/MissionForm/useCases/index.ts deleted file mode 100644 index 2550f12a40..0000000000 --- a/frontend/src/features/Mission/components/MissionForm/useCases/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { initMissionLocation } from '@features/Mission/components/MissionForm/useCases/initMissionLocation' -import { updateFAOAreas } from '@features/Mission/components/MissionForm/useCases/updateFAOAreas' -import { updateGearsOnboard } from '@features/Mission/components/MissionForm/useCases/updateGearsOnboard' -import { updateMissionLocation } from '@features/Mission/components/MissionForm/useCases/updateMissionLocation' -import { updateOtherControlsCheckboxes } from '@features/Mission/components/MissionForm/useCases/updateOtherControlsCheckboxes' -import { updateSegments } from '@features/Mission/components/MissionForm/useCases/updateSegments' -import { updateSpeciesOnboard } from '@features/Mission/components/MissionForm/useCases/updateSpeciesOnboard' - -export const formUsecase = { - initMissionLocation, - updateFAOAreas, - updateGearsOnboard, - updateMissionLocation, - updateOtherControlsCheckboxes, - updateSegments, - updateSpeciesOnboard -} diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts b/frontend/src/features/Mission/components/MissionForm/useCases/initMissionGeometry.ts similarity index 90% rename from frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/initMissionGeometry.ts index ce8328cb60..a7c53bc4a4 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/initMissionLocation.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/initMissionGeometry.ts @@ -5,7 +5,7 @@ import { convertToGeoJSONGeometryObject } from '../../../../../domain/entities/l import type { GeoJSON } from '../../../../../domain/types/GeoJSON' -export const initMissionLocation = dispatch => async (isGeometryComputedFromControls: boolean | undefined) => { +export const initMissionGeometry = dispatch => async (isGeometryComputedFromControls: boolean | undefined) => { if (!isGeometryComputedFromControls) { return } diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionFAOAreas.ts similarity index 95% rename from frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/updateActionFAOAreas.ts index 6c002cb8ee..7990f7462a 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateFAOAreas.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionFAOAreas.ts @@ -2,7 +2,7 @@ import { faoAreasApi } from '@api/faoAreas' import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' -export const updateFAOAreas = +export const updateActionFAOAreas = (dispatch, setFieldValue: (field: string, value: any) => void) => async (missionAction: MissionActionFormValues): Promise => { const { data: computedVesselFaoAreas } = await dispatch( diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts similarity index 97% rename from frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts index 1f9e44e0f3..aafcb8c038 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateGearsOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts @@ -8,7 +8,7 @@ import type { MainRootState } from '@store' import type { AnyAction } from 'redux' import type { ThunkDispatch } from 'redux-thunk' -export const updateGearsOnboard = +export const updateActionGearsOnboard = ( dispatch: ThunkDispatch, setFieldValue: (field: string, value: any) => void, diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSegments.ts similarity index 96% rename from frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/updateActionSegments.ts index fcf676722f..fa1ed64c27 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateSegments.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSegments.ts @@ -5,7 +5,7 @@ import { getFleetSegments } from '../../../../../domain/use_cases/vessel/getFlee import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' import type { Option } from '@mtes-mct/monitor-ui' -export const updateSegments = +export const updateActionSegments = ( dispatch, setFieldValue: (field: string, value: any) => void, diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts similarity index 97% rename from frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts index 0099ca987a..5872ec1ee7 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateSpeciesOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts @@ -5,7 +5,7 @@ import { vesselApi } from '@features/Vessel/apis' import type { RiskFactor } from '../../../../../domain/entities/vessel/riskFactor/types' import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' -export const updateSpeciesOnboard = +export const updateActionSpeciesOnboard = (dispatch, setFieldValue: (field: string, value: any) => void) => async (missionAction: MissionActionFormValues): Promise => { if (!missionAction.internalReferenceNumber) { diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionGeometry.ts similarity index 98% rename from frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts rename to frontend/src/features/Mission/components/MissionForm/useCases/updateMissionGeometry.ts index 50d05bd01f..2a9d99f0dd 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionLocation.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateMissionGeometry.ts @@ -7,7 +7,7 @@ import { first, orderBy } from 'lodash' import type { Port } from '../../../../../domain/types/port' import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' -export const updateMissionLocation = +export const updateMissionGeometry = ( dispatch, ports: Port.Port[] | undefined, diff --git a/frontend/src/features/Mission/useCases/deleteMissionAction.ts b/frontend/src/features/Mission/useCases/deleteMissionAction.ts index 6946523863..57a84ac393 100644 --- a/frontend/src/features/Mission/useCases/deleteMissionAction.ts +++ b/frontend/src/features/Mission/useCases/deleteMissionAction.ts @@ -1,7 +1,8 @@ import { missionActionApi } from '@api/missionAction' import { portApi } from '@api/port' import { missionFormActions } from '@features/Mission/components/MissionForm/slice' -import { formUsecase } from '@features/Mission/components/MissionForm/useCases' +import { initMissionGeometry } from '@features/Mission/components/MissionForm/useCases/initMissionGeometry' +import { updateMissionGeometry } from '@features/Mission/components/MissionForm/useCases/updateMissionGeometry' import { validateMissionForms } from '@features/Mission/components/MissionForm/utils/validateMissionForms' import { EnvMissionAction } from '@features/Mission/envMissionAction.types' import { monitorfishMissionApi } from '@features/Mission/monitorfishMissionApi' @@ -51,13 +52,13 @@ export const deleteMissionAction = ) if (nextControlActionsWithGeometry.length === 0) { - await formUsecase.initMissionLocation(dispatch)(isGeometryComputedFromControls) + await initMissionGeometry(dispatch)(isGeometryComputedFromControls) } else { const { data: ports } = await dispatch(portApi.endpoints.getPorts.initiate()) const missionId = getState().missionForm.draft?.mainFormValues?.id - const { actions, envActions } = await getActions(missionId) + const { actions, envActions } = await getMissionActions(missionId) - await formUsecase.updateMissionLocation( + await updateMissionGeometry( dispatch, ports, envActions, @@ -78,7 +79,7 @@ export const deleteMissionAction = } } - async function getActions(missionId: number | undefined): Promise<{ + async function getMissionActions(missionId: number | undefined): Promise<{ actions: MissionAction.MissionAction[] envActions: EnvMissionAction.MissionAction[] }> {