From 9e2eae5ef8601c56bb71f8cd34583f8a3af982e4 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Thu, 30 Jan 2025 18:03:06 +0100 Subject: [PATCH 01/21] wip: use zod for last position type and check few types --- frontend/config/jest.config.js | 2 +- frontend/src/api/api.ts | 9 + frontend/src/api/beaconMalfunction.ts | 4 +- frontend/src/api/vessel.ts | 15 +- .../src/domain/entities/estimatedPosition.js | 4 +- .../entities/vessel/riskFactor/types.ts | 22 - .../track/__tests__/__mocks__/vesselTrack.ts | 194 ++++---- .../vesselTrackWithDuplicatePositions.ts | 114 ++--- .../vessel/track/__tests__/index.test.ts | 42 +- .../domain/entities/vessel/track/constants.ts | 4 +- .../src/domain/entities/vessel/track/index.ts | 54 +-- frontend/src/domain/entities/vessel/types.ts | 206 +-------- frontend/src/domain/entities/vessel/vessel.ts | 87 ++-- .../src/domain/entities/vesselLabelLine.ts | 3 +- .../src/domain/entities/vesselTrackDepth.ts | 5 +- .../domain/shared_slices/FavoriteVessel.ts | 13 +- frontend/src/domain/shared_slices/Global.ts | 5 +- .../domain/use_cases/alert/silenceAlert.ts | 4 +- .../domain/use_cases/alert/validateAlert.ts | 4 +- .../getVesselBeaconMalfunctions.ts | 4 +- .../use_cases/vessel/getFilteredVessels.ts | 4 +- .../use_cases/vessel/hideVesselTrack.ts | 4 +- .../src/domain/use_cases/vessel/showVessel.ts | 22 +- .../showVesselFromBeaconMalfunctionsKanban.ts | 4 +- .../use_cases/vessel/showVesselTrack.ts | 7 +- .../updateSelectedVesselTrackRequest.ts | 5 +- .../PendingAlertRow.tsx | 4 +- .../SilencedAlerts/fields/VesselField.tsx | 34 +- .../SideWindowAlerts/SilencedAlerts/index.tsx | 4 +- .../SideWindowAlerts/SilencedAlerts/types.ts | 8 +- .../SideWindowAlerts/SilencedAlerts/utils.ts | 8 +- frontend/src/features/Alert/types.ts | 18 +- .../src/features/BeaconMalfunction/types.ts | 4 +- .../CreateOrEditFleetSegmentModal.tsx | 4 +- .../FleetSegmentsBackoffice/schema.ts | 4 +- .../VesselCurrentFleetSegmentDetails/utils.ts | 2 +- frontend/src/features/FleetSegment/types.ts | 12 +- .../src/features/Logbook/Logbook.types.ts | 4 +- frontend/src/features/Logbook/api.ts | 4 +- .../VesselLogbook/LogbookSummary/index.tsx | 13 +- frontend/src/features/Logbook/slice.ts | 7 +- .../Logbook/useCases/getVesselLogbook.ts | 4 +- frontend/src/features/Logbook/utils.ts | 9 +- .../FavoriteVessels/FavoriteVessel.tsx | 6 +- .../MapButtons/FavoriteVessels/index.tsx | 2 +- .../src/features/Map/components/MapMenu.tsx | 4 +- .../Map/useCases/clickOnMapFeature.ts | 6 +- .../ActionForm/shared/VesselField.tsx | 12 +- .../useCases/updateActionGearsOnboard.ts | 4 +- .../useCases/updateActionSpeciesOnboard.ts | 6 +- .../PriorNotificationList/columns.tsx | 2 +- .../PriorNotificationList/utils.tsx | 5 +- .../components/shared/CardBodyHead/TagBar.tsx | 2 +- .../RegulationTables/tableCells.tsx | 2 +- .../__tests__/utils.test.ts | 4 +- .../__tests__/ReportingCard.test.tsx | 5 +- .../components/ReportingForm/index.tsx | 8 +- .../ReportingTable/EditReporting.tsx | 4 +- .../cells/ActionButtonsCell.tsx | 4 +- frontend/src/features/Reporting/types.ts | 5 +- .../__tests__/__mocks__/dummyReporting.ts | 5 +- .../__tests__/archiveReporting.test.ts | 4 +- .../Reporting/useCases/addReporting.ts | 4 +- .../Reporting/useCases/archiveReporting.ts | 4 +- .../Reporting/useCases/archiveReportings.ts | 4 +- .../Reporting/useCases/deleteReporting.ts | 4 +- .../Reporting/useCases/deleteReportings.ts | 4 +- .../Reporting/useCases/updateReporting.ts | 8 +- frontend/src/features/RiskFactor/apis.ts | 35 ++ .../components/VesselRiskFactor.tsx | 2 +- frontend/src/features/RiskFactor/types.ts | 28 ++ .../index.ts => features/RiskFactor/utils.ts} | 8 - frontend/src/features/Vessel/Vessel.types.ts | 195 +++++++- .../VesselLabelOverlay/VesselLabel.tsx | 4 +- .../components/VesselLabelOverlay/index.tsx | 2 +- .../components/VesselList/VesselListModal.tsx | 4 +- .../VesselSearch/VesselSearchResult.tsx | 6 +- .../Vessel/components/VesselSearch/utils.ts | 10 +- .../Controls/InfractionsSummary.tsx | 6 +- .../VesselSidebarHeader/VesselName.tsx | 2 +- .../VesselSidebarHeader/index.tsx | 4 +- .../actions/TrackRequest/ExportTrack.tsx | 9 +- .../TrackRequest/HighlightPositionCell.tsx | 4 +- .../actions/TrackRequest/constants.tsx | 4 +- .../Vessel/components/VesselSidebar/index.tsx | 4 +- .../risk_factor/RiskFactorCursor.tsx | 6 +- .../RiskFactorExplanationModal.tsx | 12 +- .../risk_factor/RiskFactorResume.tsx | 68 ++- .../DetectabilityRiskFactorDetails.tsx | 53 +-- .../details/ImpactRiskFactorDetails.tsx | 11 +- .../details/ProbabilityRiskFactorDetails.tsx | 49 +- .../VesselAlertAndBeaconMalfunctionLayer.jsx | 3 +- .../Vessel/layers/VesselAlertLayer.jsx | 6 +- .../layers/VesselBeaconMalfunctionLayer.jsx | 3 +- .../layers/VesselEstimatedPositionLayer.jsx | 4 +- .../layers/VesselInfractionSuspicionLayer.jsx | 6 +- .../Vessel/layers/VesselSelectedLayer.jsx | 4 +- .../Vessel/layers/VesselsLabelsLayer.tsx | 20 +- .../Vessel/layers/VesselsLayer/index.tsx | 6 +- .../Vessel/layers/VesselsTracksLayer.tsx | 2 +- frontend/src/features/Vessel/layers/style.ts | 4 +- frontend/src/features/Vessel/slice.ts | 73 ++- .../Vessel/useCases/previewVessels.ts | 4 +- .../Vessel/useCases/showAlertInSideWindow.ts | 9 +- .../useCases/showVesselsLastPosition.ts | 12 +- frontend/src/features/Vessel/utils.ts | 81 +--- frontend/src/features/Vessel/vesselApi.ts | 17 +- frontend/src/features/Vessel/vesselNavApi.ts | 11 +- .../VesselSearch/VesselSearchResult.tsx | 2 +- .../VesselSearch/__tests__/mocks/index.ts | 428 +++++++++++++----- .../src/features/VesselSearch/constants.ts | 4 +- frontend/src/features/VesselSearch/index.tsx | 14 +- frontend/src/features/VesselSearch/utils.ts | 36 +- frontend/src/types.ts | 15 +- frontend/src/utils/undefinedize.ts | 2 - 115 files changed, 1262 insertions(+), 1132 deletions(-) delete mode 100644 frontend/src/domain/entities/vessel/riskFactor/types.ts create mode 100644 frontend/src/features/RiskFactor/apis.ts rename frontend/src/features/{Vessel => RiskFactor}/components/VesselRiskFactor.tsx (98%) create mode 100644 frontend/src/features/RiskFactor/types.ts rename frontend/src/{domain/entities/vessel/riskFactor/index.ts => features/RiskFactor/utils.ts} (92%) diff --git a/frontend/config/jest.config.js b/frontend/config/jest.config.js index 14af315d41..68a57ea37d 100644 --- a/frontend/config/jest.config.js +++ b/frontend/config/jest.config.js @@ -25,7 +25,7 @@ export default { '@hooks/*': ['hooks/*'], '@libs/*': ['libs/*'], '@pages/*': ['pages/*'], - '@store': ['store/index.ts'], + '@store': ['store/utils.ts'], '@store/*': ['store/*'], '@utils/*': ['utils/*'] }, diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index e7afc6dbd4..3d7e9c4fea 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -7,6 +7,7 @@ import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react' import { setMeasurement, startSpan } from '@sentry/react' import { normalizeRtkBaseQuery } from '@utils/normalizeRtkBaseQuery' import { sha256 } from '@utils/sha256' +import { undefinedize } from '@utils/undefinedize' import ky, { HTTPError } from 'ky' import { RTK_MAX_RETRIES, RtkCacheTagType } from './constants' @@ -208,6 +209,14 @@ export const monitorfishPublicApi = createApi({ export const monitorfishApiKy = ky.extend({ hooks: { + afterResponse: [ + async (_input, _options, response) => { + const responseData = await response.json() + const normalizedResponse = undefinedize(responseData) + + return new Response(JSON.stringify(normalizedResponse), response) + } + ], beforeError: [ async error => { const { request, response } = error diff --git a/frontend/src/api/beaconMalfunction.ts b/frontend/src/api/beaconMalfunction.ts index be412fbd97..3eaa6dc191 100644 --- a/frontend/src/api/beaconMalfunction.ts +++ b/frontend/src/api/beaconMalfunction.ts @@ -2,7 +2,6 @@ import { FrontendApiError } from '@libs/FrontendApiError' import { monitorfishApiKy } from './api' -import type { VesselId } from '../domain/entities/vessel/types' import type { NOTIFICATION_TYPE, UserType } from '@features/BeaconMalfunction/constants' import type { BeaconMalfunction, @@ -10,6 +9,7 @@ import type { UpdateBeaconMalfunction, VesselBeaconMalfunctionsResumeAndHistory } from '@features/BeaconMalfunction/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export const ARCHIVE_BEACON_MALFUNCTION = "Nous n'avons pas pu archiver les avaries VMS" export const GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les avaries VMS" @@ -92,7 +92,7 @@ async function saveBeaconMalfunctionCommentFromAPI( * @throws {@link FrontendApiError} */ async function getVesselBeaconsMalfunctionsFromAPI( - vesselId: VesselId, + vesselId: Vessel.VesselId, fromDate: Date ): Promise { try { diff --git a/frontend/src/api/vessel.ts b/frontend/src/api/vessel.ts index 1c6ca44b1b..73ece38c06 100644 --- a/frontend/src/api/vessel.ts +++ b/frontend/src/api/vessel.ts @@ -2,12 +2,13 @@ import { getVesselIdentityFromLegacyVesselIdentity, getVesselIdentityPropsAsEmptyStringsWhenUndefined } from '@features/Vessel/utils' +import { Vessel } from '@features/Vessel/Vessel.types' import { FrontendApiError } from '@libs/FrontendApiError' import { monitorfishApiKy } from './api' import { HttpStatusCode } from './constants' -import type { TrackRequest, VesselAndPositions, VesselIdentity, VesselPosition } from '../domain/entities/vessel/types' +import type { TrackRequest } from '../domain/entities/vessel/types' const VESSEL_POSITIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations du navire" const VESSEL_SEARCH_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires dans notre base" @@ -17,7 +18,7 @@ const VESSEL_SEARCH_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires * * @throws {@link FrontendApiError} */ -async function getVesselFromAPI(vesselIdentity: VesselIdentity, trackRequest: TrackRequest) { +async function getVesselFromAPI(vesselIdentity: Vessel.VesselIdentity, trackRequest: TrackRequest) { const { externalReferenceNumber, internalReferenceNumber, ircs, vesselId, vesselIdentifier } = getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) const trackDepth = trackRequest.trackDepth ?? '' @@ -30,7 +31,7 @@ async function getVesselFromAPI(vesselIdentity: VesselIdentity, trackRequest: Tr `/bff/v1/vessels/find?vesselId=${vesselId}&internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` ) .then(response => - response.json().then(vesselAndPositions => ({ + response.json().then(vesselAndPositions => ({ isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, vesselAndPositions })) @@ -45,7 +46,7 @@ async function getVesselFromAPI(vesselIdentity: VesselIdentity, trackRequest: Tr * * @throws {@link FrontendApiError} */ -async function getVesselPositionsFromAPI(identity: VesselIdentity, trackRequest: TrackRequest) { +async function getVesselPositionsFromAPI(identity: Vessel.VesselIdentity, trackRequest: TrackRequest) { const { externalReferenceNumber, internalReferenceNumber, ircs, vesselIdentifier } = getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(identity)) const trackDepth = trackRequest.trackDepth ?? '' @@ -58,7 +59,7 @@ async function getVesselPositionsFromAPI(identity: VesselIdentity, trackRequest: `/bff/v1/vessels/positions?internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` ) .then(response => - response.json().then(positions => ({ + response.json().then(positions => ({ isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, positions })) @@ -73,7 +74,9 @@ async function searchVesselsFromAPI(searched: string) { const encodedSearched = encodeURI(searched) || '' try { - return await monitorfishApiKy.get(`/bff/v1/vessels/search?searched=${encodedSearched}`).json() + return await monitorfishApiKy + .get(`/bff/v1/vessels/search?searched=${encodedSearched}`) + .json() } catch (err) { throw new FrontendApiError(VESSEL_SEARCH_ERROR_MESSAGE, (err as FrontendApiError).originalError) } diff --git a/frontend/src/domain/entities/estimatedPosition.js b/frontend/src/domain/entities/estimatedPosition.js index 62afa9af7e..901c612d98 100644 --- a/frontend/src/domain/entities/estimatedPosition.js +++ b/frontend/src/domain/entities/estimatedPosition.js @@ -3,7 +3,7 @@ import LineString from 'ol/geom/LineString' import Point from 'ol/geom/Point' import { transform } from 'ol/proj' -import { Vessel } from './vessel/vessel' +import { VesselFeature } from './vessel/vessel' import { COLORS } from '../../constants/constants' import { LayerProperties, OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../features/Map/constants' import { MonitorFishMap } from '../../features/Map/Map.types' @@ -58,7 +58,7 @@ class EstimatedPosition { vesselColor = 'rgb(202, 204, 224)' } - const opacity = Vessel.getVesselOpacity(dateTime, options.vesselIsHidden, options.vesselIsOpacityReduced) + const opacity = VesselFeature.getVesselOpacity(dateTime, options.vesselIsHidden, options.vesselIsOpacityReduced) const lineFeature = new Feature({ color: lineColor, diff --git a/frontend/src/domain/entities/vessel/riskFactor/types.ts b/frontend/src/domain/entities/vessel/riskFactor/types.ts deleted file mode 100644 index 96ddddb688..0000000000 --- a/frontend/src/domain/entities/vessel/riskFactor/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { DeclaredLogbookGear, DeclaredLogbookSpecies } from '../types' - -export type RiskFactor = { - controlPriorityLevel: number - controlRateRiskFactor: number - detectabilityRiskFactor: number - gearOnboard: DeclaredLogbookGear[] | undefined - impactRiskFactor: number - lastControlDatetime: string - numberControlsLastFiveYears: number - numberControlsLastThreeYears: number - numberGearSeizuresLastFiveYears: number - numberInfractionsLastFiveYears: number - numberSpeciesSeizuresLastFiveYears: number - numberVesselSeizuresLastFiveYears: number - probabilityRiskFactor: number - riskFactor: number - segmentHighestImpact: string - segmentHighestPriority: string - segments: string[] - speciesOnboard: DeclaredLogbookSpecies[] | undefined -} diff --git a/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrack.ts b/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrack.ts index b72c807976..129f134145 100644 --- a/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrack.ts +++ b/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrack.ts @@ -1,4 +1,4 @@ -import { NetworkType } from '../../../types' +import { Vessel } from '@features/Vessel/Vessel.types' export const DUMMY_VESSEL_TRACK = [ { @@ -12,14 +12,14 @@ export const DUMMY_VESSEL_TRACK = [ ircs: 'ZBRI', isAtPort: false, isFishing: true, - isManual: null, + isManual: undefined, latitude: 46.386, longitude: -3.328, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.8, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' }, { @@ -33,14 +33,14 @@ export const DUMMY_VESSEL_TRACK = [ ircs: 'ZBRI', isAtPort: false, isFishing: true, - isManual: null, + isManual: undefined, latitude: 46.43, longitude: -3.347, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.9, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' }, { @@ -53,15 +53,15 @@ export const DUMMY_VESSEL_TRACK = [ internalReferenceNumber: 'ABC000898396', ircs: 'ZBRI', isAtPort: false, - isFishing: null, - isManual: null, + isFishing: undefined, + isManual: undefined, latitude: 46.458, longitude: -3.411, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.2, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' }, { @@ -74,15 +74,15 @@ export const DUMMY_VESSEL_TRACK = [ internalReferenceNumber: 'ABC000898396', ircs: 'ZBRI', isAtPort: false, - isFishing: null, - isManual: null, + isFishing: undefined, + isManual: undefined, latitude: 46.486, longitude: -3.481, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 3.1, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' }, { @@ -95,15 +95,15 @@ export const DUMMY_VESSEL_TRACK = [ internalReferenceNumber: 'ABC000898396', ircs: 'ZBRI', isAtPort: false, - isFishing: null, - isManual: null, + isFishing: undefined, + isManual: undefined, latitude: 46.471, longitude: -3.552, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.8, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' }, { @@ -116,15 +116,15 @@ export const DUMMY_VESSEL_TRACK = [ internalReferenceNumber: 'ABC000898396', ircs: 'ZBRI', isAtPort: false, - isFishing: null, - isManual: null, + isFishing: undefined, + isManual: undefined, latitude: 46.45, longitude: -3.614, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.9, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' } ] @@ -133,7 +133,7 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ { course: 0.0, dateTime: '2022-11-13T20:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -144,17 +144,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T21:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -165,17 +165,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T22:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -186,17 +186,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T23:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -207,17 +207,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T00:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -228,17 +228,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T01:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -249,17 +249,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T02:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -270,17 +270,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 140.0, dateTime: '2022-11-14T03:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -291,17 +291,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T04:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -312,17 +312,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T05:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -333,17 +333,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T06:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -354,17 +354,17 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 140.0, dateTime: '2022-11-14T07:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -375,11 +375,11 @@ export const VESSEL_TRACK_ALL_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' } ] @@ -388,7 +388,7 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ { course: 0.0, dateTime: '2022-11-13T20:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -399,17 +399,17 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ isManual: false, latitude: 43.2948, longitude: 3.4498, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T21:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -420,17 +420,17 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T22:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -441,17 +441,17 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-13T23:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -462,17 +462,17 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ isManual: false, latitude: 43.2942, longitude: 3.4492, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' }, { course: 0.0, dateTime: '2022-11-14T00:23:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'ST915733', flagState: 'FR', from: 'FR', @@ -483,11 +483,11 @@ export const VESSEL_TRACK_FEW_SAME_COORDINATES = [ isManual: false, latitude: 43.2953, longitude: 3.4485, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'LA RAFALE III' } ] diff --git a/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrackWithDuplicatePositions.ts b/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrackWithDuplicatePositions.ts index 56b254b653..52ff2602ac 100644 --- a/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrackWithDuplicatePositions.ts +++ b/frontend/src/domain/entities/vessel/track/__tests__/__mocks__/vesselTrackWithDuplicatePositions.ts @@ -1,10 +1,10 @@ -import { NetworkType } from '../../../types' +import { Vessel } from '@features/Vessel/Vessel.types' export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ { course: 0.0, dateTime: '2023-09-18T18:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -15,17 +15,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 357.0, dateTime: '2023-09-18T19:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -36,17 +36,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2804, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 11.0, dateTime: '2023-09-18T20:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -57,17 +57,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7941, longitude: -4.2807, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.5, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 0.0, dateTime: '2023-09-18T21:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -78,17 +78,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 27.0, dateTime: '2023-09-18T22:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -99,17 +99,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.5, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 0.0, dateTime: '2023-09-18T23:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -120,17 +120,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 0.0, dateTime: '2023-09-19T00:57:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -141,17 +141,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2804, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 0.0, dateTime: '2023-09-19T01:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -162,17 +162,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 348.0, dateTime: '2023-09-19T02:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -183,17 +183,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.5, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 0.0, dateTime: '2023-09-19T03:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -204,17 +204,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7936, longitude: -4.2805, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 0.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 150.0, dateTime: '2023-09-19T04:30:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -225,17 +225,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7481, longitude: -4.2828, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 7.5, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 209.0, dateTime: '2023-09-19T04:58:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -246,17 +246,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.7048, longitude: -4.286, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 6.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 112.0, dateTime: '2023-09-19T05:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -267,17 +267,17 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.6723, longitude: -4.2161, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 3.0, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' }, { course: 181.0, dateTime: '2023-09-19T06:59:00Z', - destination: null, + destination: undefined, externalReferenceNumber: 'GV703653', flagState: 'FR', from: 'FR', @@ -288,11 +288,11 @@ export const DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS = [ isManual: false, latitude: 47.6432, longitude: -4.1644, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.5, - tripNumber: null, + tripNumber: undefined, vesselName: 'BUCKIE' } ] diff --git a/frontend/src/domain/entities/vessel/track/__tests__/index.test.ts b/frontend/src/domain/entities/vessel/track/__tests__/index.test.ts index a5649108d8..f605a7a4b2 100644 --- a/frontend/src/domain/entities/vessel/track/__tests__/index.test.ts +++ b/frontend/src/domain/entities/vessel/track/__tests__/index.test.ts @@ -1,16 +1,14 @@ +import { Vessel } from '@features/Vessel/Vessel.types' import { describe, expect, it } from '@jest/globals' import { includes } from 'lodash' +import { getFeaturesFromPositions, getTrackType } from '../index' import { DUMMY_VESSEL_TRACK, VESSEL_TRACK_ALL_SAME_COORDINATES, VESSEL_TRACK_FEW_SAME_COORDINATES } from './__mocks__/vesselTrack' import { DUMMY_VESSEL_TRACK_WITH_DUPLICATE_POSITIONS } from './__mocks__/vesselTrackWithDuplicatePositions' -import { NetworkType } from '../../types' -import { getFeaturesFromPositions, getTrackType } from '../index' - -import type { VesselLineFeature, VesselPointFeature, VesselPosition } from '../../types' describe('vessel/track', () => { it('getFeaturesFromPositions Should draw multiple points with the same coordinates', async () => { @@ -45,16 +43,16 @@ describe('vessel/track', () => { from: 'FR', internalReferenceNumber: 'ABC000898396', ircs: 'ZBRI', - isAtPort: null, - isFishing: null, - isManual: null, + isAtPort: undefined, + isFishing: undefined, + isManual: undefined, latitude: 46.386, longitude: -3.328, - mmsi: null, - networkType: NetworkType.SATELLITE, + mmsi: undefined, + networkType: Vessel.NetworkType.SATELLITE, positionType: 'VMS', speed: 2.8, - tripNumber: null, + tripNumber: undefined, vesselName: 'CE DEVANT ÉLEVER' } ] @@ -66,7 +64,7 @@ describe('vessel/track', () => { // Then expect(features).toHaveLength(1) - const feature = features[0] as VesselPointFeature + const feature = features[0] as Vessel.VesselPointFeature expect(feature).toMatchObject({ course: 356, dateTime: '2022-10-27T16:10:20.4564+02:00', @@ -99,7 +97,7 @@ describe('vessel/track', () => { expect(positionFeatures.map(position => position.course)).toEqual(expectedPositionsCourses) // First Line - const firstLineFeature = lineFeatures[0] as VesselLineFeature + const firstLineFeature = lineFeatures[0] as Vessel.VesselLineFeature expect(firstLineFeature!.course!.toString()).toContain('-1.8') expect(firstLineFeature!.speed).toEqual(2.8) expect(firstLineFeature!.isTimeEllipsis).toEqual(false) @@ -114,7 +112,7 @@ describe('vessel/track', () => { ]) // Second Line - const secondLineFeature = lineFeatures[1] as VesselLineFeature + const secondLineFeature = lineFeatures[1] as Vessel.VesselLineFeature expect(secondLineFeature!.course!.toString()).toContain('-2.57') expect(secondLineFeature!.speed).toEqual(2.9) expect(secondLineFeature!.isTimeEllipsis).toEqual(false) @@ -128,7 +126,7 @@ describe('vessel/track', () => { ]) // Last position - const positionFeature = positionFeatures[5] as VesselPointFeature + const positionFeature = positionFeatures[5] as Vessel.VesselPointFeature expect(positionFeature!.course).toEqual(263) expect(positionFeature!.getId()).toEqual('VESSEL_TRACK:VESSEL_ID:position:5') // A Point geometry is of type Coordinate[] @@ -153,7 +151,7 @@ describe('vessel/track', () => { expect(positionFeatures).toHaveLength(1) expect(arrowFeatures).toHaveLength(0) - const position = positionFeatures[0] as VesselPointFeature + const position = positionFeatures[0] as Vessel.VesselPointFeature expect(position.getId()).toEqual('VESSEL_TRACK:VESSEL_ID:position:0') expect(position.dateTime).toEqual('2022-11-14T07:23:00Z') }) @@ -183,9 +181,9 @@ describe('vessel/track', () => { it('getTrackType Should return FISHING When two positions have the isFishing property set as true', async () => { // Given - const firstPosition = DUMMY_VESSEL_TRACK[0] as VesselPosition + const firstPosition = DUMMY_VESSEL_TRACK[0] as Vessel.VesselPosition firstPosition.isFishing = true - const secondPosition = DUMMY_VESSEL_TRACK[1] as VesselPosition + const secondPosition = DUMMY_VESSEL_TRACK[1] as Vessel.VesselPosition secondPosition.isFishing = true // When @@ -197,9 +195,9 @@ describe('vessel/track', () => { it('getTrackType Should not return FISHING When first position does have the isFishing property set as true', async () => { // Given - const firstPosition = DUMMY_VESSEL_TRACK[0] as VesselPosition + const firstPosition = DUMMY_VESSEL_TRACK[0] as Vessel.VesselPosition firstPosition.isFishing = false - const secondPosition = DUMMY_VESSEL_TRACK[1] as VesselPosition + const secondPosition = DUMMY_VESSEL_TRACK[1] as Vessel.VesselPosition secondPosition.isFishing = true // When @@ -211,9 +209,9 @@ describe('vessel/track', () => { it('getTrackType Should not return FISHING When second position does have the isFishing property set as true', async () => { // Given - const firstPosition = DUMMY_VESSEL_TRACK[0] as VesselPosition + const firstPosition = DUMMY_VESSEL_TRACK[0] as Vessel.VesselPosition firstPosition.isFishing = true - const secondPosition = DUMMY_VESSEL_TRACK[1] as VesselPosition + const secondPosition = DUMMY_VESSEL_TRACK[1] as Vessel.VesselPosition secondPosition.isFishing = false // When @@ -225,7 +223,7 @@ describe('vessel/track', () => { it('getTrackType Should return FISHING When only one position is given and does have the isFishing property set as true', async () => { // Given - const firstPosition = DUMMY_VESSEL_TRACK[0] as VesselPosition + const firstPosition = DUMMY_VESSEL_TRACK[0] as Vessel.VesselPosition firstPosition.isFishing = true // When diff --git a/frontend/src/domain/entities/vessel/track/constants.ts b/frontend/src/domain/entities/vessel/track/constants.ts index 6a6a3d00ba..85ce402755 100644 --- a/frontend/src/domain/entities/vessel/track/constants.ts +++ b/frontend/src/domain/entities/vessel/track/constants.ts @@ -1,8 +1,8 @@ import { THEME } from '@mtes-mct/monitor-ui' -import type { TrackTypeRecordItem } from '../types' +import type { Vessel } from '@features/Vessel/Vessel.types' -export const TRACK_TYPE_RECORD: Record = { +export const TRACK_TYPE_RECORD: Record = { ELLIPSIS: { arrow: 'arrow_gray.png', code: 'ELLIPSIS', diff --git a/frontend/src/domain/entities/vessel/track/index.ts b/frontend/src/domain/entities/vessel/track/index.ts index 7d97ca36ea..4f28020c1b 100644 --- a/frontend/src/domain/entities/vessel/track/index.ts +++ b/frontend/src/domain/entities/vessel/track/index.ts @@ -11,13 +11,7 @@ import { transform } from 'ol/proj' import { TRACK_TYPE_RECORD } from './constants' import { calculatePointsDistance, calculateSplitPointCoordinates } from '../../../../utils' -import type { - VesselArrowFeature, - VesselLineFeature, - VesselPointFeature, - VesselCompositeIdentifier, - VesselPosition -} from '../types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { Coordinate } from 'ol/coordinate' const NUMBER_HOURS_TIME_ELLIPSIS = 4 @@ -29,9 +23,9 @@ const FIRST_POSITION = 0 * {@see Feature} */ export function getFeaturesFromPositions( - positions: VesselPosition[], - vesselCompositeIdentifier: VesselCompositeIdentifier -): (VesselPointFeature | VesselArrowFeature | VesselLineFeature)[] { + positions: Vessel.VesselPosition[], + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier +): (Vessel.VesselPointFeature | Vessel.VesselArrowFeature | Vessel.VesselLineFeature)[] { const hasOnlyOnePosition = positions?.length === 1 if (hasOnlyOnePosition) { return getPositionFeatureOfIndex(positions, vesselCompositeIdentifier, FIRST_POSITION) @@ -47,7 +41,7 @@ export function getFeaturesFromPositions( return getPositionFeatureOfIndex(positions, vesselCompositeIdentifier, positions.length - 1) } - let features: (VesselPointFeature | VesselArrowFeature | VesselLineFeature)[] = [] + let features: (Vessel.VesselPointFeature | Vessel.VesselArrowFeature | Vessel.VesselLineFeature)[] = [] const positionsPointFeatures = buildPointFeatures(positions, vesselCompositeIdentifier) features = features.concat(positionsPointFeatures) @@ -61,8 +55,8 @@ export function getFeaturesFromPositions( } function getPositionFeatureOfIndex( - positions: VesselPosition[], - vesselCompositeIdentifier: VesselCompositeIdentifier, + positions: Vessel.VesselPosition[], + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier, index: number ) { const position = positions[index] @@ -77,27 +71,27 @@ function getPositionFeatureOfIndex( } function buildPointFeatures( - positions: VesselPosition[], - vesselCompositeIdentifier: VesselCompositeIdentifier -): VesselPointFeature[] { + positions: Vessel.VesselPosition[], + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier +): Vessel.VesselPointFeature[] { return positions .map((position, currentIndex) => { const coordinates = transform([position.longitude, position.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION) return buildPointFeature(coordinates, currentIndex, position, vesselCompositeIdentifier) }) - .filter((circlePoint): circlePoint is VesselPointFeature => circlePoint !== null) + .filter((circlePoint): circlePoint is Vessel.VesselPointFeature => circlePoint !== null) } function buildPointFeature( coordinates: Coordinate, index: number, - position: VesselPosition, - vesselCompositeIdentifier: VesselCompositeIdentifier -): VesselPointFeature { + position: Vessel.VesselPosition, + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier +): Vessel.VesselPointFeature { const pointFeature = new Feature({ geometry: new Point(coordinates) - }) as VesselPointFeature + }) as Vessel.VesselPointFeature // TODO Properties are removed when included directly in the `geometryOrProperties` of the Feature instantiation pointFeature.name = `${LayerProperties.VESSEL_TRACK.code}:position:${index}` pointFeature.course = position.course @@ -114,8 +108,8 @@ function buildPointFeature( function buildArrowPointFeatures( vesselTrackLines, - vesselCompositeIdentifier: VesselCompositeIdentifier -): VesselArrowFeature[] { + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier +): Vessel.VesselArrowFeature[] { return vesselTrackLines .map((feature, index) => { const pointsDistance = calculatePointsDistance( @@ -131,7 +125,7 @@ function buildArrowPointFeatures( const arrowFeature = new Feature({ geometry: new Point(arrowPointCoordinates) - }) as VesselArrowFeature + }) as Vessel.VesselArrowFeature // TODO Properties are removed when included directly in the `geometryOrProperties` of the Feature instantiation arrowFeature.name = `${LayerProperties.VESSEL_TRACK.code}:arrow:${index}` arrowFeature.course = feature.course @@ -152,9 +146,9 @@ function buildArrowPointFeatures( } function buildLineStringFeatures( - positions: VesselPosition[], - vesselCompositeIdentifier: VesselCompositeIdentifier -): VesselLineFeature[] { + positions: Vessel.VesselPosition[], + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier +): Vessel.VesselLineFeature[] { return positions .filter(position => position) .map((firstPosition, index) => { @@ -189,7 +183,7 @@ function buildLineStringFeatures( const feature = new Feature({ geometry: new LineString([firstPoint, secondPoint]) - }) as VesselLineFeature + }) as Vessel.VesselLineFeature // TODO Properties are removed when included directly in the `geometryOrProperties` of the Feature instantiation feature.firstPositionDate = firstPositionDate feature.secondPositionDate = secondPositionDate @@ -205,10 +199,10 @@ function buildLineStringFeatures( return feature }) - .filter((lineString): lineString is VesselLineFeature => lineString !== null) + .filter((lineString): lineString is Vessel.VesselLineFeature => lineString !== null) } -export function getTrackType(positions: VesselPosition[], isTimeEllipsis) { +export function getTrackType(positions: Vessel.VesselPosition[], isTimeEllipsis) { const [firstPosition, secondPosition] = positions if (isTimeEllipsis) { diff --git a/frontend/src/domain/entities/vessel/types.ts b/frontend/src/domain/entities/vessel/types.ts index 23932f92b0..7082a1136c 100644 --- a/frontend/src/domain/entities/vessel/types.ts +++ b/frontend/src/domain/entities/vessel/types.ts @@ -1,152 +1,9 @@ // TODO This should be moved to `entities/vessel/mission.types.ts` import type { VesselTrackDepth } from '../vesselTrackDepth' -import type { PendingAlertValueType } from '@features/Alert/types' -import type { ReportingType } from '@features/Reporting/types' import type { SelectableVesselTrackDepth } from '@features/Vessel/components/VesselSidebar/actions/TrackRequest/types' import type { Vessel } from '@features/Vessel/Vessel.types' import type { Coordinate } from 'ol/coordinate' -import type Feature from 'ol/Feature' -import type LineString from 'ol/geom/LineString' -import type Point from 'ol/geom/Point' - -/** - * The vessel id number used to identify vessels entered in the NAVPRO French database - * It is used for : - * - Controls - * - Beacons - * - Vessel information - * - * i.e: 20569 - */ -export type VesselId = number - -/** - * The vessel composite key/identifier used to identify all vessels - * by concatenating : - * - internalReferenceNumber - * - ircs - * - externalReferenceNumber - * - * The result is :`internalReferenceNumber/ircs/externalReferenceNumber` - * - * i.e: "FAK000999999/CALLME/DONTSINK" - */ -export type VesselCompositeIdentifier = string - -/** - * The vessel feature id is the vessel composite key concatenated to the `vessel:` string. - * It is mainly used to distinct OpenLayers objects (called Features) - * - * i.e: "vessel:FAK000999999/CALLME/DONTSINK" - * @see VesselCompositeIdentifier - */ -export type VesselFeatureId = string - -export enum VesselIdentifier { - EXTERNAL_REFERENCE_NUMBER = 'EXTERNAL_REFERENCE_NUMBER', - INTERNAL_REFERENCE_NUMBER = 'INTERNAL_REFERENCE_NUMBER', - IRCS = 'IRCS' -} - -export enum NetworkType { - CELLULAR = 'CELLULAR', - SATELLITE = 'SATELLITE' -} - -/** - * @deprecated - * Use `Vessel.VesselIdentity` BUT BE CAREFUL: there are not exactly equal regarding nullish and optional props. - * There is a `getVesselIdentityFromLegacyVesselIdentity()` util to convert from one to another if needed. - */ -export type VesselIdentity = { - beaconNumber?: number | null | undefined - districtCode?: string | null | undefined - externalReferenceNumber: string | null | undefined - flagState: string - internalReferenceNumber: string | null | undefined - ircs: string | null | undefined - mmsi?: string | null | undefined - vesselId?: VesselId | null | undefined - vesselIdentifier?: VesselIdentifier | null | undefined - vesselLength?: number | null | undefined - vesselName?: string | null | undefined -} - -export type VesselAndPositions = { - positions: VesselPosition[] - vessel: Vessel.EnrichedVessel -} - -export type VesselLastPosition = { - alerts: Array | null | undefined - beaconMalfunctionId: number | null - beaconNumber?: number | null - course: number - dateTime: string - departureDateTime: string - destination: string - detectabilityRiskFactor: number - district: string - districtCode: string - emissionPeriod: number - estimatedCurrentLatitude: number - estimatedCurrentLongitude: number - externalReferenceNumber: string - flagState: string - from: string - gearOnboard: DeclaredLogbookGear[] - impactRiskFactor: number - internalReferenceNumber: string - ircs: string - isAtPort: boolean - lastControlDateTime: string - lastControlInfraction: boolean - lastLogbookMessageDateTime: string - latitude: number - length: number - longitude: number - mmsi: string - positionType: string - postControlComment: number - probabilityRiskFactor: number - registryPortLocode: string - registryPortName: string - reportings: ReportingType[] - riskFactor: number - segments: string[] - speciesOnboard: DeclaredLogbookSpecies[] - speed: number - totalWeightOnboard: number - tripNumber: number - underCharter: boolean - vesselId: number | null - vesselIdentifier: VesselIdentifier - vesselName: string - width: number -} - -export type VesselPosition = { - course: number - dateTime: string - destination: string | null - externalReferenceNumber: string | null - flagState: string - from: string - internalReferenceNumber: string | null - ircs: string | null - isAtPort: boolean | null - isFishing: boolean | null - isManual: boolean | null - latitude: number - longitude: number - mmsi: string | null - networkType: NetworkType | null - positionType: string - speed: number - tripNumber: number | null - vesselName: string -} export type FishingActivityShowedOnMap = { /** The coordinates of the fishing activity */ @@ -163,31 +20,17 @@ export type FishingActivityShowedOnMap = { name: string } -export type DeclaredLogbookGear = { - dimension: number - gear: string - mesh: number -} - export type ShowedVesselTrack = { coordinates: Coordinate course: number extent: number[] | null isDefaultTrackDepth: boolean - positions: VesselPosition[] + positions: Vessel.VesselPosition[] toHide: boolean toShow: boolean toZoom: boolean vesselCompositeIdentifier: string - vesselIdentity: VesselIdentity -} - -// TODO Exist both in Vessel and Species. -export type DeclaredLogbookSpecies = { - faoZone: string - gear: string - species: string - weight: number + vesselIdentity: Vessel.VesselIdentity } export type TrackRequest = TrackRequestCustom | TrackRequestPredefined @@ -201,48 +44,3 @@ export type TrackRequestPredefined = { beforeDateTime: null trackDepth: SelectableVesselTrackDepth } - -export interface VesselPointFeature extends Feature { - course?: number - dateTime?: string - name?: string - positionType?: string - speed?: number -} - -export interface VesselLineFeature extends Feature { - course?: number - dateTime?: string - firstPositionDate?: Date - isTimeEllipsis?: boolean - positionType?: string - secondPositionDate?: Date - speed?: number - trackType?: TrackTypeRecordItem -} - -export interface VesselArrowFeature extends Feature { - course?: number - name?: string -} - -export type TrackTypeRecordItem = { - arrow: string - code: string - color: string - description: string -} - -export type VesselEnhancedLastPositionWebGLObject = Vessel.VesselEnhancedObject & { - coordinates: number[] - course: number - filterPreview: number // 0 is False, 1 is True - for WebGL - hasBeaconMalfunction: boolean - isAtPort: boolean - isFiltered: number // 0 is False, 1 is True - for WebGL - lastPositionSentAt: number - speed: number - vesselFeatureId: VesselFeatureId -} - -export type VesselLastPositionFeature = Feature & VesselEnhancedLastPositionWebGLObject diff --git a/frontend/src/domain/entities/vessel/vessel.ts b/frontend/src/domain/entities/vessel/vessel.ts index b5b31c6f09..891cee5465 100644 --- a/frontend/src/domain/entities/vessel/vessel.ts +++ b/frontend/src/domain/entities/vessel/vessel.ts @@ -1,13 +1,13 @@ +import { BaseLayer } from '@features/Map/constants' +import { MonitorFishMap } from '@features/Map/Map.types' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import countries from 'i18n-iso-countries' import { VesselLabel } from './label/types' -import { BaseLayer } from '../../../features/Map/constants' -import { MonitorFishMap } from '../../../features/Map/Map.types' -import type { ShowedVesselTrack, VesselCompositeIdentifier, VesselIdentity } from './types' -import type { Reporting } from '../../../features/Reporting/types' -import type { Vessel as VesselTypes } from '../../../features/Vessel/Vessel.types' +import type { ShowedVesselTrack } from './types' import type { PartialExcept } from '../../../types' +import type { Vessel } from '@features/Vessel/Vessel.types' export const VESSEL_ALERT_STYLE = 1 export const VESSEL_INFRACTION_SUSPICION_STYLE = 1 @@ -15,7 +15,7 @@ export const VESSEL_BEACON_MALFUNCTION_STYLE = 1 export const VESSEL_ALERT_AND_BEACON_MALFUNCTION = 1 export const VESSEL_SELECTOR_STYLE = 200 -export class Vessel { +export class VesselFeature { static vesselIsMovingSpeed = 0.1 static getVesselFeatureId(vessel) { @@ -41,7 +41,7 @@ export class Vessel { */ static getVesselFeatureLabel( feature: PartialExcept< - VesselTypes.VesselEnhancedObject, + Vessel.VesselEnhancedObject, | 'beaconMalfunctionId' | 'dateTime' | 'detectabilityRiskFactor' @@ -66,13 +66,15 @@ export class Vessel { vesselsLastPositionVisibility: MonitorFishMap.LastPositionVisibility } ): { - labelText: string | null - riskFactor: { - detectabilityRiskFactor: number - globalRisk: number - impactRiskFactor: number - probabilityRiskFactor: number - } | null + labelText: string | undefined + riskFactor: + | { + detectabilityRiskFactor: number + globalRisk: number + impactRiskFactor: number + probabilityRiskFactor: number + } + | undefined } { const { hideVesselsAtPort, @@ -83,18 +85,19 @@ export class Vessel { } = options const vesselDate = new Date(feature.dateTime) const vesselIsHidden = new Date() - const hasBeenControlledLastFiveYears = - new Date(feature.lastControlDateTime).getTime() > new Date(vesselIsHidden.getUTCFullYear() - 5, 0, 1).getTime() + const hasBeenControlledLastFiveYears = feature.lastControlDateTime + ? new Date(feature.lastControlDateTime).getTime() > new Date(vesselIsHidden.getUTCFullYear() - 5, 0, 1).getTime() + : false vesselIsHidden.setHours(vesselIsHidden.getHours() - vesselsLastPositionVisibility.hidden) // TODO Properly type this const. const label: { - labelText: string | null - riskFactor: any | null + labelText: string | undefined + riskFactor: any | undefined underCharter: any } = { - labelText: null, - riskFactor: null, + labelText: undefined, + riskFactor: undefined, underCharter: feature.underCharter } @@ -117,7 +120,7 @@ export class Vessel { break } case VesselLabel.VESSEL_NATIONALITY: { - label.labelText = feature.flagState ? (countries.getName(feature.flagState, 'fr') ?? null) : null + label.labelText = feature.flagState ? (countries.getName(feature.flagState, 'fr') ?? undefined) : undefined break } case VesselLabel.VESSEL_FLEET_SEGMENT: { @@ -125,7 +128,7 @@ export class Vessel { break } default: - label.labelText = null + label.labelText = undefined } } @@ -151,35 +154,10 @@ export class Vessel { selectedBaseLayer === BaseLayer.SATELLITE.code || selectedBaseLayer === BaseLayer.DARK.code } -/** @deprecated Use `extractVesselIdentityProps()` from `@features/Vessel/utils`. */ -export const getOnlyVesselIdentityProperties = ( - vessel: - | VesselTypes.VesselEnhancedObject - | VesselTypes.SelectedVessel - | VesselTypes.EnrichedVessel - | Reporting.Reporting -): VesselIdentity => ({ - beaconNumber: 'beaconNumber' in vessel && !!vessel.beaconNumber ? vessel.beaconNumber : null, - districtCode: 'districtCode' in vessel && !!vessel.districtCode ? vessel.districtCode : null, - externalReferenceNumber: vessel.externalReferenceNumber ?? null, - flagState: vessel.flagState, - internalReferenceNumber: vessel.internalReferenceNumber ?? null, - ircs: 'ircs' in vessel && !!vessel.ircs ? vessel.ircs : null, - mmsi: 'mmsi' in vessel && !!vessel.mmsi ? vessel.mmsi : null, - vesselId: 'vesselId' in vessel && !!vessel.vesselId ? vessel.vesselId : null, - vesselIdentifier: 'vesselIdentifier' in vessel && !!vessel.vesselIdentifier ? vessel.vesselIdentifier : null, - vesselName: vessel.vesselName ?? null -}) - -export const getVesselCompositeIdentifier: (vessel) => VesselCompositeIdentifier = vessel => - `${vessel.internalReferenceNumber ?? 'UNKNOWN'}/${vessel.ircs ?? 'UNKNOWN'}/${ - vessel.externalReferenceNumber ?? 'UNKNOWN' - }` - /** * Returns true if there is at least one vessel track or vessel selected * @param {Object.} vesselsTracksShowed - * @param {VesselIdentity | null} selectedVesselIdentity + * @param {Vessel.VesselIdentity | null} selectedVesselIdentity * @return {boolean} */ export const atLeastOneVesselSelected = (vesselsTracksShowed, selectedVesselIdentity) => @@ -191,9 +169,9 @@ export const atLeastOneVesselSelected = (vesselsTracksShowed, selectedVesselIden * - The vessel is selected (`selectedVesselIdentity` param) */ export const vesselIsShowed = ( - vesselIdentity: VesselIdentity, + vesselIdentity: Vessel.VesselIdentity, vesselsTracksShowed: ShowedVesselTrack, - selectedVesselIdentity: VesselIdentity + selectedVesselIdentity: Vessel.VesselIdentity ): boolean => vesselsAreEquals(vesselIdentity, selectedVesselIdentity) || vesselsAreEquals(vesselIdentity, vesselsTracksShowed.vesselIdentity) @@ -269,8 +247,6 @@ export enum VesselLocation { SEA = 'SEA' } -export const TEMPORARY_VESSEL_TRACK = 'temp' - export enum VesselSidebarTab { CONTROLS = 'CONTROLS', ERSVMS = 'ERSVMS', @@ -289,11 +265,16 @@ export enum FishingActivitiesTab { * An unknown vessel to use when no vessel is found * @see https://github.com/MTES-MCT/monitorfish/pull/2045/files#diff-bcb14fe011ecfdcd40c018e16578c292cd8ba9d5bd39ad19600172865980caadR104 */ -export const UNKNOWN_VESSEL: VesselIdentity = { +export const UNKNOWN_VESSEL: Vessel.VesselIdentity = { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'UNKNOWN', flagState: 'UNDEFINED', internalReferenceNumber: 'UNKNOWN', ircs: 'UNKNOWN', + mmsi: undefined, vesselId: -1, + vesselIdentifier: undefined, + vesselLength: undefined, vesselName: 'UNKNOWN' } diff --git a/frontend/src/domain/entities/vesselLabelLine.ts b/frontend/src/domain/entities/vesselLabelLine.ts index 34dcae4570..99035da8f9 100644 --- a/frontend/src/domain/entities/vesselLabelLine.ts +++ b/frontend/src/domain/entities/vesselLabelLine.ts @@ -1,9 +1,8 @@ import { LayerProperties } from '@features/Map/constants' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import Feature from 'ol/Feature' import LineString from 'ol/geom/LineString' -import { getVesselCompositeIdentifier } from './vessel/vessel' - export class VesselLabelLine { static opacityProperty = 'opacity' diff --git a/frontend/src/domain/entities/vesselTrackDepth.ts b/frontend/src/domain/entities/vesselTrackDepth.ts index 3232dd90e6..3001124cae 100644 --- a/frontend/src/domain/entities/vesselTrackDepth.ts +++ b/frontend/src/domain/entities/vesselTrackDepth.ts @@ -1,7 +1,8 @@ import { NoDEPFoundError } from '../../errors/NoDEPFoundError' import { NoPositionsFoundError } from '../../errors/NoPositionsFoundError' -import type { TrackRequest, TrackRequestPredefined, VesselPosition } from './vessel/types' +import type { TrackRequest, TrackRequestPredefined } from './vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export enum VesselTrackDepth { CUSTOM = 'CUSTOM', @@ -71,7 +72,7 @@ export const getTrackRequestFromDates = (afterDateTime: Date, beforeDateTime: Da }) export function throwCustomErrorFromAPIFeedback( - positions: VesselPosition[], + positions: Vessel.VesselPosition[], isTrackDepthModified: boolean, isFromUserAction: boolean ) { diff --git a/frontend/src/domain/shared_slices/FavoriteVessel.ts b/frontend/src/domain/shared_slices/FavoriteVessel.ts index 8c487307b3..a05fb3253c 100644 --- a/frontend/src/domain/shared_slices/FavoriteVessel.ts +++ b/frontend/src/domain/shared_slices/FavoriteVessel.ts @@ -1,20 +1,17 @@ // TODO Move that into Vessel slice, or into a User slice if there are other per-user customizable data. +import { extractVesselIdentityProps, getVesselCompositeIdentifier } from '@features/Vessel/utils' import { createSlice } from '@reduxjs/toolkit' import { getLocalStorageState } from '../../utils' -import { - getOnlyVesselIdentityProperties, - getVesselCompositeIdentifier, - vesselsAreEquals -} from '../entities/vessel/vessel' +import { vesselsAreEquals } from '../entities/vessel/vessel' -import type { VesselIdentity } from '../entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' const FAVORITE_VESSELS_LOCAL_STORAGE_KEY = 'favoriteVessels' export type FavoriteVesselState = { - favorites: VesselIdentity[] + favorites: Vessel.VesselIdentity[] } const INITIAL_STATE: FavoriteVesselState = { favorites: getLocalStorageState([], FAVORITE_VESSELS_LOCAL_STORAGE_KEY) @@ -32,7 +29,7 @@ const favoriteVesselSlice = createSlice({ * @param {{payload: Vessel}} action - The vessel */ addVesselToFavorites(state, action) { - const newFavorite = getOnlyVesselIdentityProperties(action.payload) + const newFavorite = extractVesselIdentityProps(action.payload) // Remove vessel if found state.favorites = state.favorites.filter(favoriteVessel => !vesselsAreEquals(favoriteVessel, newFavorite)) diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/domain/shared_slices/Global.ts index a8b772bd0c..c8e4f3ec16 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/domain/shared_slices/Global.ts @@ -1,8 +1,9 @@ import { UserType } from '@features/BeaconMalfunction/constants' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { createSlice } from '@reduxjs/toolkit' import { getLocalStorageState } from '../../utils' -import { getOnlyVesselIdentityProperties, vesselsAreEquals } from '../entities/vessel/vessel' +import { vesselsAreEquals } from '../entities/vessel/vessel' import type { MapBox } from '@features/Map/constants' import type { PayloadAction } from '@reduxjs/toolkit' @@ -60,7 +61,7 @@ export const globalSlice = createSlice({ * @deprecated Can be replaced by a non-Redux function once `@features/VesselSearch` is replaced with `@features/Vessel/components/VesselSearch`. */ addSearchedVessel(state, action) { - const vesselIdentityToAdd = getOnlyVesselIdentityProperties(action.payload) + const vesselIdentityToAdd = extractVesselIdentityProps(action.payload) // Remove vessel if already in the list state.lastSearchedVessels = state.lastSearchedVessels.filter( diff --git a/frontend/src/domain/use_cases/alert/silenceAlert.ts b/frontend/src/domain/use_cases/alert/silenceAlert.ts index 0ea7fb7a55..0d2cf32da6 100644 --- a/frontend/src/domain/use_cases/alert/silenceAlert.ts +++ b/frontend/src/domain/use_cases/alert/silenceAlert.ts @@ -9,7 +9,7 @@ import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeat import { silenceAlertFromAPI } from '../../../api/alert' import { deleteListItems } from '../../../utils/deleteListItems' -import { Vessel } from '../../entities/vessel/vessel' +import { VesselFeature } from '../../entities/vessel/vessel' import { setError } from '../../shared_slices/Global' import type { MainAppThunk } from '../../../store' @@ -43,7 +43,7 @@ export const silenceAlert = removeVesselAlertAndUpdateReporting({ alertType: silencedAlert.value.type, isValidated: false, - vesselFeatureId: Vessel.getVesselFeatureId(silencedAlert) + vesselFeatureId: VesselFeature.getVesselFeatureId(silencedAlert) }) ) diff --git a/frontend/src/domain/use_cases/alert/validateAlert.ts b/frontend/src/domain/use_cases/alert/validateAlert.ts index 001e2f5f10..0a45f8b6bc 100644 --- a/frontend/src/domain/use_cases/alert/validateAlert.ts +++ b/frontend/src/domain/use_cases/alert/validateAlert.ts @@ -7,7 +7,7 @@ import { vesselApi } from '@features/Vessel/vesselApi' import { validateAlertFromAPI } from '../../../api/alert' import { deleteListItems } from '../../../utils/deleteListItems' import { updateListItemsProp } from '../../../utils/updateListItemsProp' -import { Vessel } from '../../entities/vessel/vessel' +import { VesselFeature } from '../../entities/vessel/vessel' import { setError } from '../../shared_slices/Global' import type { MainAppThunk } from '../../../store' @@ -40,7 +40,7 @@ export const validateAlert = removeVesselAlertAndUpdateReporting({ alertType: validatedAlert.value?.type, isValidated: true, - vesselFeatureId: Vessel.getVesselFeatureId(validatedAlert) + vesselFeatureId: VesselFeature.getVesselFeatureId(validatedAlert) }) ) dispatch(renderVesselFeatures()) diff --git a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts b/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts index f6a591de74..ffe8bd934f 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts +++ b/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts @@ -1,8 +1,8 @@ +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { openBeaconMalfunction } from './openBeaconMalfunction' import { getVesselBeaconsMalfunctionsFromAPI } from '../../../api/beaconMalfunction' -import { getOnlyVesselIdentityProperties } from '../../entities/vessel/vessel' import { loadVesselBeaconMalfunctions, resetVesselBeaconMalfunctionsResumeAndHistory, @@ -37,7 +37,7 @@ export const getVesselBeaconMalfunctions = (isFromUserAction: boolean) => async dispatch( setVesselBeaconMalfunctionsResumeAndHistory({ ...vesselBeaconsMalfunctions, - vesselIdentity: getOnlyVesselIdentityProperties(selectedVessel) + vesselIdentity: extractVesselIdentityProps(selectedVessel) }) ) diff --git a/frontend/src/domain/use_cases/vessel/getFilteredVessels.ts b/frontend/src/domain/use_cases/vessel/getFilteredVessels.ts index 560907c2e9..a3b4a5360b 100644 --- a/frontend/src/domain/use_cases/vessel/getFilteredVessels.ts +++ b/frontend/src/domain/use_cases/vessel/getFilteredVessels.ts @@ -4,7 +4,7 @@ import VectorSource from 'ol/source/Vector' import { MonitorFishWorker } from '../../../workers/MonitorFishWorker' -import type { VesselEnhancedLastPositionWebGLObject } from '../../entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' const vectorSource = new VectorSource({ format: new GeoJSON({ @@ -13,7 +13,7 @@ const vectorSource = new VectorSource({ }) }) -export const getFilteredVessels = (vessels: VesselEnhancedLastPositionWebGLObject[], filters) => async () => { +export const getFilteredVessels = (vessels: Vessel.VesselEnhancedLastPositionWebGLObject[], filters) => async () => { const monitorFishWorker = await MonitorFishWorker const workerFilters = getFiltersWithoutZonesSelected(filters) diff --git a/frontend/src/domain/use_cases/vessel/hideVesselTrack.ts b/frontend/src/domain/use_cases/vessel/hideVesselTrack.ts index afe6c4f2dd..c1011c27ee 100644 --- a/frontend/src/domain/use_cases/vessel/hideVesselTrack.ts +++ b/frontend/src/domain/use_cases/vessel/hideVesselTrack.ts @@ -1,10 +1,10 @@ import { updateVesselTrackAsToHide } from '@features/Vessel/slice' -import type { VesselCompositeIdentifier } from '../../entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' /** * Hide a specified vessel track on map */ -export const hideVesselTrack = (vesselCompositeIdentifier: VesselCompositeIdentifier) => dispatch => { +export const hideVesselTrack = (vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier) => dispatch => { dispatch(updateVesselTrackAsToHide(vesselCompositeIdentifier)) } diff --git a/frontend/src/domain/use_cases/vessel/showVessel.ts b/frontend/src/domain/use_cases/vessel/showVessel.ts index c4555989fd..15d4afe6c6 100644 --- a/frontend/src/domain/use_cases/vessel/showVessel.ts +++ b/frontend/src/domain/use_cases/vessel/showVessel.ts @@ -4,23 +4,27 @@ import { doNotAnimate } from '@features/Map/slice' import { loadingVessel, resetLoadingVessel, setSelectedVessel, vesselSelectors } from '@features/Vessel/slice' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { captureMessage } from '@sentry/react' +import { omit } from 'lodash' -import { Vessel } from '../../entities/vessel/vessel' +import { VesselFeature } from '../../entities/vessel/vessel' import { getCustomOrDefaultTrackRequest, throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { displayedComponentActions } from '../../shared_slices/DisplayedComponent' import { displayedErrorActions } from '../../shared_slices/DisplayedError' import { addSearchedVessel, removeError, setError } from '../../shared_slices/Global' import { displayOrLogError } from '../error/displayOrLogError' -import type { VesselIdentity } from '../../entities/vessel/types' -import type { Vessel as VesselTypes } from '@features/Vessel/Vessel.types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { MainAppThunk } from '@store' /** * Show a specified vessel track on map and on the vessel right sidebar */ export const showVessel = - (vesselIdentity: VesselIdentity, isFromSearch: boolean, isFromUserAction: boolean): MainAppThunk> => + ( + vesselIdentity: Vessel.VesselIdentity, + isFromSearch: boolean, + isFromUserAction: boolean + ): MainAppThunk> => async (dispatch, getState) => { try { const { fishingActivities, map, vessel } = getState() @@ -36,8 +40,8 @@ export const showVessel = }) ) - const vesselFeatureId = Vessel.getVesselFeatureId(vesselIdentity) - const selectedVesselLastPosition: VesselTypes.VesselEnhancedObject | undefined = vessels.find( + const vesselFeatureId = VesselFeature.getVesselFeatureId(vesselIdentity) + const selectedVesselLastPosition: Vessel.VesselEnhancedObject | undefined = vessels.find( lastPosition => lastPosition.vesselFeatureId === vesselFeatureId ) @@ -75,7 +79,7 @@ export const showVessel = // As a safeguard, the VesselIdentity is added as a base object (in case no last position and no vessel are found) ...vesselIdentity, // If we found a last position, we enrich the vessel - ...selectedVesselLastPosition, + ...omit(selectedVesselLastPosition, ['riskFactor']), // If we found a vessel from the vessels table, we enrich the vessel ...vesselAndPositions?.vessel } @@ -84,7 +88,7 @@ export const showVessel = dispatch( setSelectedVessel({ positions: vesselAndPositions.positions, - vessel: selectedVessel as VesselTypes.SelectedVessel + vessel: selectedVessel as Vessel.SelectedVessel }) ) } catch (error) { @@ -100,7 +104,7 @@ export const showVessel = } } -function dispatchLoadingVessel(dispatch, isFromUserAction: boolean, vesselIdentity: VesselIdentity) { +function dispatchLoadingVessel(dispatch, isFromUserAction: boolean, vesselIdentity: Vessel.VesselIdentity) { dispatch(doNotAnimate(!isFromUserAction)) dispatch(removeError()) dispatch( diff --git a/frontend/src/domain/use_cases/vessel/showVesselFromBeaconMalfunctionsKanban.ts b/frontend/src/domain/use_cases/vessel/showVesselFromBeaconMalfunctionsKanban.ts index d772d59790..ce2ea2ff3e 100644 --- a/frontend/src/domain/use_cases/vessel/showVesselFromBeaconMalfunctionsKanban.ts +++ b/frontend/src/domain/use_cases/vessel/showVesselFromBeaconMalfunctionsKanban.ts @@ -1,5 +1,6 @@ import { END_OF_MALFUNCTION_REASON_RECORD } from '@features/BeaconMalfunction/constants' import { vesselActions } from '@features/Vessel/slice' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { VesselSidebarTab } from 'domain/entities/vessel/vessel' import { VesselTrackDepth } from 'domain/entities/vesselTrackDepth' @@ -27,7 +28,8 @@ export const showVesselFromBeaconMalfunctionsKanban = (beaconMalfunction, openVM await dispatch(vesselActions.setSelectedVesselCustomTrackRequest(trackRequest)) } - await dispatch(showVessel(beaconMalfunction, false, true)) + const identity = extractVesselIdentityProps(beaconMalfunction) + await dispatch(showVessel(identity, false, true)) if (openVMRERSTab) { dispatch(vesselActions.setSelectedVesselSidebarTab(VesselSidebarTab.ERSVMS)) diff --git a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts index 6e306fc26e..d420eb394a 100644 --- a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts +++ b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts @@ -1,14 +1,15 @@ import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '@features/Map/constants' import { doNotAnimate } from '@features/Map/slice' import { addVesselTrackShowed, resetLoadingVessel } from '@features/Vessel/slice' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { transform } from 'ol/proj' import { getVesselPositionsFromAPI } from '../../../api/vessel' -import { getVesselCompositeIdentifier } from '../../entities/vessel/vessel' import { getCustomOrDefaultTrackRequest, throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { removeError, setError } from '../../shared_slices/Global' -import type { TrackRequest, VesselIdentity } from '../../entities/vessel/types' +import type { TrackRequest } from '../../entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { MainAppThunk } from '@store' /** @@ -16,7 +17,7 @@ import type { MainAppThunk } from '@store' */ export const showVesselTrack = ( - vesselIdentity: VesselIdentity, + vesselIdentity: Vessel.VesselIdentity, isFromUserAction: boolean, trackRequest: TrackRequest | null, hasZoom: boolean = false diff --git a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts index 82ceec3047..1de1e43d44 100644 --- a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts +++ b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts @@ -12,14 +12,15 @@ import { throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth import { removeError, setError } from '../../shared_slices/Global' import type { MainAppDispatch, MainAppThunk } from '../../../store' -import type { TrackRequest, VesselIdentity } from '../../entities/vessel/types' +import type { TrackRequest } from '../../entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' /** * Modify the vessel track depth on map */ export const updateSelectedVesselTrackRequest = ( - vesselIdentity: VesselIdentity, + vesselIdentity: Vessel.VesselIdentity, trackRequest: TrackRequest, withoutFishingMessagesRerendering: boolean = false ): MainAppThunk => diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/AlertListAndReportingList/PendingAlertRow.tsx b/frontend/src/features/Alert/components/SideWindowAlerts/AlertListAndReportingList/PendingAlertRow.tsx index 20ab6be8b0..384f249284 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/AlertListAndReportingList/PendingAlertRow.tsx +++ b/frontend/src/features/Alert/components/SideWindowAlerts/AlertListAndReportingList/PendingAlertRow.tsx @@ -1,5 +1,6 @@ import { COLORS } from '@constants/constants' import { Flag } from '@features/Vessel/components/VesselList/tableCells' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import countries from 'i18n-iso-countries' @@ -93,7 +94,8 @@ export function PendingAlertRow({ alt="Voir sur la carte" data-cy="side-window-alerts-show-vessel" onClick={() => { - dispatch(showVessel(alert, false, true)) + const identity = extractVesselIdentityProps(alert) + dispatch(showVessel(identity, false, true)) }} src={`${baseUrl}/Icone_voir_sur_la_carte.png`} style={showIconStyle} diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx index f08843921b..734f8c885c 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx +++ b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx @@ -7,22 +7,22 @@ import styled from 'styled-components' import { UNKNOWN_VESSEL } from '../../../../../../domain/entities/vessel/vessel' import { VesselSearch } from '../../../../../VesselSearch' -import type { VesselIdentity } from '../../../../../../domain/entities/vessel/types' import type { SilencedAlertFormValues } from '../types' +import type { Vessel } from '@features/Vessel/Vessel.types' export function VesselField() { const { errors, setValues, values } = useFormikContext() const { newWindowContainerRef } = useNewWindow() - const defaultValue: VesselIdentity = useMemo( + const defaultValue: Partial = useMemo( () => ({ - externalReferenceNumber: values.externalReferenceNumber ?? null, + externalReferenceNumber: values.externalReferenceNumber ?? undefined, flagState: values.flagState ?? '', - internalReferenceNumber: values.internalReferenceNumber ?? null, - ircs: values.ircs ?? null, - vesselId: values.vesselId ?? null, - vesselIdentifier: values.vesselIdentifier ?? null, - vesselName: values.vesselName ?? null + internalReferenceNumber: values.internalReferenceNumber ?? undefined, + ircs: values.ircs ?? undefined, + vesselId: values.vesselId ?? undefined, + vesselIdentifier: values.vesselIdentifier ?? undefined, + vesselName: values.vesselName ?? undefined }), [ values.flagState, @@ -35,14 +35,14 @@ export function VesselField() { ] ) - const handleVesselSearchChange = (nextVessel: VesselIdentity | undefined) => { + const handleVesselSearchChange = (nextVessel: Partial | undefined) => { if (!nextVessel) { setValues({ ...values, - externalReferenceNumber: null, - internalReferenceNumber: null, - ircs: null, - vesselId: null + externalReferenceNumber: undefined, + internalReferenceNumber: undefined, + ircs: undefined, + vesselId: undefined }) return @@ -58,11 +58,11 @@ export function VesselField() { setValues({ ...values, - externalReferenceNumber: nextVessel.externalReferenceNumber ?? null, + externalReferenceNumber: nextVessel.externalReferenceNumber ?? undefined, flagState: nextVessel.flagState?.toUpperCase(), - internalReferenceNumber: nextVessel.internalReferenceNumber ?? null, - ircs: nextVessel.ircs ?? null, - vesselId: nextVessel.vesselId ?? null, + internalReferenceNumber: nextVessel.internalReferenceNumber ?? undefined, + ircs: nextVessel.ircs ?? undefined, + vesselId: nextVessel.vesselId ?? undefined, vesselIdentifier: nextVessel.vesselIdentifier ?? undefined, vesselName: nextVessel.vesselName }) diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/index.tsx b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/index.tsx index b2257975a2..aa5388af84 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/index.tsx +++ b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/index.tsx @@ -1,6 +1,7 @@ import { getAlertNameFromType } from '@features/Alert/components/SideWindowAlerts/AlertListAndReportingList/utils' import { Flag } from '@features/Vessel/components/VesselList/tableCells' import { sortArrayByColumn, SortType } from '@features/Vessel/components/VesselList/tableSort' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { Button, CustomSearch, Icon } from '@mtes-mct/monitor-ui' @@ -136,7 +137,8 @@ export function SilencedAlerts() { alt="Voir sur la carte" data-cy="side-window-silenced-alerts-show-vessel" onClick={() => { - dispatch(showVessel(alert, false, true)) + const identity = extractVesselIdentityProps(alert) + dispatch(showVessel(identity, false, true)) }} src={`${baseUrl}/Icone_voir_sur_la_carte.png`} title="Voir sur la carte" diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/types.ts b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/types.ts index 609be18105..fc566f5e92 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/types.ts +++ b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/types.ts @@ -3,8 +3,8 @@ import type { SilencedAlertData } from '../../../types' export type SilencedAlertFormValues = Partial export const emptySilencedAlert: SilencedAlertFormValues = { - externalReferenceNumber: null, - internalReferenceNumber: null, - ircs: null, - vesselId: null + externalReferenceNumber: undefined, + internalReferenceNumber: undefined, + ircs: undefined, + vesselId: undefined } diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/utils.ts b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/utils.ts index 775b01847d..4792f7b1be 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/utils.ts +++ b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/utils.ts @@ -25,13 +25,13 @@ export function getSilencedAlertFromSilencedAlertFormValues(formValues: Silenced } return { - externalReferenceNumber: formValues.externalReferenceNumber ?? null, + externalReferenceNumber: formValues.externalReferenceNumber, flagState: formValues.flagState, - internalReferenceNumber: formValues.internalReferenceNumber ?? null, - ircs: formValues.ircs ?? null, + internalReferenceNumber: formValues.internalReferenceNumber, + ircs: formValues.ircs, silencedBeforeDate: formValues.silencedBeforeDate, value: formValues.value, - vesselId: formValues.vesselId ?? null, + vesselId: formValues.vesselId, vesselIdentifier: formValues.vesselIdentifier, vesselName: formValues.vesselName } diff --git a/frontend/src/features/Alert/types.ts b/frontend/src/features/Alert/types.ts index c5cef8c061..483f653ff4 100644 --- a/frontend/src/features/Alert/types.ts +++ b/frontend/src/features/Alert/types.ts @@ -1,7 +1,7 @@ import { Seafront } from '@constants/seafront' -import type { VesselIdentifier, VesselIdentity } from '../../domain/entities/vessel/types' import type { MissionAction } from '@features/Mission/missionAction.types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { Except } from 'type-fest' export enum PendingAlertValueType { @@ -25,7 +25,7 @@ export type PendingAlert = { ircs: string tripNumber: string value: PendingAlertValue - vesselIdentifier: VesselIdentifier + vesselIdentifier: Vessel.VesselIdentifier vesselName: string } @@ -44,16 +44,16 @@ export type LEGACY_PendingAlert = PendingAlert & { } export type SilencedAlert = { - externalReferenceNumber: string | null + externalReferenceNumber: string | undefined flagState: string id: string - internalReferenceNumber: string | null - ircs: string | null - isReactivated: boolean | null + internalReferenceNumber: string | undefined + ircs: string | undefined + isReactivated: boolean | undefined silencedBeforeDate: string value: PendingAlertValue - vesselId: number | null - vesselIdentifier: VesselIdentifier + vesselId: number | undefined + vesselIdentifier: Vessel.VesselIdentifier vesselName: string } @@ -74,6 +74,6 @@ export type SilenceAlertQueueItem = { silencedAlertPeriodRequest: SilencedAlertPeriodRequest } -export type AlertNameAndVesselIdentity = VesselIdentity & { +export type AlertNameAndVesselIdentity = Vessel.VesselIdentity & { name: string | null | undefined } diff --git a/frontend/src/features/BeaconMalfunction/types.ts b/frontend/src/features/BeaconMalfunction/types.ts index 1eabdf6436..915729a085 100644 --- a/frontend/src/features/BeaconMalfunction/types.ts +++ b/frontend/src/features/BeaconMalfunction/types.ts @@ -4,8 +4,8 @@ import type { BeaconMalfunctionVesselStatus, EndOfBeaconMalfunctionReason } from './constants' -import type { VesselIdentity } from '../../domain/entities/vessel/types' import type { BeaconMalfunctionDetailsType } from '@features/BeaconMalfunction/components/BeaconMalfunctionBoard/utils' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { Integer } from 'type-fest' export type BeaconMalfunction = { @@ -86,7 +86,7 @@ export type VesselBeaconMalfunctionsResumeAndHistory = { current: BeaconMalfunctionResumeAndDetails | null history: BeaconMalfunctionResumeAndDetails[] resume: VesselBeaconMalfunctionsResume - vesselIdentity: VesselIdentity + vesselIdentity: Vessel.VesselIdentity } export type VesselBeaconMalfunctionsResume = { diff --git a/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/CreateOrEditFleetSegmentModal.tsx b/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/CreateOrEditFleetSegmentModal.tsx index adab60ce18..f1f79440ae 100644 --- a/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/CreateOrEditFleetSegmentModal.tsx +++ b/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/CreateOrEditFleetSegmentModal.tsx @@ -47,14 +47,14 @@ export function CreateOrEditFleetSegmentModal({ return { faoAreas: [], gears: [], - impactRiskFactor: undefined, + impactRiskFactor: 2.0, mainScipSpeciesType: undefined, maxMesh: undefined, minMesh: undefined, minShareOfTargetSpecies: undefined, priority: 0, segment: '', - segmentName: undefined, + segmentName: '', targetSpecies: [], vesselTypes: [], year diff --git a/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/schema.ts b/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/schema.ts index 7a6ea80f7d..8ecaf186db 100644 --- a/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/schema.ts +++ b/frontend/src/features/FleetSegment/components/FleetSegmentsBackoffice/schema.ts @@ -6,7 +6,7 @@ import type { FleetSegment } from '@features/FleetSegment/types' export const FLEET_SEGMENT_FORM_SCHEMA: ObjectSchema = object({ faoAreas: array(string().required()).required().default([]), gears: array(string().required()).required().default([]), - impactRiskFactor: number().min(1).max(4).default(undefined), + impactRiskFactor: number().min(1).max(4).required(), mainScipSpeciesType: mixed().oneOf(Object.values(ScipSpeciesType)).default(undefined), maxMesh: number().default(undefined), minMesh: number().default(undefined), @@ -16,7 +16,7 @@ export const FLEET_SEGMENT_FORM_SCHEMA: ObjectSchema = object({ .required('Veuillez renseigner un nom de segment valide (sans espace).') .transform((value, originalValue) => (/\s/.test(originalValue) ? undefined : value)) .default(undefined), - segmentName: string().default(undefined), + segmentName: string().required(), targetSpecies: array(string().required()).required().default([]), vesselTypes: array(string().required()).required().default([]), year: number().required().default(undefined) diff --git a/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/utils.ts b/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/utils.ts index 5d1dd66413..d9279e502c 100644 --- a/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/utils.ts +++ b/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/utils.ts @@ -1,7 +1,7 @@ import { uniq } from 'lodash' -import type { RiskFactor } from '../../../../domain/entities/vessel/riskFactor/types' import type { Gear } from '../../../../domain/types/Gear' +import type { RiskFactor } from '../../../RiskFactor/types' import type { FleetSegment } from '@features/FleetSegment/types' export function getTargetSpeciesIncludedInSegments( diff --git a/frontend/src/features/FleetSegment/types.ts b/frontend/src/features/FleetSegment/types.ts index 27aed13302..f75110f1b2 100644 --- a/frontend/src/features/FleetSegment/types.ts +++ b/frontend/src/features/FleetSegment/types.ts @@ -1,5 +1,7 @@ import { z } from 'zod' +import { numberOrUndefined } from '../../types' + export enum ScipSpeciesType { DEMERSAL = 'DEMERSAL', OTHER = 'OTHER', @@ -10,14 +12,14 @@ export enum ScipSpeciesType { export const FleetSegmentSchema = z.strictObject({ faoAreas: z.array(z.string()), gears: z.array(z.string()), - impactRiskFactor: z.number().optional(), + impactRiskFactor: z.number(), mainScipSpeciesType: z.nativeEnum(ScipSpeciesType).optional(), - maxMesh: z.number().optional(), - minMesh: z.number().optional(), - minShareOfTargetSpecies: z.number().optional(), + maxMesh: numberOrUndefined, + minMesh: numberOrUndefined, + minShareOfTargetSpecies: numberOrUndefined, priority: z.number(), segment: z.string(), - segmentName: z.string().optional(), + segmentName: z.string(), targetSpecies: z.array(z.string()), vesselTypes: z.array(z.string()), year: z.number() diff --git a/frontend/src/features/Logbook/Logbook.types.ts b/frontend/src/features/Logbook/Logbook.types.ts index 262c2740f7..b9bf592a0f 100644 --- a/frontend/src/features/Logbook/Logbook.types.ts +++ b/frontend/src/features/Logbook/Logbook.types.ts @@ -1,6 +1,6 @@ import type { AllSeafrontGroup, NoSeafrontGroup, SeafrontGroup } from '@constants/seafront' import type { PriorNotification } from '@features/PriorNotification/PriorNotification.types' -import type { VesselIdentity } from 'domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export namespace Logbook { export type Message = @@ -358,7 +358,7 @@ export namespace Logbook { logbookMessagesAndAlerts: FishingActivities startDate: string | undefined tripNumber: string - vesselIdentity: VesselIdentity + vesselIdentity: Vessel.VesselIdentity } export type FishingActivities = { diff --git a/frontend/src/features/Logbook/api.ts b/frontend/src/features/Logbook/api.ts index bc89962908..23b4c1ff6a 100644 --- a/frontend/src/features/Logbook/api.ts +++ b/frontend/src/features/Logbook/api.ts @@ -6,14 +6,14 @@ import { NavigateTo } from './constants' import { Logbook } from './Logbook.types' import { monitorfishApi, monitorfishLightApi } from '../../api/api' -import type { VesselIdentity } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' const LAST_LOGBOOK_TRIPS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les dernières marées" const LOGBOOK_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les messages JPE de ce navire" export type GetVesselLogbookParams = { tripNumber: string | number | undefined - vesselIdentity: VesselIdentity + vesselIdentity: Vessel.VesselIdentity voyageRequest: NavigateTo | undefined } diff --git a/frontend/src/features/Logbook/components/VesselLogbook/LogbookSummary/index.tsx b/frontend/src/features/Logbook/components/VesselLogbook/LogbookSummary/index.tsx index 800be892bb..c239d85f8a 100644 --- a/frontend/src/features/Logbook/components/VesselLogbook/LogbookSummary/index.tsx +++ b/frontend/src/features/Logbook/components/VesselLogbook/LogbookSummary/index.tsx @@ -31,13 +31,16 @@ type LogbookSummaryProps = Readonly<{ }> export function LogbookSummary({ showLogbookMessages }: LogbookSummaryProps) { const dispatch = useMainAppDispatch() + const selectedVesselIdentity = useMainAppSelector(state => state.vessel.selectedVesselIdentity) const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) const fishingActivities = useMainAppSelector(state => state.fishingActivities.fishingActivities) const isFirstVoyage = useMainAppSelector(state => state.fishingActivities.isFirstVoyage) const isLastVoyage = useMainAppSelector(state => state.fishingActivities.isLastVoyage) const tripNumber = useMainAppSelector(state => state.fishingActivities.tripNumber) - const { data: lastLogbookTrips } = useGetLastLogbookTripsQuery(selectedVessel?.internalReferenceNumber ?? skipToken) + const { data: lastLogbookTrips } = useGetLastLogbookTripsQuery( + selectedVesselIdentity?.internalReferenceNumber ?? skipToken + ) const getVesselLogbook = useGetLogbookUseCase() @@ -55,11 +58,11 @@ export function LogbookSummary({ showLogbookMessages }: LogbookSummaryProps) { )?.value }, [fishingActivities?.alerts]) - const goToPreviousTrip = () => dispatch(getVesselLogbook(selectedVessel, NavigateTo.PREVIOUS, true)) - const goToNextTrip = () => dispatch(getVesselLogbook(selectedVessel, NavigateTo.NEXT, true)) - const goToLastTrip = () => dispatch(getVesselLogbook(selectedVessel, NavigateTo.LAST, true)) + const goToPreviousTrip = () => dispatch(getVesselLogbook(selectedVesselIdentity, NavigateTo.PREVIOUS, true)) + const goToNextTrip = () => dispatch(getVesselLogbook(selectedVesselIdentity, NavigateTo.NEXT, true)) + const goToLastTrip = () => dispatch(getVesselLogbook(selectedVesselIdentity, NavigateTo.LAST, true)) const getLogbookTrip = (nextTripNumber: string | undefined) => - dispatch(getVesselLogbook(selectedVessel, NavigateTo.EQUALS, true, nextTripNumber)) + dispatch(getVesselLogbook(selectedVesselIdentity, NavigateTo.EQUALS, true, nextTripNumber)) return ( <> diff --git a/frontend/src/features/Logbook/slice.ts b/frontend/src/features/Logbook/slice.ts index fec25ca9a1..ab4cf31c54 100644 --- a/frontend/src/features/Logbook/slice.ts +++ b/frontend/src/features/Logbook/slice.ts @@ -4,7 +4,8 @@ import { getActivityDateTimeFromMessage, getLogbookMessageType } from './utils' import { FishingActivitiesTab } from '../../domain/entities/vessel/vessel' import type { Logbook } from './Logbook.types' -import type { FishingActivityShowedOnMap, VesselIdentity } from '../../domain/entities/vessel/types' +import type { FishingActivityShowedOnMap } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { PayloadAction } from '@reduxjs/toolkit' // TODO Properly type this redux state. @@ -20,7 +21,7 @@ export type LogbookState = { nextFishingActivities: Logbook.FishingActivities | null redrawFishingActivitiesOnMap: boolean tripNumber: string | null - vesselIdentity: VesselIdentity | undefined + vesselIdentity: Vessel.VesselIdentity | undefined } const INITIAL_STATE: LogbookState = { areFishingActivitiesShowedOnMap: false, @@ -64,7 +65,7 @@ const logbookSlice = createSlice({ /** * Init vessel fishing activities */ - init(state, action: PayloadAction) { + init(state, action: PayloadAction) { state.areFishingActivitiesShowedOnMap = false state.fishingActivitiesShowedOnMap = [] state.fishingActivities = undefined diff --git a/frontend/src/features/Logbook/useCases/getVesselLogbook.ts b/frontend/src/features/Logbook/useCases/getVesselLogbook.ts index 65d6a75e4a..d13a46636a 100644 --- a/frontend/src/features/Logbook/useCases/getVesselLogbook.ts +++ b/frontend/src/features/Logbook/useCases/getVesselLogbook.ts @@ -12,7 +12,7 @@ import { logbookApi, logbookLightApi } from '../api' import { NavigateTo } from '../constants' import { logbookActions } from '../slice' -import type { VesselIdentity } from '../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { MainAppThunk } from '@store' /** @@ -21,7 +21,7 @@ import type { MainAppThunk } from '@store' export const getVesselLogbook = (isInLightMode: boolean) => ( - vesselIdentity: VesselIdentity | undefined, + vesselIdentity: Vessel.VesselIdentity | undefined, navigateTo: NavigateTo | undefined, isFromUserAction: boolean, nextTripNumber?: string diff --git a/frontend/src/features/Logbook/utils.ts b/frontend/src/features/Logbook/utils.ts index 4602a82984..44dba22f60 100644 --- a/frontend/src/features/Logbook/utils.ts +++ b/frontend/src/features/Logbook/utils.ts @@ -9,7 +9,8 @@ import { undefinedize } from '../../utils/undefinedize' import type { CatchProperty, CatchWithProperties, ProtectedCatchWithProperties } from './components/VesselLogbook/types' import type { SpeciesInsight, SpeciesToSpeciesInsight, SpeciesToSpeciesInsightList } from './types' -import type { DeclaredLogbookSpecies, FishingActivityShowedOnMap } from '../../domain/entities/vessel/types' +import type { FishingActivityShowedOnMap } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' function getCatchPropertiesObject(logbookCatch: Logbook.Catch): CatchProperty { return { @@ -463,15 +464,15 @@ export const getLogbookMessageType = (message: Logbook.Message): string => { const NOT_FOUND = -1 -export function getSummedSpeciesOnBoard(speciesOnBoard: DeclaredLogbookSpecies[]) { - return speciesOnBoard.reduce((accumulator: DeclaredLogbookSpecies[], specy: DeclaredLogbookSpecies) => { +export function getSummedSpeciesOnBoard(speciesOnBoard: Vessel.DeclaredLogbookSpecies[]) { + return speciesOnBoard.reduce((accumulator: Vessel.DeclaredLogbookSpecies[], specy: Vessel.DeclaredLogbookSpecies) => { const previousSpecyIndex = accumulator.findIndex(existingSpecy => existingSpecy.species === specy.species) if (previousSpecyIndex !== NOT_FOUND && accumulator[previousSpecyIndex]) { const nextSpecy = { ...accumulator[previousSpecyIndex] } // @ts-ignore nextSpecy.weight = (nextSpecy.weight ?? 0) + specy.weight - accumulator[previousSpecyIndex] = nextSpecy as DeclaredLogbookSpecies + accumulator[previousSpecyIndex] = nextSpecy as Vessel.DeclaredLogbookSpecies return accumulator } diff --git a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/FavoriteVessel.tsx b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/FavoriteVessel.tsx index 9aa688c2c4..4a9a0695cc 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/FavoriteVessel.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/FavoriteVessel.tsx @@ -9,14 +9,14 @@ import { showVessel } from '../../../../../domain/use_cases/vessel/showVessel' import { showVesselTrack } from '../../../../../domain/use_cases/vessel/showVesselTrack' import { unselectVessel } from '../../../../../domain/use_cases/vessel/unselectVessel' -import type { VesselCompositeIdentifier, VesselIdentity } from '../../../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' type FavoriteVesselProps = Readonly<{ - favorite: VesselIdentity + favorite: Vessel.VesselIdentity isLastItem: boolean isTrackShowed: boolean isVesselShowed: boolean - vesselCompositeIdentifier: VesselCompositeIdentifier + vesselCompositeIdentifier: Vessel.VesselCompositeIdentifier }> export function FavoriteVessel({ favorite, diff --git a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx index dcece27179..2bf966c3b8 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx @@ -1,5 +1,6 @@ import { COLORS } from '@constants/constants' import { MapBox } from '@features/Map/constants' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { setLeftMapBoxOpened } from 'domain/shared_slices/Global' @@ -7,7 +8,6 @@ import { useRef } from 'react' import styled from 'styled-components' import { FavoriteVessel } from './FavoriteVessel' -import { getVesselCompositeIdentifier } from '../../../../../domain/entities/vessel/vessel' import { MapPropertyTrigger } from '../../../../commonComponents/MapPropertyTrigger' import { MapComponent } from '../../../../commonStyles/MapComponent' import HidingOtherTracksSVG from '../../../../icons/Bouton_masquer_pistes_actif.svg?react' diff --git a/frontend/src/features/Map/components/MapMenu.tsx b/frontend/src/features/Map/components/MapMenu.tsx index 7fddbd17df..2a0ae97036 100644 --- a/frontend/src/features/Map/components/MapMenu.tsx +++ b/frontend/src/features/Map/components/MapMenu.tsx @@ -9,12 +9,12 @@ import MapMenuOverlay from './MapMenuOverlay' import { vesselSelectors } from '../../Vessel/slice' import { LayerProperties } from '../constants' -import type { VesselEnhancedLastPositionWebGLObject } from '../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export function MapMenu() { const vessels = useMainAppSelector(state => vesselSelectors.selectAll(state.vessel.vessels)) const [coordinates, setCoordinates] = useState([]) - const vessel = useRef() + const vessel = useRef() useEffect(() => { function showMenu(event) { diff --git a/frontend/src/features/Map/useCases/clickOnMapFeature.ts b/frontend/src/features/Map/useCases/clickOnMapFeature.ts index 656d9c1924..37d9b18e4b 100644 --- a/frontend/src/features/Map/useCases/clickOnMapFeature.ts +++ b/frontend/src/features/Map/useCases/clickOnMapFeature.ts @@ -10,9 +10,9 @@ import GeoJSON from 'ol/format/GeoJSON' import { LayerProperties, OPENLAYERS_PROJECTION } from '../constants' import { MonitorFishMap } from '../Map.types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { MainAppDispatch } from '@store' import type { HybridAppDispatch, HybridAppThunk } from '@store/types' -import type { VesselIdentity, VesselLastPositionFeature } from 'domain/entities/vessel/types' import type { Feature } from 'ol' import type { Geometry } from 'ol/geom' @@ -76,7 +76,9 @@ export const clickOnMapFeature = } if (clickedFeatureId.includes(MonitorFishMap.MonitorFishLayer.VESSELS)) { - const clickedVessel = (mapClick.feature as VesselLastPositionFeature).getProperties() as VesselIdentity + const clickedVessel = ( + mapClick.feature as Vessel.VesselLastPositionFeature + ).getProperties() as Vessel.VesselIdentity if (mapClick.ctrlKeyPressed) { // Vessel dispatches can only be called from the main app (FronteOffice) diff --git a/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx b/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx index fb29ec5c4e..8be8f812c4 100644 --- a/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx +++ b/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx @@ -11,7 +11,7 @@ import styled from 'styled-components' import { useGetMissionActionFormikUsecases } from '../../hooks/useGetMissionActionFormikUsecases' import type { MissionActionFormValues } from '../../types' -import type { VesselIdentity } from 'domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export function VesselField() { const { errors, setValues, values } = useFormikContext() @@ -49,7 +49,7 @@ export function VesselField() { values.ircs ]) - const handleVesselSearchChange = (nextVessel: VesselIdentity | undefined) => { + const handleVesselSearchChange = (nextVessel: Partial | undefined) => { if (!nextVessel) { setValues({ ...values, @@ -72,11 +72,11 @@ export function VesselField() { setValues({ ...values, - districtCode: nextVessel.districtCode ?? undefined, - externalReferenceNumber: nextVessel.externalReferenceNumber ?? undefined, + districtCode: nextVessel.districtCode, + externalReferenceNumber: nextVessel.externalReferenceNumber, flagState: nextVessel.flagState, - internalReferenceNumber: nextVessel.internalReferenceNumber ?? undefined, - ircs: nextVessel.ircs ?? undefined, + internalReferenceNumber: nextVessel.internalReferenceNumber, + ircs: nextVessel.ircs, vesselId: nextVessel.vesselId, vesselName: nextVessel.vesselName }) diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts index 1d626979f4..057e2533d7 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts @@ -1,5 +1,5 @@ import { MissionAction } from '@features/Mission/missionAction.types' -import { vesselApi } from '@features/Vessel/vesselApi' +import { riskFactorApi } from '@features/RiskFactor/apis' import { FrontendError } from '@libs/FrontendError' import type { Gear } from '../../../../../domain/types/Gear' @@ -20,7 +20,7 @@ export const updateActionGearsOnboard = } const { data: riskFactor } = await dispatch( - vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) + riskFactorApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) ) if (!riskFactor) { return [] diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts index b1214f6588..188a5dfb8c 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts @@ -1,9 +1,9 @@ import { getSummedSpeciesOnBoard } from '@features/Logbook/utils' import { MissionAction } from '@features/Mission/missionAction.types' -import { vesselApi } from '@features/Vessel/vesselApi' +import { riskFactorApi } from '@features/RiskFactor/apis' -import type { RiskFactor } from '../../../../../domain/entities/vessel/riskFactor/types' import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' +import type { RiskFactor } from '@features/RiskFactor/types' export const updateActionSpeciesOnboard = (dispatch, setFieldValue: (field: string, value: any) => void) => @@ -13,7 +13,7 @@ export const updateActionSpeciesOnboard = } const { data: riskFactor } = await dispatch( - vesselApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) + riskFactorApi.endpoints.getRiskFactor.initiate(missionAction.internalReferenceNumber) ) if (!riskFactor) { return [] diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/columns.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/columns.tsx index 064fb811e6..6de5818604 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/columns.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/columns.tsx @@ -8,7 +8,7 @@ import { ActionButtonsCell } from './cells/ActionButtonsCell' import { StateCell } from './cells/StateCell' import { TypesCell } from './cells/TypesCell' import { None, StyledCountryFlag } from './styles' -import { VesselRiskFactor } from '../../../Vessel/components/VesselRiskFactor' +import { VesselRiskFactor } from '../../../RiskFactor/components/VesselRiskFactor' import { PriorNotification } from '../../PriorNotification.types' import type { CellContext, ColumnDef } from '@tanstack/react-table' diff --git a/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.tsx b/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.tsx index 472b52d376..370cced4e9 100644 --- a/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.tsx +++ b/frontend/src/features/PriorNotification/components/PriorNotificationList/utils.tsx @@ -12,8 +12,8 @@ import { BLUEFIN_TUNA_NAME_FR, BLUEFIN_TUNA_SPECY_CODE } from '@features/PriorNotification/constants' +import { Vessel } from '@features/Vessel/Vessel.types' import { THEME, customDayjs, getMaybeBooleanFromRichBoolean, type DateAsStringRange } from '@mtes-mct/monitor-ui' -import { VesselIdentifier } from 'domain/entities/vessel/types' import { update } from 'lodash' import styled from 'styled-components' @@ -30,7 +30,6 @@ import { import { PriorNotification } from '../../PriorNotification.types' import type { FilterStatus, ListFilter } from './types' -import type { Vessel } from '@features/Vessel/Vessel.types' import type { Column } from '@tanstack/react-table' import type { CSSProperties } from 'react' @@ -304,7 +303,7 @@ export function getVesselIdentityFromPriorNotification( vesselId: priorNotification.vesselId, // In practice, prior notifications always have a vessel CFR (`vesselInternalReferenceNumber`) // despite the `| undefined` - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, vesselLength: priorNotification.vesselLength, vesselName: priorNotification.vesselName } diff --git a/frontend/src/features/PriorNotification/components/shared/CardBodyHead/TagBar.tsx b/frontend/src/features/PriorNotification/components/shared/CardBodyHead/TagBar.tsx index 33f958dcca..e4fcc1295d 100644 --- a/frontend/src/features/PriorNotification/components/shared/CardBodyHead/TagBar.tsx +++ b/frontend/src/features/PriorNotification/components/shared/CardBodyHead/TagBar.tsx @@ -1,4 +1,4 @@ -import { VesselRiskFactor } from '@features/Vessel/components/VesselRiskFactor' +import { VesselRiskFactor } from '@features/RiskFactor/components/VesselRiskFactor' import { THEME } from '@mtes-mct/monitor-ui' import styled from 'styled-components' diff --git a/frontend/src/features/Regulation/components/RegulationTables/tableCells.tsx b/frontend/src/features/Regulation/components/RegulationTables/tableCells.tsx index b8a9231dc2..d1d827d207 100644 --- a/frontend/src/features/Regulation/components/RegulationTables/tableCells.tsx +++ b/frontend/src/features/Regulation/components/RegulationTables/tableCells.tsx @@ -6,7 +6,7 @@ import { useCallback, type ChangeEvent } from 'react' import { SelectPicker, Table } from 'rsuite' import styled from 'styled-components' -import { getRiskFactorColor } from 'domain/entities/vessel/riskFactor' +import { getRiskFactorColor } from '@features/RiskFactor/utils' import { theme } from 'ui/theme' import { RiskFactorBox } from '@features/Vessel/components/VesselSidebar/risk_factor/RiskFactorBox' diff --git a/frontend/src/features/Reporting/components/CurrentReportingList/__tests__/utils.test.ts b/frontend/src/features/Reporting/components/CurrentReportingList/__tests__/utils.test.ts index cc128de605..3f8ac29dca 100644 --- a/frontend/src/features/Reporting/components/CurrentReportingList/__tests__/utils.test.ts +++ b/frontend/src/features/Reporting/components/CurrentReportingList/__tests__/utils.test.ts @@ -1,8 +1,8 @@ import { Seafront } from '@constants/seafront' import { ReportingType } from '@features/Reporting/types' +import { Vessel } from '@features/Vessel/Vessel.types' import { expect } from '@jest/globals' -import { VesselIdentifier } from '../../../../../domain/entities/vessel/types' import { PendingAlertValueType } from '../../../../Alert/types' import { sortByValidationOrCreationDateDesc } from '../utils' @@ -38,7 +38,7 @@ describe('Reportings/Current/utils.sortByValidationOrCreationDateDesc()', () => type: PendingAlertValueType.TWELVE_MILES_FISHING_ALERT }, vesselId: 1234568, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, vesselName: 'A VESSEL' } const secondReporting: PendingAlertReporting = { diff --git a/frontend/src/features/Reporting/components/ReportingCard/__tests__/ReportingCard.test.tsx b/frontend/src/features/Reporting/components/ReportingCard/__tests__/ReportingCard.test.tsx index 83b36d8847..50d7aa5cd5 100644 --- a/frontend/src/features/Reporting/components/ReportingCard/__tests__/ReportingCard.test.tsx +++ b/frontend/src/features/Reporting/components/ReportingCard/__tests__/ReportingCard.test.tsx @@ -2,6 +2,7 @@ import { Seafront } from '@constants/seafront' import { PendingAlertValueType } from '@features/Alert/types' import { ReportingCard } from '@features/Reporting/components/ReportingCard' import { ReportingType } from '@features/Reporting/types' +import { Vessel } from '@features/Vessel/Vessel.types' import { afterAll, describe, expect, it, jest } from '@jest/globals' import { THEME, ThemeProvider } from '@mtes-mct/monitor-ui' import { render, screen } from '@testing-library/react' @@ -10,8 +11,6 @@ import { noop } from 'lodash' import { Provider } from 'react-redux' import configureStore from 'redux-mock-store' -import { VesselIdentifier } from '../../../../../domain/entities/vessel/types' - import type { PendingAlertReporting } from '@features/Reporting/types' jest.mock('../../../useCases/archiveReporting', () => ({ archiveReporting: jest.fn() })) @@ -56,7 +55,7 @@ describe('ReportingCard()', () => { type: PendingAlertValueType.TWELVE_MILES_FISHING_ALERT }, vesselId: 1234568, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, vesselName: 'A VESSEL' } diff --git a/frontend/src/features/Reporting/components/ReportingForm/index.tsx b/frontend/src/features/Reporting/components/ReportingForm/index.tsx index 62c36c9e0a..16bcdd95bc 100644 --- a/frontend/src/features/Reporting/components/ReportingForm/index.tsx +++ b/frontend/src/features/Reporting/components/ReportingForm/index.tsx @@ -1,17 +1,17 @@ import { WindowContext } from '@api/constants' import { CreateOrEditReportingSchema } from '@features/Reporting/components/ReportingForm/schemas' import { getFormFields, getReportingValue } from '@features/Reporting/components/ReportingForm/utils' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Formik } from 'formik' import { useCallback, useEffect } from 'react' import { Form } from './Form' -import { getOnlyVesselIdentityProperties } from '../../../../domain/entities/vessel/vessel' import { addReporting } from '../../useCases/addReporting' import { updateReporting } from '../../useCases/updateReporting' -import type { VesselIdentity } from '../../../../domain/entities/vessel/types' import type { EditedReporting, Reporting } from '../../types' +import type { Vessel } from '@features/Vessel/Vessel.types' type ReportingFormProps = { className?: string | undefined @@ -19,7 +19,7 @@ type ReportingFormProps = { hasWhiteBackground?: boolean onClose: () => void onIsDirty?: ((isDirty: boolean) => void) | undefined - vesselIdentity: VesselIdentity + vesselIdentity: Vessel.VesselIdentity windowContext: WindowContext } export function ReportingForm({ @@ -46,7 +46,7 @@ export function ReportingForm({ if (editedReporting?.id) { await dispatch( updateReporting( - getOnlyVesselIdentityProperties(editedReporting), + extractVesselIdentityProps(editedReporting), editedReporting.id, nextReporting, editedReporting.type, diff --git a/frontend/src/features/Reporting/components/ReportingTable/EditReporting.tsx b/frontend/src/features/Reporting/components/ReportingTable/EditReporting.tsx index 25e6bf2b91..f35fe91281 100644 --- a/frontend/src/features/Reporting/components/ReportingTable/EditReporting.tsx +++ b/frontend/src/features/Reporting/components/ReportingTable/EditReporting.tsx @@ -1,6 +1,7 @@ import { WindowContext } from '@api/constants' import { ErrorWall } from '@components/ErrorWall' import { reportingActions } from '@features/Reporting/slice' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' @@ -8,7 +9,6 @@ import { THEME } from '@mtes-mct/monitor-ui' import { useCallback } from 'react' import styled from 'styled-components' -import { getOnlyVesselIdentityProperties } from '../../../../domain/entities/vessel/vessel' import CloseIconSVG from '../../../icons/Croix_grise.svg?react' import AlertsSVG from '../../../icons/Icone_alertes_gris.svg?react' import { ReportingForm } from '../ReportingForm' @@ -21,7 +21,7 @@ export function EditReporting() { ) const baseUrl = window.location.origin - const vesselIdentity = editedReporting ? getOnlyVesselIdentityProperties(editedReporting) : undefined + const vesselIdentity = editedReporting ? extractVesselIdentityProps(editedReporting) : undefined const closeForm = useCallback(() => { dispatch(reportingActions.unsetEditedReporting()) diff --git a/frontend/src/features/Reporting/components/ReportingTable/cells/ActionButtonsCell.tsx b/frontend/src/features/Reporting/components/ReportingTable/cells/ActionButtonsCell.tsx index a985b5cca4..1af20906b4 100644 --- a/frontend/src/features/Reporting/components/ReportingTable/cells/ActionButtonsCell.tsx +++ b/frontend/src/features/Reporting/components/ReportingTable/cells/ActionButtonsCell.tsx @@ -1,5 +1,6 @@ import { reportingActions } from '@features/Reporting/slice' import { ReportingType } from '@features/Reporting/types' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' import { type CSSProperties, useCallback } from 'react' @@ -30,7 +31,8 @@ export function ActionButtonsCell({ reporting }: ActionButtonsCellProps) { const focusOnMap = useCallback( (focusedReporting: Reporting.Reporting) => { - dispatch(showVessel(focusedReporting, false, true)) + const identity = extractVesselIdentityProps(focusedReporting) + dispatch(showVessel(identity, false, true)) }, [dispatch] ) diff --git a/frontend/src/features/Reporting/types.ts b/frontend/src/features/Reporting/types.ts index a85bf2cffe..8a8eeec0a5 100644 --- a/frontend/src/features/Reporting/types.ts +++ b/frontend/src/features/Reporting/types.ts @@ -1,8 +1,7 @@ // TODO Wrap into a `Reporting` namespace. import { Seafront } from '@constants/seafront' - -import { VesselIdentifier } from '../../domain/entities/vessel/types' +import { Vessel } from '@features/Vessel/Vessel.types' import type { Infraction } from '../../domain/types/infraction' import type { PendingAlertValue } from '../Alert/types' @@ -36,7 +35,7 @@ export type BaseReporting = { underCharter: boolean | undefined validationDate: string | undefined vesselId: number | undefined - vesselIdentifier: VesselIdentifier | undefined + vesselIdentifier: Vessel.VesselIdentifier | undefined vesselName: string | undefined // TODO These 2 props shouldn't be there at all and should be treated in a separated redux state. diff --git a/frontend/src/features/Reporting/useCases/__tests__/__mocks__/dummyReporting.ts b/frontend/src/features/Reporting/useCases/__tests__/__mocks__/dummyReporting.ts index 67256cd4e2..2fa1dd4922 100644 --- a/frontend/src/features/Reporting/useCases/__tests__/__mocks__/dummyReporting.ts +++ b/frontend/src/features/Reporting/useCases/__tests__/__mocks__/dummyReporting.ts @@ -1,8 +1,7 @@ import { Seafront } from '@constants/seafront' import { PendingAlertValueType } from '@features/Alert/types' import { ReportingType } from '@features/Reporting/types' - -import { VesselIdentifier } from '../../../../../domain/entities/vessel/types' +import { Vessel } from '@features/Vessel/Vessel.types' import type { PendingAlertReporting } from '@features/Reporting/types' @@ -33,6 +32,6 @@ export const fortyHeightHourAlertReporting: PendingAlertReporting = { type: PendingAlertValueType.MISSING_FAR_48_HOURS_ALERT }, vesselId: 1234568, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, vesselName: 'A VESSEL' } diff --git a/frontend/src/features/Reporting/useCases/__tests__/archiveReporting.test.ts b/frontend/src/features/Reporting/useCases/__tests__/archiveReporting.test.ts index 087a37848c..e1ab9dc042 100644 --- a/frontend/src/features/Reporting/useCases/__tests__/archiveReporting.test.ts +++ b/frontend/src/features/Reporting/useCases/__tests__/archiveReporting.test.ts @@ -1,10 +1,10 @@ import { PendingAlertValueType } from '@features/Alert/types' import { fortyHeightHourAlertReporting } from '@features/Reporting/useCases/__tests__/__mocks__/dummyReporting' import { archiveReporting } from '@features/Reporting/useCases/archiveReporting' +import { Vessel } from '@features/Vessel/Vessel.types' import { describe, it, expect, afterAll } from '@jest/globals' import { mockedDispatch } from '@store/__tests__/utils' -import { VesselIdentifier } from '../../../../domain/entities/vessel/types' import { deleteReporting } from '../deleteReporting' /** @@ -31,7 +31,7 @@ describe('archiveReporting()', () => { flagState: '', internalReferenceNumber: '', vesselId: 1234568, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, vesselName: '' } } diff --git a/frontend/src/features/Reporting/useCases/addReporting.ts b/frontend/src/features/Reporting/useCases/addReporting.ts index d928ebab8d..7d6a3184a7 100644 --- a/frontend/src/features/Reporting/useCases/addReporting.ts +++ b/frontend/src/features/Reporting/useCases/addReporting.ts @@ -1,7 +1,7 @@ import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { addVesselReporting } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' @@ -20,7 +20,7 @@ export const addReporting = dispatch( addVesselReporting({ reportingType: newReporting?.type, - vesselFeatureId: Vessel.getVesselFeatureId(selectedVesselIdentity) + vesselFeatureId: VesselFeature.getVesselFeatureId(selectedVesselIdentity) }) ) diff --git a/frontend/src/features/Reporting/useCases/archiveReporting.ts b/frontend/src/features/Reporting/useCases/archiveReporting.ts index 3bdf246029..355084f1ee 100644 --- a/frontend/src/features/Reporting/useCases/archiveReporting.ts +++ b/frontend/src/features/Reporting/useCases/archiveReporting.ts @@ -4,7 +4,7 @@ import { deleteReporting } from '@features/Reporting/useCases/deleteReporting' import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { removeVesselReporting } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' @@ -32,7 +32,7 @@ export const archiveReporting = dispatch( removeVesselReporting({ reportingType: reporting.type, - vesselFeatureId: Vessel.getVesselFeatureId(selectedVesselIdentity) + vesselFeatureId: VesselFeature.getVesselFeatureId(selectedVesselIdentity) }) ) diff --git a/frontend/src/features/Reporting/useCases/archiveReportings.ts b/frontend/src/features/Reporting/useCases/archiveReportings.ts index 429a78e19d..1d1b8f632f 100644 --- a/frontend/src/features/Reporting/useCases/archiveReportings.ts +++ b/frontend/src/features/Reporting/useCases/archiveReportings.ts @@ -3,7 +3,7 @@ import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeat import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { isNotNullish } from '@utils/isNotNullish' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { removeVesselReportings } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' @@ -47,7 +47,7 @@ function getReportingsInformationFromIds(ids: number[], currentReportings: Repor return { id: foundReporting.id, type: foundReporting.type, - vesselFeatureId: Vessel.getVesselFeatureId(foundReporting) + vesselFeatureId: VesselFeature.getVesselFeatureId(foundReporting) } }) .filter(isNotNullish) diff --git a/frontend/src/features/Reporting/useCases/deleteReporting.ts b/frontend/src/features/Reporting/useCases/deleteReporting.ts index fa5297a31a..c36b6c610a 100644 --- a/frontend/src/features/Reporting/useCases/deleteReporting.ts +++ b/frontend/src/features/Reporting/useCases/deleteReporting.ts @@ -3,7 +3,7 @@ import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeat import { vesselApi } from '@features/Vessel/vesselApi' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { removeVesselReporting } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' @@ -24,7 +24,7 @@ export const deleteReporting = dispatch( removeVesselReporting({ reportingType, - vesselFeatureId: Vessel.getVesselFeatureId(selectedVesselIdentity) + vesselFeatureId: VesselFeature.getVesselFeatureId(selectedVesselIdentity) }) ) diff --git a/frontend/src/features/Reporting/useCases/deleteReportings.ts b/frontend/src/features/Reporting/useCases/deleteReportings.ts index af6b73949b..f2bf76d146 100644 --- a/frontend/src/features/Reporting/useCases/deleteReportings.ts +++ b/frontend/src/features/Reporting/useCases/deleteReportings.ts @@ -3,7 +3,7 @@ import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeat import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { isNotNullish } from '@utils/isNotNullish' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { removeVesselReportings } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' @@ -47,7 +47,7 @@ function getReportingsInformationFromIds(ids: number[], currentReportings: Repor return { id: foundReporting.id, type: foundReporting.type, - vesselFeatureId: Vessel.getVesselFeatureId(foundReporting) + vesselFeatureId: VesselFeature.getVesselFeatureId(foundReporting) } }) .filter(isNotNullish) diff --git a/frontend/src/features/Reporting/useCases/updateReporting.ts b/frontend/src/features/Reporting/useCases/updateReporting.ts index 9b0118ef22..0d1c77aea2 100644 --- a/frontend/src/features/Reporting/useCases/updateReporting.ts +++ b/frontend/src/features/Reporting/useCases/updateReporting.ts @@ -1,20 +1,20 @@ import { WindowContext } from '@api/constants' import { ReportingType } from '@features/Reporting/types' import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' +import { Vessel } from '@features/Vessel/Vessel.types' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { addVesselReporting, removeVesselReporting } from '../../Vessel/slice' import { reportingApi } from '../reportingApi' -import type { VesselIdentity } from '../../../domain/entities/vessel/types' import type { EditedReporting } from '@features/Reporting/types' import type { MainAppThunk } from '@store' export const updateReporting = ( - vesselIdentity: VesselIdentity, + vesselIdentity: Vessel.VesselIdentity, id: number, nextReportingFormData: EditedReporting, previousReportingType: ReportingType, @@ -31,7 +31,7 @@ export const updateReporting = // We update the reportings of the last positions vessels state if (previousReportingType !== nextReportingFormData.type) { - const vesselFeatureId = Vessel.getVesselFeatureId(vesselIdentity) + const vesselFeatureId = VesselFeature.getVesselFeatureId(vesselIdentity) dispatch( removeVesselReporting({ diff --git a/frontend/src/features/RiskFactor/apis.ts b/frontend/src/features/RiskFactor/apis.ts new file mode 100644 index 0000000000..f2ce146e06 --- /dev/null +++ b/frontend/src/features/RiskFactor/apis.ts @@ -0,0 +1,35 @@ +import { monitorfishApi } from '@api/api' +import { BackendApi } from '@api/BackendApi.types' +import { RiskFactorSchema } from '@features/RiskFactor/types' + +import type { RiskFactor } from '@features/RiskFactor/types' +import type { SafeParseReturnType } from 'zod' + +function valueOrundefinedIfNotFoundOrThrow( + result: SafeParseReturnType, + response: BackendApi.ResponseBodyError | Type +): Type | undefined { + if (!result.success) { + if ((response as BackendApi.ResponseBodyError).code === BackendApi.ErrorCode.NOT_FOUND_BUT_OK) { + return undefined + } + + throw result.error + } + + return response as Type +} + +export const riskFactorApi = monitorfishApi.injectEndpoints({ + endpoints: builder => ({ + getRiskFactor: builder.query({ + providesTags: () => [{ type: 'RiskFactor' }], + query: internalReferenceNumber => `/vessels/risk_factor?internalReferenceNumber=${internalReferenceNumber}`, + transformResponse: (response: BackendApi.ResponseBodyError | RiskFactor) => { + const result = RiskFactorSchema.safeParse(response) + + return valueOrundefinedIfNotFoundOrThrow(result, response) + } + }) + }) +}) diff --git a/frontend/src/features/Vessel/components/VesselRiskFactor.tsx b/frontend/src/features/RiskFactor/components/VesselRiskFactor.tsx similarity index 98% rename from frontend/src/features/Vessel/components/VesselRiskFactor.tsx rename to frontend/src/features/RiskFactor/components/VesselRiskFactor.tsx index d84a0eabb9..f445549ce8 100644 --- a/frontend/src/features/Vessel/components/VesselRiskFactor.tsx +++ b/frontend/src/features/RiskFactor/components/VesselRiskFactor.tsx @@ -7,7 +7,7 @@ import { getImpactRiskFactorText, getProbabilityRiskFactorText, getRiskFactorColor -} from '../../../domain/entities/vessel/riskFactor' +} from '../utils' type VesselRiskFactorProps = Readonly< Undefine<{ diff --git a/frontend/src/features/RiskFactor/types.ts b/frontend/src/features/RiskFactor/types.ts new file mode 100644 index 0000000000..e7cd13d2f7 --- /dev/null +++ b/frontend/src/features/RiskFactor/types.ts @@ -0,0 +1,28 @@ +import { Vessel } from '@features/Vessel/Vessel.types' +import { z } from 'zod' + +import { numberOrUndefined, stringOrUndefined } from '../../types' + +// TODO Check which of these types are nullable or not +export const RiskFactorSchema = z.strictObject({ + controlPriorityLevel: z.number(), + controlRateRiskFactor: z.number(), + detectabilityRiskFactor: z.number(), + gearOnboard: z.array(Vessel.DeclaredLogbookGearSchema).optional(), + impactRiskFactor: z.number(), + lastControlDatetime: stringOrUndefined, + numberControlsLastFiveYears: numberOrUndefined, + numberControlsLastThreeYears: numberOrUndefined, + numberGearSeizuresLastFiveYears: numberOrUndefined, + numberInfractionsLastFiveYears: numberOrUndefined, + numberSpeciesSeizuresLastFiveYears: numberOrUndefined, + numberVesselSeizuresLastFiveYears: numberOrUndefined, + probabilityRiskFactor: z.number(), + riskFactor: z.number(), + segmentHighestImpact: stringOrUndefined, + segmentHighestPriority: stringOrUndefined, + segments: z.array(z.string()), + speciesOnboard: z.array(Vessel.DeclaredLogbookSpeciesSchema).optional() +}) + +export type RiskFactor = z.infer diff --git a/frontend/src/domain/entities/vessel/riskFactor/index.ts b/frontend/src/features/RiskFactor/utils.ts similarity index 92% rename from frontend/src/domain/entities/vessel/riskFactor/index.ts rename to frontend/src/features/RiskFactor/utils.ts index bb418d50cf..c6485e9ee2 100644 --- a/frontend/src/domain/entities/vessel/riskFactor/index.ts +++ b/frontend/src/features/RiskFactor/utils.ts @@ -1,5 +1,3 @@ -import type { DeclaredLogbookSpecies } from '../types' - // TODO Replace with theme colors. export const getRiskFactorColor = (riskFactor: number) => { if (riskFactor >= 1 && riskFactor < 1.75) { @@ -120,9 +118,3 @@ export const getControlRateRiskFactorText = (controlRate: number) => { return undefined } - -export function getFaoZonesFromSpeciesOnboard(speciesOnboard: Array) { - const faoZones = speciesOnboard.map(species => species.faoZone) - - return [...new Set(faoZones)] -} diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index 8131baaf01..7b6c2526b3 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -1,6 +1,14 @@ +import { PendingAlertValueType } from '@features/Alert/types' +import { ReportingType } from '@features/Reporting/types' +import { z } from 'zod' + +import { booleanOrUndefined, numberOrUndefined, stringOrUndefined } from '../../types' + import type { ProducerOrganizationMembership } from '@features/ProducerOrganizationMembership/types' -import type { RiskFactor } from 'domain/entities/vessel/riskFactor/types' -import type { VesselId, VesselIdentifier, VesselLastPosition } from 'domain/entities/vessel/types' +import type { RiskFactor } from '@features/RiskFactor/types' +import type Feature from 'ol/Feature' +import type LineString from 'ol/geom/LineString' +import type Point from 'ol/geom/Point' export namespace Vessel { export type Beacon = { @@ -64,7 +72,7 @@ export namespace Vessel { speciesArray: string[] } - export type SelectedVessel = VesselEnhancedObject & Vessel.EnrichedVessel + export type SelectedVessel = Omit & Vessel.EnrichedVessel export type AugmentedSelectedVessel = SelectedVessel & { hasAlert: boolean hasInfractionSuspicion: boolean @@ -84,6 +92,187 @@ export namespace Vessel { vesselName: string | undefined } + /** + * The vessel id number used to identify vessels entered in NAVPRO French database + * It is used for : + * - Controls + * - Beacons + * - Vessel information + * + * i.e: 20569 + */ + export type VesselId = number + + /** + * The vessel composite key/identifier used to identify all vessels + * by concatenating : + * - internalReferenceNumber + * - ircs + * - externalReferenceNumber + * + * The result is :`internalReferenceNumber/ircs/externalReferenceNumber` + * + * i.e: "FAK000999999/CALLME/DONTSINK" + */ + export type VesselCompositeIdentifier = string + + /** + * The vessel feature id is the vessel composite key concatenated to the `vessel:` string. + * It is mainly used to distinct OpenLayers objects (called Features) + * + * i.e: "vessel:FAK000999999/CALLME/DONTSINK" + * @see VesselCompositeIdentifier + */ + export type VesselFeatureId = string + + export enum VesselIdentifier { + EXTERNAL_REFERENCE_NUMBER = 'EXTERNAL_REFERENCE_NUMBER', + INTERNAL_REFERENCE_NUMBER = 'INTERNAL_REFERENCE_NUMBER', + IRCS = 'IRCS' + } + + export enum NetworkType { + CELLULAR = 'CELLULAR', + SATELLITE = 'SATELLITE' + } + + export type VesselAndPositions = { + positions: VesselPosition[] + vessel: Vessel.EnrichedVessel + } + + // TODO Check which of these types are nullable or not + export const DeclaredLogbookGearSchema = z.strictObject({ + dimensions: stringOrUndefined, + gear: stringOrUndefined, + mesh: numberOrUndefined + }) + + // TODO Check which of these types are nullable or not + export const DeclaredLogbookSpeciesSchema = z.strictObject({ + faoZone: stringOrUndefined, + gear: stringOrUndefined, + species: stringOrUndefined, + weight: numberOrUndefined + }) + export type DeclaredLogbookSpecies = z.infer + + // TODO Check which of these types are nullable or not + export const VesselLastPositionSchema = z.strictObject({ + alerts: z.array(z.union([z.nativeEnum(PendingAlertValueType), z.literal('PNO_LAN_WEIGHT_TOLERANCE_ALERT')])), + beaconMalfunctionId: numberOrUndefined, + beaconNumber: numberOrUndefined, + course: z.number(), + dateTime: z.string(), + departureDateTime: stringOrUndefined, + destination: stringOrUndefined, + detectabilityRiskFactor: numberOrUndefined, + district: stringOrUndefined, + districtCode: stringOrUndefined, + emissionPeriod: numberOrUndefined, + estimatedCurrentLatitude: numberOrUndefined, + estimatedCurrentLongitude: numberOrUndefined, + externalReferenceNumber: stringOrUndefined, + flagState: z.string(), + from: stringOrUndefined, + gearOnboard: z.array(DeclaredLogbookGearSchema), + impactRiskFactor: numberOrUndefined, + internalReferenceNumber: stringOrUndefined, + ircs: stringOrUndefined, + isAtPort: booleanOrUndefined, + lastControlDateTime: stringOrUndefined, + lastControlInfraction: booleanOrUndefined, + lastLogbookMessageDateTime: stringOrUndefined, + latitude: z.number(), + length: numberOrUndefined, + longitude: z.number(), + mmsi: stringOrUndefined, + positionType: z.string(), + postControlComment: stringOrUndefined, + probabilityRiskFactor: numberOrUndefined, + registryPortLocode: stringOrUndefined, + registryPortName: stringOrUndefined, + reportings: z.array(z.nativeEnum(ReportingType)), + riskFactor: numberOrUndefined, + segments: z.array(z.string()), + speciesOnboard: z.array(DeclaredLogbookSpeciesSchema), + speed: z.number(), + totalWeightOnboard: numberOrUndefined, + tripNumber: stringOrUndefined, + underCharter: booleanOrUndefined, + vesselId: numberOrUndefined, + vesselIdentifier: z.nativeEnum(VesselIdentifier), + vesselName: stringOrUndefined, + width: numberOrUndefined + }) + export type VesselLastPosition = z.infer + + export type VesselPosition = { + course: number + dateTime: string + destination: string | undefined + externalReferenceNumber: string | undefined + flagState: string + from: string + internalReferenceNumber: string | undefined + ircs: string | undefined + isAtPort: boolean | undefined + isFishing: boolean | undefined + isManual: boolean | undefined + latitude: number + longitude: number + mmsi: string | undefined + networkType: NetworkType | undefined + positionType: string + speed: number + tripNumber: number | undefined + vesselName: string + } + + export interface VesselPointFeature extends Feature { + course?: number + dateTime?: string + name?: string + positionType?: string + speed?: number + } + + export interface VesselLineFeature extends Feature { + course?: number + dateTime?: string + firstPositionDate?: Date + isTimeEllipsis?: boolean + positionType?: string + secondPositionDate?: Date + speed?: number + trackType?: TrackTypeRecordItem + } + + export interface VesselArrowFeature extends Feature { + course?: number + name?: string + } + + export type TrackTypeRecordItem = { + arrow: string + code: string + color: string + description: string + } + export type VesselEnhancedLastPositionWebGLObject = Vessel.VesselEnhancedObject & { + coordinates: number[] + course: number + filterPreview: number // 0 is False, 1 is True - for WebGL + hasBeaconMalfunction: boolean + isAtPort: boolean | undefined + isFiltered: number // 0 is False, 1 is True - for WebGL + lastPositionSentAt: number + speed: number + vesselFeatureId: VesselFeatureId + } + + export type VesselLastPositionFeature = Feature & VesselEnhancedLastPositionWebGLObject + // --------------------------------------------------------------------------- // API diff --git a/frontend/src/features/Vessel/components/VesselLabelOverlay/VesselLabel.tsx b/frontend/src/features/Vessel/components/VesselLabelOverlay/VesselLabel.tsx index 9dc8cd41be..9eefddb942 100644 --- a/frontend/src/features/Vessel/components/VesselLabelOverlay/VesselLabel.tsx +++ b/frontend/src/features/Vessel/components/VesselLabelOverlay/VesselLabel.tsx @@ -3,13 +3,13 @@ import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useEffect, useState } from 'react' import styled from 'styled-components' +import { showVessel } from '../../../../domain/use_cases/vessel/showVessel' import { getDetectabilityRiskFactorText, getImpactRiskFactorText, getProbabilityRiskFactorText, getRiskFactorColor -} from '../../../../domain/entities/vessel/riskFactor' -import { showVessel } from '../../../../domain/use_cases/vessel/showVessel' +} from '../../../RiskFactor/utils' export function VesselLabel({ featureId, diff --git a/frontend/src/features/Vessel/components/VesselLabelOverlay/index.tsx b/frontend/src/features/Vessel/components/VesselLabelOverlay/index.tsx index 6cb9898f77..f2801f6172 100644 --- a/frontend/src/features/Vessel/components/VesselLabelOverlay/index.tsx +++ b/frontend/src/features/Vessel/components/VesselLabelOverlay/index.tsx @@ -1,4 +1,5 @@ import { monitorfishMap } from '@features/Map/monitorfishMap' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMoveOverlayWhenDragging } from '@hooks/useMoveOverlayWhenDragging' import { useMoveOverlayWhenZooming } from '@hooks/useMoveOverlayWhenZooming' import Overlay from 'ol/Overlay' @@ -6,7 +7,6 @@ import { useEffect, useMemo, useRef, useState } from 'react' import styled from 'styled-components' import { VesselLabel } from './VesselLabel' -import { getVesselCompositeIdentifier } from '../../../../domain/entities/vessel/vessel' import type { MutableRefObject } from 'react' diff --git a/frontend/src/features/Vessel/components/VesselList/VesselListModal.tsx b/frontend/src/features/Vessel/components/VesselList/VesselListModal.tsx index 32503cd6fe..dbc656e12f 100644 --- a/frontend/src/features/Vessel/components/VesselList/VesselListModal.tsx +++ b/frontend/src/features/Vessel/components/VesselList/VesselListModal.tsx @@ -20,11 +20,11 @@ import { unselectVessel } from '../../../../domain/use_cases/vessel/unselectVess import { LegacyRsuiteComponentsWrapper } from '../../../../ui/LegacyRsuiteComponentsWrapper' import PreviewSVG from '../../../icons/Oeil_apercu_carte.svg?react' -import type { VesselEnhancedLastPositionWebGLObject } from '../../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' const NOT_FOUND = -1 -type CheckedVesselEnhancedLastPositionWebGLObject = VesselEnhancedLastPositionWebGLObject & { +type CheckedVesselEnhancedLastPositionWebGLObject = Vessel.VesselEnhancedLastPositionWebGLObject & { checked?: boolean } diff --git a/frontend/src/features/Vessel/components/VesselSearch/VesselSearchResult.tsx b/frontend/src/features/Vessel/components/VesselSearch/VesselSearchResult.tsx index 782b3fb1b5..130ec47303 100644 --- a/frontend/src/features/Vessel/components/VesselSearch/VesselSearchResult.tsx +++ b/frontend/src/features/Vessel/components/VesselSearch/VesselSearchResult.tsx @@ -1,14 +1,12 @@ -import { getVesselIdentityFromLegacyVesselIdentity } from '@features/Vessel/utils' +import { getVesselCompositeIdentifier, getVesselIdentityFromLegacyVesselIdentity } from '@features/Vessel/utils' import { localStorageManager } from '@libs/LocalStorageManager' import { LocalStorageKey } from '@libs/LocalStorageManager/constants' import { useMemo } from 'react' import styled from 'styled-components' import { VesselSearchResultItem } from './VesselSearchResultItem' -import { getVesselCompositeIdentifier } from '../../../../domain/entities/vessel/vessel' import type { Vessel } from '../../Vessel.types' -import type { VesselIdentity } from 'domain/entities/vessel/types' type VesselSearchResultProps = Readonly<{ foundVessels: Vessel.VesselIdentity[] @@ -25,7 +23,7 @@ export function VesselSearchResult({ const baseUrl = useMemo(() => window.location.origin, []) const lastSearchResults = localStorageManager - .get(LocalStorageKey.LastSearchVessels, []) + .get(LocalStorageKey.LastSearchVessels, []) .map(getVesselIdentityFromLegacyVesselIdentity) return ( diff --git a/frontend/src/features/Vessel/components/VesselSearch/utils.ts b/frontend/src/features/Vessel/components/VesselSearch/utils.ts index 5a3cfe9b58..22c2cd3951 100644 --- a/frontend/src/features/Vessel/components/VesselSearch/utils.ts +++ b/frontend/src/features/Vessel/components/VesselSearch/utils.ts @@ -1,6 +1,4 @@ -import { VesselIdentifier } from '../../../../domain/entities/vessel/types' - -import type { Vessel } from '@features/Vessel/Vessel.types' +import { Vessel } from '@features/Vessel/Vessel.types' export function enrichWithVesselIdentifierIfUndefined(identity: Vessel.VesselIdentity): Vessel.VesselIdentity { if (identity.vesselIdentifier) { @@ -8,15 +6,15 @@ export function enrichWithVesselIdentifierIfUndefined(identity: Vessel.VesselIde } if (identity.internalReferenceNumber) { - return { ...identity, vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER } + return { ...identity, vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER } } if (identity.ircs) { - return { ...identity, vesselIdentifier: VesselIdentifier.IRCS } + return { ...identity, vesselIdentifier: Vessel.VesselIdentifier.IRCS } } if (identity.externalReferenceNumber) { - return { ...identity, vesselIdentifier: VesselIdentifier.EXTERNAL_REFERENCE_NUMBER } + return { ...identity, vesselIdentifier: Vessel.VesselIdentifier.EXTERNAL_REFERENCE_NUMBER } } return identity diff --git a/frontend/src/features/Vessel/components/VesselSidebar/Controls/InfractionsSummary.tsx b/frontend/src/features/Vessel/components/VesselSidebar/Controls/InfractionsSummary.tsx index 6fc99d6b1f..9682ad554e 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/Controls/InfractionsSummary.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/Controls/InfractionsSummary.tsx @@ -3,9 +3,9 @@ import styled from 'styled-components' import { NoValue } from '../common_styles/common.style' type InfractionsSummaryProps = { - numberOfControlsWithSomeGearsSeized: number - numberOfControlsWithSomeSpeciesSeized: number - numberOfDiversions: number + numberOfControlsWithSomeGearsSeized: number | undefined + numberOfControlsWithSomeSpeciesSeized: number | undefined + numberOfDiversions: number | undefined } export function InfractionsSummary({ numberOfControlsWithSomeGearsSeized, diff --git a/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/VesselName.tsx b/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/VesselName.tsx index 418f84e4d2..bdbc8486d2 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/VesselName.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/VesselName.tsx @@ -1,7 +1,7 @@ import { COLORS } from '@constants/constants' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' -import { getVesselCompositeIdentifier } from 'domain/entities/vessel/vessel' import { addVesselToFavorites, removeVesselFromFavorites } from 'domain/shared_slices/FavoriteVessel' import { unselectVessel } from 'domain/use_cases/vessel/unselectVessel' import countries from 'i18n-iso-countries' diff --git a/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/index.tsx b/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/index.tsx index 2ddf21fef5..18dfbded64 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/index.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/VesselSidebarHeader/index.tsx @@ -13,7 +13,7 @@ import { MapComponent } from '../../../../commonStyles/MapComponent' import SearchIconSVG from '../../../../icons/Loupe.svg?react' import { VesselSearch } from '../../../../VesselSearch' -import type { VesselIdentity } from 'domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' export function VesselSidebarHeader() { const dispatch = useMainAppDispatch() @@ -29,7 +29,7 @@ export function VesselSidebarHeader() { const isRightMenuShrinked = vesselSidebarIsOpen && !rightMenuIsOpen const handleVesselChange = useCallback( - (vesselIdentity: VesselIdentity | undefined) => { + (vesselIdentity: Vessel.VesselIdentity | undefined) => { if (!vesselIdentity) { return } diff --git a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/ExportTrack.tsx b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/ExportTrack.tsx index 7f77f83c56..5fb20e0bc8 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/ExportTrack.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/ExportTrack.tsx @@ -1,10 +1,10 @@ import { WSG84_PROJECTION } from '@features/Map/constants' +import { Vessel } from '@features/Vessel/Vessel.types' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { useTracking } from '@hooks/useTracking' import { downloadAsCsv } from '@utils/downloadAsCsv' import { getCoordinates } from 'coordinates' import dayjs from 'dayjs' -import { NetworkType } from 'domain/entities/vessel/types' import countries from 'i18n-iso-countries' import { useCallback, useMemo } from 'react' import styled from 'styled-components' @@ -14,9 +14,8 @@ import { PrimaryButton } from '../../../../../commonStyles/Buttons.style' import ExportSVG from '../../../../../icons/Bouton_exporter_piste_navire.svg?react' import type { DownloadAsCsvMap } from '@utils/downloadAsCsv' -import type { VesselPosition } from 'domain/entities/vessel/types' -type VesselPositionWithId = VesselPosition & { +type VesselPositionWithId = Vessel.VesselPosition & { id: string latitude: string longitude: string @@ -87,11 +86,11 @@ export function ExportTrack() { networkType: { label: 'Type de réseau', transform: position => { - if (position.networkType === NetworkType.CELLULAR) { + if (position.networkType === Vessel.NetworkType.CELLULAR) { return 'Cellulaire' } - if (position.networkType === NetworkType.SATELLITE) { + if (position.networkType === Vessel.NetworkType.SATELLITE) { return 'Satellite' } diff --git a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/HighlightPositionCell.tsx b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/HighlightPositionCell.tsx index 8224dd50b7..f22079358f 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/HighlightPositionCell.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/HighlightPositionCell.tsx @@ -10,13 +10,13 @@ import ManualPositionSVG from '../../../../../icons/Pastille_position_manuelle.s import { animateToCoordinates } from '../../../../../Map/slice' import { highlightVesselTrackPosition } from '../../../../slice' -import type { VesselPosition } from '../../../../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' type HighlightPositionCellProps = { isAtPortPositionMarkerShowed?: boolean isManualPositionMarkerShowed?: boolean isNetworkTypeMarkerShowed?: boolean - row: VesselPosition + row: Vessel.VesselPosition value: unknown } export function HighlightPositionCell({ diff --git a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/constants.tsx b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/constants.tsx index af00061667..e67fe5a100 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/constants.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/actions/TrackRequest/constants.tsx @@ -4,11 +4,11 @@ import { isNumeric } from '@utils/isNumeric' import { VesselTrackDepth } from '../../../../../../domain/entities/vesselTrackDepth' -import type { VesselPosition } from '../../../../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { Option } from '@mtes-mct/monitor-ui' import type { ColumnDef } from '@tanstack/react-table' -export const POSITION_TABLE_COLUMNS: Array> = [ +export const POSITION_TABLE_COLUMNS: Array> = [ { accessorKey: 'id', cell: info => , diff --git a/frontend/src/features/Vessel/components/VesselSidebar/index.tsx b/frontend/src/features/Vessel/components/VesselSidebar/index.tsx index 43b0788c80..159177b478 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/index.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/index.tsx @@ -9,7 +9,7 @@ import { ShowFishingActivitiesOnMap } from './actions/show_fishing_activities' import { TrackRequest } from './actions/TrackRequest' import { Body } from './Body' import { Tabs } from './Tabs' -import { Vessel } from '../../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../../domain/entities/vessel/vessel' import { MapComponent } from '../../../commonStyles/MapComponent' export function VesselSidebar() { @@ -22,7 +22,7 @@ export function VesselSidebar() { useEffect(() => { if (selectedVessel) { - trackPage(Vessel.getVesselFeatureId(selectedVessel)) + trackPage(VesselFeature.getVesselFeatureId(selectedVessel)) } }, [trackPage, selectedVessel]) diff --git a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorCursor.tsx b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorCursor.tsx index 7d0f61fc34..bd26b6edf9 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorCursor.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorCursor.tsx @@ -8,8 +8,8 @@ type RiskFactorCursorProps = { height: number isBig?: boolean progress: number - underCharter?: boolean - value: string + underCharter?: boolean | undefined + value: number | undefined withoutBox?: boolean } export function RiskFactorCursor({ @@ -34,7 +34,7 @@ export function RiskFactorCursor({ return ( - {value} + {value?.toFixed(1)} {underCharter ? : null} diff --git a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorExplanationModal.tsx b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorExplanationModal.tsx index 352a2ea8ac..852074ea07 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorExplanationModal.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorExplanationModal.tsx @@ -3,17 +3,17 @@ import styled from 'styled-components' import { RiskFactorBox } from './RiskFactorBox' import { RiskFactorExplanationSchema } from './RiskFactorExplanationSchema' -import { - getDetectabilityRiskFactorText, - getImpactRiskFactorText, - getProbabilityRiskFactorText, - getRiskFactorColor -} from '../../../../../domain/entities/vessel/riskFactor' import { StyledModalHeader } from '../../../../commonComponents/StyledModalHeader' import { basePrimaryButton, SecondaryButton } from '../../../../commonStyles/Buttons.style' import RiskFactorControlSVG from '../../../../icons/Note_de_controle_gyrophare.svg?react' import RiskFactorImpactSVG from '../../../../icons/Note_impact_poisson.svg?react' import RiskFactorInfractionsSVG from '../../../../icons/Note_infraction_stop.svg?react' +import { + getDetectabilityRiskFactorText, + getImpactRiskFactorText, + getProbabilityRiskFactorText, + getRiskFactorColor +} from '../../../../RiskFactor/utils' import type { Promisable } from 'type-fest' diff --git a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorResume.tsx b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorResume.tsx index e6cb0d9162..292dc12c52 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorResume.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/RiskFactorResume.tsx @@ -1,5 +1,13 @@ // TODO Fix the `no-unsafe-optional-chaining` statements. +import { ChevronIcon } from '@features/commonStyles/icons/ChevronIcon.style' +import { + getDetectabilityRiskFactorText, + getImpactRiskFactorText, + getProbabilityRiskFactorText, + getRiskFactorColor +} from '@features/RiskFactor/utils' +import { useMainAppSelector } from '@hooks/useMainAppSelector' import { useState } from 'react' import styled from 'styled-components' @@ -8,14 +16,6 @@ import { ImpactRiskFactorDetails } from './details/ImpactRiskFactorDetails' import { ProbabilityRiskFactorDetails } from './details/ProbabilityRiskFactorDetails' import { RiskFactorCursor } from './RiskFactorCursor' import { RiskFactorExplanationModal } from './RiskFactorExplanationModal' -import { - getDetectabilityRiskFactorText, - getImpactRiskFactorText, - getProbabilityRiskFactorText, - getRiskFactorColor -} from '../../../../../domain/entities/vessel/riskFactor' -import { useMainAppSelector } from '../../../../../hooks/useMainAppSelector' -import { ChevronIcon } from '../../../../commonStyles/icons/ChevronIcon.style' import RiskFactorControlSVG from '../../../../icons/Note_de_controle_gyrophare.svg?react' import RiskFactorImpactSVG from '../../../../icons/Note_impact_poisson.svg?react' import RiskFactorInfractionsSVG from '../../../../icons/Note_infraction_stop.svg?react' @@ -36,14 +36,13 @@ export function RiskFactorResume() { Note de risque globale {getImpactRiskFactorText( - selectedVessel?.riskFactor?.impactRiskFactor, - !!selectedVessel?.riskFactor?.segmentHighestImpact + selectedVessel.riskFactor.impactRiskFactor, + !!selectedVessel.riskFactor.segmentHighestImpact )} @@ -95,22 +93,21 @@ export function RiskFactorResume() { {getProbabilityRiskFactorText( - selectedVessel?.riskFactor?.probabilityRiskFactor, - !!selectedVessel?.riskFactor?.numberControlsLastFiveYears + selectedVessel.riskFactor.probabilityRiskFactor, + !!selectedVessel.riskFactor.numberControlsLastFiveYears )} @@ -126,17 +123,16 @@ export function RiskFactorResume() { - {getDetectabilityRiskFactorText(selectedVessel?.riskFactor?.detectabilityRiskFactor, true)} + {getDetectabilityRiskFactorText(selectedVessel.riskFactor.detectabilityRiskFactor, true)} @@ -151,7 +147,7 @@ export function RiskFactorResume() { } const GlobalText = styled.div<{ - $underCharter: boolean + $underCharter: boolean | undefined }>` ${p => p.$underCharter @@ -203,7 +199,7 @@ const GlobalRisk = styled.div` ` const SeeMore = styled.a<{ - $underCharter: boolean + $underCharter: boolean | undefined }>` font-size: 11px; color: ${p => p.theme.color.slateGray}; diff --git a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/DetectabilityRiskFactorDetails.tsx b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/DetectabilityRiskFactorDetails.tsx index 28dcc1e71a..eecb948333 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/DetectabilityRiskFactorDetails.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/DetectabilityRiskFactorDetails.tsx @@ -1,11 +1,9 @@ +import { getControlPriorityLevel, getControlRateRiskFactorText, getRiskFactorColor } from '@features/RiskFactor/utils' import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { isDefined } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' import styled from 'styled-components' -import { - getControlPriorityLevel, - getControlRateRiskFactorText, - getRiskFactorColor -} from '../../../../../../domain/entities/vessel/riskFactor' import { getDate } from '../../../../../../utils' import { RiskFactorCursor } from '../RiskFactorCursor' @@ -13,22 +11,19 @@ type DetectabilityRiskFactorDetailsProps = { isOpen: boolean } export function DetectabilityRiskFactorDetails({ isOpen }: DetectabilityRiskFactorDetailsProps) { - const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) - - // TODO Fix `riskFactor` does not exist on type `AugmentedSelectedVessel`. - // @ts-ignore - const { riskFactor } = selectedVessel + const riskFactor = useMainAppSelector(state => state.vessel.selectedVessel)?.riskFactor + assertNotNullish(riskFactor) return ( - Priorité du segment {riskFactor?.segmentHighestPriority} + Priorité du segment {riskFactor.segmentHighestPriority} - {riskFactor?.controlPriorityLevel ? ( - `${riskFactor?.controlPriorityLevel?.toFixed(1)} – ${getControlPriorityLevel( - riskFactor?.controlPriorityLevel, - riskFactor?.segmentHighestPriority + {riskFactor.controlPriorityLevel ? ( + `${riskFactor.controlPriorityLevel?.toFixed(1)} – ${getControlPriorityLevel( + riskFactor.controlPriorityLevel, + riskFactor.segmentHighestPriority )}` ) : ( - @@ -36,19 +31,19 @@ export function DetectabilityRiskFactorDetails({ isOpen }: DetectabilityRiskFact Priorité du navire - {riskFactor?.controlRateRiskFactor ? ( - `${riskFactor?.controlRateRiskFactor?.toFixed(1)} – ${getControlRateRiskFactorText( - riskFactor?.controlRateRiskFactor + {riskFactor.controlRateRiskFactor ? ( + `${riskFactor.controlRateRiskFactor?.toFixed(1)} – ${getControlRateRiskFactorText( + riskFactor.controlRateRiskFactor )}` ) : ( - @@ -56,11 +51,11 @@ export function DetectabilityRiskFactorDetails({ isOpen }: DetectabilityRiskFact @@ -69,9 +64,9 @@ export function DetectabilityRiskFactorDetails({ isOpen }: DetectabilityRiskFact Temporalité - {riskFactor?.numberControlsLastThreeYears || riskFactor?.numberControlsLastThreeYears === 0 ? ( - `${riskFactor?.numberControlsLastThreeYears} contrôle${ - riskFactor?.numberControlsLastThreeYears > 1 ? 's' : '' + {isDefined(riskFactor.numberControlsLastThreeYears) ? ( + `${riskFactor.numberControlsLastThreeYears} contrôle${ + riskFactor.numberControlsLastThreeYears > 1 ? 's' : '' } sur les 3 dernières années` ) : ( - @@ -81,8 +76,8 @@ export function DetectabilityRiskFactorDetails({ isOpen }: DetectabilityRiskFact Dernier contrôle - {riskFactor?.lastControlDatetime ? ( - `Le ${getDate(riskFactor?.lastControlDatetime)}` + {riskFactor.lastControlDatetime ? ( + `Le ${getDate(riskFactor.lastControlDatetime)}` ) : ( - )} diff --git a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/ImpactRiskFactorDetails.tsx b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/ImpactRiskFactorDetails.tsx index c49f5d1e32..7102eb0f52 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/ImpactRiskFactorDetails.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/risk_factor/details/ImpactRiskFactorDetails.tsx @@ -1,31 +1,32 @@ import { VesselCurrentFleetSegmentDetails } from '@features/FleetSegment/components/VesselCurrentFleetSegmentDetails' import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { assertNotNullish } from '@utils/assertNotNullish' import { useRef } from 'react' import styled from 'styled-components' import InfoSVG from '../../../../../icons/Information.svg?react' export function ImpactRiskFactorDetails({ isOpen }) { - const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) + const riskFactor = useMainAppSelector(state => state.vessel.selectedVessel)?.riskFactor + assertNotNullish(riskFactor) const currentFleetSegmentDetailsElementRef = useRef(null) - const riskFactor = selectedVessel?.riskFactor return ( - {riskFactor?.segmentHighestImpact ? ( + {riskFactor.segmentHighestImpact ? ( <> Segment de flotte actuel - {riskFactor?.segmentHighestImpact}{' '} + {riskFactor.segmentHighestImpact}{' '} state.vessel.selectedVessel) + const riskFactor = useMainAppSelector(state => state.vessel.selectedVessel)?.riskFactor + assertNotNullish(riskFactor) const currentYear = new Date().getUTCFullYear() - if (!selectedVessel) { - return <> - } - return ( @@ -28,10 +26,9 @@ export function ProbabilityRiskFactorDetails({ isOpen }) { Temporalité - {selectedVessel.riskFactor.numberControlsLastFiveYears || - selectedVessel.riskFactor.numberControlsLastFiveYears === 0 ? ( - `${selectedVessel.riskFactor.numberControlsLastFiveYears} contrôle${ - selectedVessel.riskFactor.numberControlsLastFiveYears > 1 ? 's' : '' + {isDefined(riskFactor.numberControlsLastFiveYears) ? ( + `${riskFactor.numberControlsLastFiveYears} contrôle${ + riskFactor.numberControlsLastFiveYears > 1 ? 's' : '' } sur 5 ans (${new Date(currentYear - 4, 0, 1).getUTCFullYear()} - ${currentYear})` ) : ( - @@ -41,14 +38,12 @@ export function ProbabilityRiskFactorDetails({ isOpen }) { Infractions pêche - {(selectedVessel.riskFactor.numberInfractionsLastFiveYears || - selectedVessel.riskFactor.numberInfractionsLastFiveYears === 0) && - (selectedVessel.riskFactor.numberControlsLastFiveYears || - selectedVessel.riskFactor.numberControlsLastFiveYears === 0) ? ( - `${selectedVessel.riskFactor.numberInfractionsLastFiveYears} infraction${ - selectedVessel.riskFactor.numberInfractionsLastFiveYears > 1 ? 's' : '' - } pêche / ${selectedVessel.riskFactor.numberControlsLastFiveYears} contrôle${ - selectedVessel.riskFactor.numberControlsLastFiveYears > 1 ? 's' : '' + {isDefined(riskFactor.numberInfractionsLastFiveYears) && + isDefined(riskFactor.numberControlsLastFiveYears) ? ( + `${riskFactor.numberInfractionsLastFiveYears} infraction${ + riskFactor.numberInfractionsLastFiveYears > 1 ? 's' : '' + } pêche / ${riskFactor.numberControlsLastFiveYears} contrôle${ + riskFactor.numberControlsLastFiveYears > 1 ? 's' : '' }` ) : ( - @@ -63,14 +58,14 @@ export function ProbabilityRiskFactorDetails({ isOpen }) { } const NoValue = styled.span` - color: ${COLORS.slateGray}; + color: ${p => p.theme.color.slateGray}; font-weight: 300; line-height: normal; ` const Line = styled.div` width: 100%; - border-bottom: 1px solid ${COLORS.lightGray}; + border-bottom: 1px solid ${p => p.theme.color.lightGray}; ` const SubRiskDetails = styled.div<{ @@ -95,7 +90,7 @@ const Zone = styled.div` text-align: left; display: flex; flex-wrap: wrap; - background: ${COLORS.white}; + background: ${p => p.theme.color.white}; ` const Fields = styled.table` @@ -114,7 +109,7 @@ const Field = styled.tr` ` const Key = styled.th` - color: ${COLORS.slateGray}; + color: ${p => p.theme.color.slateGray}; flex: initial; display: inline-block; margin: 0; @@ -130,7 +125,7 @@ const Key = styled.th` const Value = styled.td` font-size: 13px; - color: ${COLORS.gunMetal}; + color: ${p => p.theme.color.gunMetal}; margin: 0; text-align: left; padding: 1px 5px 5px 5px; diff --git a/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx b/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx index c9b829b1d6..b7c4024f36 100644 --- a/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselAlertAndBeaconMalfunctionLayer.jsx @@ -7,10 +7,11 @@ import { useSelector } from 'react-redux' import { getVesselAlertAndBeaconMalfunctionStyle } from './style' import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' -import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel' +import { vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { LayerProperties } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' import { vesselSelectors } from '../slice' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' function VesselAlertAndBeaconMalfunctionLayer() { const isSuperUser = useIsSuperUser() diff --git a/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx b/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx index e812d2f293..b921af7f99 100644 --- a/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselAlertLayer.jsx @@ -8,14 +8,14 @@ import { useSelector } from 'react-redux' import { getVesselAlertStyle } from './style' import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' import { - getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, - Vessel, + VesselFeature, vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { LayerProperties } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' import { vesselSelectors } from '../slice' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' function VesselAlertLayer() { const isSuperUser = useIsSuperUser() @@ -92,7 +92,7 @@ function VesselAlertLayer() { if (hideNonSelectedVessels && !vesselIsShowed(vessel, vesselsTracksShowed, selectedVesselIdentity)) { return features } - if (!Vessel.getVesselOpacity(vessel.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { + if (!VesselFeature.getVesselOpacity(vessel.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { return features } diff --git a/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx b/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx index 4ec3d48c06..ce987bc16e 100644 --- a/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselBeaconMalfunctionLayer.jsx @@ -7,10 +7,11 @@ import { useSelector } from 'react-redux' import { getVesselBeaconMalfunctionStyle } from './style' import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' -import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../domain/entities/vessel/vessel' +import { vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { LayerProperties } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' import { vesselSelectors } from '../slice' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' function VesselBeaconMalfunctionLayer() { const isSuperUser = useIsSuperUser() diff --git a/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx b/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx index a190d75b4b..1244b38a8c 100644 --- a/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselEstimatedPositionLayer.jsx @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux' import { getEstimatedPositionStyle } from './styles/vesselEstimatedPosition.style' import { EstimatedPosition } from '../../../domain/entities/estimatedPosition' -import { getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed } from '../../../domain/entities/vessel/vessel' +import { getVesselLastPositionVisibilityDates, VesselFeature, vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { LayerProperties } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' import { vesselSelectors } from '../slice' @@ -89,7 +89,7 @@ function VesselEstimatedPositionLayer() { return EstimatedPosition.getFeatures(vessel, options) } - const isLight = Vessel.iconIsLight(selectedBaseLayer) + const isLight = VesselFeature.iconIsLight(selectedBaseLayer) const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) const options = { diff --git a/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx b/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx index ab78b257c1..ab09ecca9d 100644 --- a/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselInfractionSuspicionLayer.jsx @@ -8,14 +8,14 @@ import { useSelector } from 'react-redux' import { getVesselInfractionSuspicionStyle } from './style' import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' import { - getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, - Vessel, + VesselFeature, vesselIsShowed } from '../../../domain/entities/vessel/vessel' import { LayerProperties } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' import { vesselSelectors } from '../slice' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' function VesselInfractionSuspicionLayer() { const isSuperUser = useIsSuperUser() @@ -88,7 +88,7 @@ function VesselInfractionSuspicionLayer() { if (hideNonSelectedVessels && !vesselIsShowed(vessel, vesselsTracksShowed, selectedVesselIdentity)) { return features } - if (!Vessel.getVesselOpacity(vessel.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { + if (!VesselFeature.getVesselOpacity(vessel.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { return features } diff --git a/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx b/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx index 68f81d193c..70bed1da37 100644 --- a/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx +++ b/frontend/src/features/Vessel/layers/VesselSelectedLayer.jsx @@ -7,7 +7,7 @@ import React, { useEffect, useRef } from 'react' import { useSelector } from 'react-redux' import { getSelectedVesselStyle } from './style' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { LayerProperties, OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../Map/constants' import { monitorfishMap } from '../../Map/monitorfishMap' @@ -22,7 +22,7 @@ function VesselSelectedLayer() { wrapX: false }) ) - const isLight = Vessel.iconIsLight(selectedBaseLayer) + const isLight = VesselFeature.iconIsLight(selectedBaseLayer) const style = getSelectedVesselStyle({ isLight }) const layerRef = useRef( new Vector({ diff --git a/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx b/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx index 0ded2ea4e7..785dba0fac 100644 --- a/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx +++ b/frontend/src/features/Vessel/layers/VesselsLabelsLayer.tsx @@ -2,7 +2,7 @@ import { LayerProperties } from '@features/Map/constants' import { getLabelLineStyle } from '@features/Map/layers/styles/labelLine.style' import { MonitorFishMap } from '@features/Map/Map.types' import { monitorfishMap } from '@features/Map/monitorfishMap' -import { extractVesselPropertiesFromFeature } from '@features/Vessel/utils' +import { extractVesselPropertiesFromFeature, getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { usePrevious } from '@mtes-mct/monitor-ui' import LineString from 'ol/geom/LineString' @@ -12,11 +12,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { useIsSuperUser } from '../../../auth/hooks/useIsSuperUser' import { drawMovedLabelLineIfFoundAndReturnOffset } from '../../../domain/entities/vessel/label' -import { - getVesselCompositeIdentifier, - getVesselLastPositionVisibilityDates, - Vessel -} from '../../../domain/entities/vessel/vessel' +import { getVesselLastPositionVisibilityDates, VesselFeature } from '../../../domain/entities/vessel/vessel' import { VesselLabelLine } from '../../../domain/entities/vesselLabelLine' import { VesselLabelOverlay } from '../components/VesselLabelOverlay' import { vesselSelectors } from '../slice' @@ -204,7 +200,11 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { // @ts-ignore ?.getSource() vesselsLayer?.current?.forEachFeatureInExtent(monitorfishMap.getView().calculateExtent(), vesselFeature => { - const opacity = Vessel.getVesselOpacity(vesselFeature.get('dateTime'), vesselIsHidden, vesselIsOpacityReduced) + const opacity = VesselFeature.getVesselOpacity( + vesselFeature.get('dateTime'), + vesselIsHidden, + vesselIsOpacityReduced + ) const identity = { externalReferenceNumber: vesselFeature.get('externalReferenceNumber'), internalReferenceNumber: vesselFeature.get('internalReferenceNumber'), @@ -250,7 +250,7 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { 'vesselIdentifier', 'vesselName' ]) - const label = Vessel.getVesselFeatureLabel(vesselProperties, { + const label = VesselFeature.getVesselFeatureLabel(vesselProperties, { hideVesselsAtPort, isRiskFactorShowed: isSuperUser && riskFactorShowedOnMap, vesselLabel, @@ -264,7 +264,7 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { } const labelLineFeatureId = VesselLabelLine.getFeatureId(identity) const opacity = - Vessel.getVesselOpacity(vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced) || + VesselFeature.getVesselOpacity(vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced) || vesselProperties.beaconMalfunctionId ? 1 : 0 @@ -333,7 +333,7 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { const isFiltered = filterShowed && nonFilteredVesselsAreHidden // && filteredVesselsFeaturesUids?.length FIXME: if filterShowed, is it really necessary to check filteredVesselsFeaturesUids ? let featuresRequiringLabel if (hideNonSelectedVessels) { - const selectedVesselId = selectedVessel && Vessel.getVesselFeatureId(selectedVessel) + const selectedVesselId = selectedVessel && VesselFeature.getVesselFeatureId(selectedVessel) const showedFeaturesIdentities = Object.keys(vesselsTracksShowed) featuresRequiringLabel = featuresInExtent.filter( feature => diff --git a/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx b/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx index f04dca9369..9e2a0023c7 100644 --- a/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx +++ b/frontend/src/features/Vessel/layers/VesselsLayer/index.tsx @@ -5,7 +5,7 @@ import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/const import { useMainAppSelector } from '@hooks/useMainAppSelector' import { memo, useEffect } from 'react' -import { getVesselLastPositionVisibilityDates, Vessel } from '../../../../domain/entities/vessel/vessel' +import { getVesselLastPositionVisibilityDates, VesselFeature } from '../../../../domain/entities/vessel/vessel' import { theme } from '../../../../ui/theme' import { booleanToInt, customHexToRGB } from '../../../../utils' import { getWebGLVesselStyleVariables } from '../style' @@ -24,7 +24,7 @@ function UnmemoizedVesselsLayer() { useEffect(() => { // styles derived from state - const isLight = Vessel.iconIsLight(selectedBaseLayer) + const isLight = VesselFeature.iconIsLight(selectedBaseLayer) const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) const filterColorRGBArray = customHexToRGB( @@ -81,7 +81,7 @@ function UnmemoizedVesselsLayer() { }, [previewFilteredVesselsMode]) useEffect(() => { - const isLight = Vessel.iconIsLight(selectedBaseLayer) + const isLight = VesselFeature.iconIsLight(selectedBaseLayer) VESSELS_VECTOR_LAYER.updateStyleVariables({ isLight: booleanToInt(isLight) }) }, [selectedBaseLayer]) diff --git a/frontend/src/features/Vessel/layers/VesselsTracksLayer.tsx b/frontend/src/features/Vessel/layers/VesselsTracksLayer.tsx index 8d0dd5a65f..693814bd85 100644 --- a/frontend/src/features/Vessel/layers/VesselsTracksLayer.tsx +++ b/frontend/src/features/Vessel/layers/VesselsTracksLayer.tsx @@ -2,6 +2,7 @@ import CloseVesselTrackOverlay from '@features/Map/components/CloseVesselTrackOv import FishingActivityOverlay from '@features/Map/components/FishingActivityOverlay' import { LayerProperties } from '@features/Map/constants' import { monitorfishMap } from '@features/Map/monitorfishMap' +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { usePrevious } from '@mtes-mct/monitor-ui' @@ -18,7 +19,6 @@ import { removeVesselTrackFeatures, updateTrackCircleStyle } from '../../../domain/entities/vessel/track' -import { getVesselCompositeIdentifier } from '../../../domain/entities/vessel/vessel' import { logbookActions } from '../../Logbook/slice' import { getFishingActivityFeatureOnTrackLine } from '../../Logbook/utils' import { animateToCoordinates } from '../../Map/slice' diff --git a/frontend/src/features/Vessel/layers/style.ts b/frontend/src/features/Vessel/layers/style.ts index b42e41074a..a0773d2041 100644 --- a/frontend/src/features/Vessel/layers/style.ts +++ b/frontend/src/features/Vessel/layers/style.ts @@ -4,7 +4,7 @@ import Circle from 'ol/style/Circle' import Stroke from 'ol/style/Stroke' import { - Vessel, + VesselFeature, VESSEL_ALERT_AND_BEACON_MALFUNCTION, VESSEL_ALERT_STYLE, VESSEL_BEACON_MALFUNCTION_STYLE, @@ -73,7 +73,7 @@ export const getWebGLVesselStyle = (): WebGLStyle => { filterColor, defaultVesselColor ], - 'icon-offset': ['case', ['>', ['get', 'speed'], Vessel.vesselIsMovingSpeed], [0, 0], [25, 0]], + 'icon-offset': ['case', ['>', ['get', 'speed'], VesselFeature.vesselIsMovingSpeed], [0, 0], [25, 0]], 'icon-opacity': [ 'case', featureHas('hasBeaconMalfunction'), diff --git a/frontend/src/features/Vessel/slice.ts b/frontend/src/features/Vessel/slice.ts index 59a900569b..05db6c3d3b 100644 --- a/frontend/src/features/Vessel/slice.ts +++ b/frontend/src/features/Vessel/slice.ts @@ -1,29 +1,17 @@ import { reportingIsAnInfractionSuspicion } from '@features/Reporting/utils' +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { createEntityAdapter, createSlice, type EntityState, type PayloadAction } from '@reduxjs/toolkit' -import { - atLeastOneVesselSelected, - getOnlyVesselIdentityProperties, - Vessel, - VesselSidebarTab -} from '../../domain/entities/vessel/vessel' +import { atLeastOneVesselSelected, VesselFeature, VesselSidebarTab } from '../../domain/entities/vessel/vessel' import { ReportingType, ReportingTypeCharacteristics } from '../Reporting/types' -import type { - FishingActivityShowedOnMap, - ShowedVesselTrack, - TrackRequest, - VesselEnhancedLastPositionWebGLObject, - VesselFeatureId, - VesselIdentity, - VesselPosition -} from '../../domain/entities/vessel/types' -import type { Vessel as VesselTypes } from '@features/Vessel/Vessel.types' +import type { FishingActivityShowedOnMap, ShowedVesselTrack, TrackRequest } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' const NOT_FOUND = -1 export const vesselsAdapter = createEntityAdapter({ - selectId: (vessel: VesselEnhancedLastPositionWebGLObject) => vessel.vesselFeatureId, + selectId: (vessel: Vessel.VesselEnhancedLastPositionWebGLObject) => vessel.vesselFeatureId, sortComparer: false }) @@ -31,13 +19,13 @@ export const vesselsAdapter = createEntityAdapter({ export type VesselState = { fishingActivitiesShowedOnMap: FishingActivityShowedOnMap[] hideNonSelectedVessels: boolean - highlightedVesselTrackPosition: VesselPosition | null + highlightedVesselTrackPosition: Vessel.VesselPosition | null isFocusedOnVesselSearch: boolean loadingPositions: boolean | null loadingVessel: boolean | null - selectedVessel: VesselTypes.AugmentedSelectedVessel | undefined - selectedVesselIdentity: VesselIdentity | undefined - selectedVesselPositions: VesselPosition[] | null + selectedVessel: Vessel.AugmentedSelectedVessel | undefined + selectedVesselIdentity: Vessel.VesselIdentity | undefined + selectedVesselPositions: Vessel.VesselPosition[] | null selectedVesselSidebarTab: VesselSidebarTab selectedVesselTrackRequest: TrackRequest | null tripMessagesLastToFormerDEPDateTimes: any[] @@ -45,7 +33,7 @@ export type VesselState = { uniqueVesselsSpecies: any[] vesselSidebarIsOpen: boolean vesselTrackExtent: any | null - vessels: EntityState + vessels: EntityState vesselsEstimatedPositions: any[] vesselsTracksShowed: Record } @@ -84,7 +72,7 @@ const vesselSlice = createSlice({ state, action: PayloadAction<{ reportingType: ReportingType - vesselFeatureId: VesselFeatureId + vesselFeatureId: Vessel.VesselFeatureId }> ) { const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) @@ -104,7 +92,7 @@ const vesselSlice = createSlice({ if ( state.selectedVesselIdentity && - Vessel.getVesselFeatureId(state.selectedVesselIdentity) === action.payload.vesselFeatureId + VesselFeature.getVesselFeatureId(state.selectedVesselIdentity) === action.payload.vesselFeatureId ) { let reportings: ReportingType[] = [] @@ -115,7 +103,7 @@ const vesselSlice = createSlice({ const nextVesselReportings = reportings.concat(action.payload.reportingType) state.selectedVessel = { - ...(state.selectedVessel as VesselTypes.AugmentedSelectedVessel), + ...(state.selectedVessel as Vessel.AugmentedSelectedVessel), hasInfractionSuspicion: nextVesselReportings.some(reportingIsAnInfractionSuspicion), reportings: nextVesselReportings } @@ -154,7 +142,7 @@ const vesselSlice = createSlice({ /** * Highlight a vessel position on map from the vessel track positions table */ - highlightVesselTrackPosition(state, action: PayloadAction) { + highlightVesselTrackPosition(state, action: PayloadAction) { state.highlightedVesselTrackPosition = action.payload }, @@ -162,7 +150,7 @@ const vesselSlice = createSlice({ state, action: PayloadAction<{ calledFromCron: boolean - vesselIdentity: VesselIdentity + vesselIdentity: Vessel.VesselIdentity }> ) { state.selectedVesselIdentity = action.payload.vesselIdentity @@ -183,7 +171,7 @@ const vesselSlice = createSlice({ action: PayloadAction<{ alertType: string isValidated: boolean - vesselFeatureId: VesselFeatureId + vesselFeatureId: Vessel.VesselFeatureId }> ) { const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) @@ -225,7 +213,7 @@ const vesselSlice = createSlice({ } reportingsWithAlert = reportingsWithAlert.concat([ReportingType.ALERT]) state.selectedVessel = { - ...(state.selectedVessel as VesselTypes.AugmentedSelectedVessel), + ...(state.selectedVessel as Vessel.AugmentedSelectedVessel), alerts: filteredAlerts, hasAlert: !!filteredAlerts?.length, hasInfractionSuspicion: reportingsWithAlert.some(reportingType => @@ -243,7 +231,7 @@ const vesselSlice = createSlice({ state, action: PayloadAction<{ reportingType: string - vesselFeatureId: VesselFeatureId + vesselFeatureId: Vessel.VesselFeatureId }> ) { const vessel = vesselSelectors.selectById(state.vessels, action.payload.vesselFeatureId) @@ -264,7 +252,7 @@ const vesselSlice = createSlice({ if ( state.selectedVessel && - Vessel.getVesselFeatureId(state.selectedVesselIdentity) === action.payload.vesselFeatureId + VesselFeature.getVesselFeatureId(state.selectedVesselIdentity) === action.payload.vesselFeatureId ) { const vesselReportingWithoutFirstFoundReportingType = state.selectedVessel.reportings?.reduce(filterFirstFoundReportingType(action.payload.reportingType), []) || [] @@ -323,7 +311,7 @@ const vesselSlice = createSlice({ return } - const selectedVesselFeatureId = Vessel.getVesselFeatureId(state.selectedVesselIdentity) + const selectedVesselFeatureId = VesselFeature.getVesselFeatureId(state.selectedVesselIdentity) if ( state.selectedVessel && vesselsFeatureIds.find(vesselFeatureId => selectedVesselFeatureId === vesselFeatureId) @@ -337,7 +325,7 @@ const vesselSlice = createSlice({ ) state.selectedVessel = { - ...(state.selectedVessel as VesselTypes.AugmentedSelectedVessel), + ...(state.selectedVessel as Vessel.AugmentedSelectedVessel), hasInfractionSuspicion: vesselReportingWithoutFirstFoundReportingTypes.some(reportingType => reportingIsAnInfractionSuspicion(reportingType) ), @@ -345,12 +333,6 @@ const vesselSlice = createSlice({ } } }, - /** - * Reset the highlighted vessel position - */ - resetHighlightedVesselTrackPosition(state) { - state.highlightedVesselTrackPosition = null - }, resetLoadingVessel(state) { state.loadingVessel = false @@ -384,7 +366,7 @@ const vesselSlice = createSlice({ ) }, - setFilteredVesselsFeatures(state, action: PayloadAction) { + setFilteredVesselsFeatures(state, action: PayloadAction) { const filteredVesselsFeaturesUids = action.payload const vesselIds = state.vessels.ids @@ -436,14 +418,14 @@ const vesselSlice = createSlice({ setSelectedVessel( state, action: PayloadAction<{ - positions: VesselPosition[] - vessel: VesselTypes.SelectedVessel + positions: Vessel.VesselPosition[] + vessel: Vessel.SelectedVessel }> ) { state.loadingVessel = null state.loadingPositions = null state.selectedVessel = action.payload.vessel - state.selectedVesselIdentity = getOnlyVesselIdentityProperties(action.payload.vessel) + state.selectedVesselIdentity = extractVesselIdentityProps(action.payload.vessel) state.selectedVesselPositions = action.payload.positions }, @@ -463,7 +445,7 @@ const vesselSlice = createSlice({ state.selectedVesselSidebarTab = action.payload }, - setVessels(state, action: PayloadAction) { + setVessels(state, action: PayloadAction) { if (!action.payload || !Array.isArray(action.payload)) { return } @@ -490,7 +472,7 @@ const vesselSlice = createSlice({ /** * Update the positions of the vessel */ - updateSelectedVesselPositions(state, action: PayloadAction) { + updateSelectedVesselPositions(state, action: PayloadAction) { state.loadingPositions = null state.selectedVesselPositions = action.payload }, @@ -593,7 +575,6 @@ export const { removeVesselAlertAndUpdateReporting, removeVesselReporting, removeVesselReportings, - resetHighlightedVesselTrackPosition, resetLoadingVessel, resetSelectedVessel, setAllVesselsAsUnfiltered, diff --git a/frontend/src/features/Vessel/useCases/previewVessels.ts b/frontend/src/features/Vessel/useCases/previewVessels.ts index 7279b0041b..7e983d28b2 100644 --- a/frontend/src/features/Vessel/useCases/previewVessels.ts +++ b/frontend/src/features/Vessel/useCases/previewVessels.ts @@ -8,11 +8,11 @@ import { getExtentFromGeoJSON } from '../../../utils' import { animateToExtent } from '../../Map/slice' import { setPreviewFilteredVesselsFeatures } from '../slice' -import type { VesselEnhancedLastPositionWebGLObject } from '../../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { MainAppThunk } from '@store' export const previewVessels = - (filteredVessels: VesselEnhancedLastPositionWebGLObject[]): MainAppThunk => + (filteredVessels: Vessel.VesselEnhancedLastPositionWebGLObject[]): MainAppThunk => async (dispatch, getState) => { const { zonesSelected } = getState().vesselList const vesselFeatureIds = filteredVessels.map(vessel => vessel.vesselFeatureId) diff --git a/frontend/src/features/Vessel/useCases/showAlertInSideWindow.ts b/frontend/src/features/Vessel/useCases/showAlertInSideWindow.ts index 543bc25e18..f4e85864df 100644 --- a/frontend/src/features/Vessel/useCases/showAlertInSideWindow.ts +++ b/frontend/src/features/Vessel/useCases/showAlertInSideWindow.ts @@ -11,11 +11,18 @@ export const showAlertInSideWindow = dispatch(openSideWindowPath({ menu: SideWindowMenuKey.ALERT_LIST_AND_REPORTING_LIST })) dispatch( focusOnAlert({ + beaconNumber: selectedVessel.beaconNumber, + districtCode: selectedVessel.districtCode, externalReferenceNumber: selectedVessel.externalReferenceNumber, flagState: selectedVessel.flagState, internalReferenceNumber: selectedVessel.internalReferenceNumber, ircs: selectedVessel.ircs, - name: selectedVessel.alerts?.length ? selectedVessel?.alerts[0] : null + mmsi: selectedVessel.mmsi, + name: selectedVessel.alerts?.length ? selectedVessel?.alerts[0] : undefined, + vesselId: selectedVessel.vesselId, + vesselIdentifier: selectedVessel.vesselIdentifier, + vesselLength: selectedVessel.length, + vesselName: selectedVessel.vesselName }) ) } diff --git a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts index 4c1b06cf77..e32e36e6e7 100644 --- a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts +++ b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts @@ -2,20 +2,20 @@ import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '@features/Map/constants import { reportingIsAnInfractionSuspicion } from '@features/Reporting/utils' import { VESSELS_VECTOR_LAYER } from '@features/Vessel/layers/VesselsLayer/constants' import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' +import { Vessel } from '@features/Vessel/Vessel.types' import { transform } from 'ol/proj' import { applyFilterToVessels } from './applyFilterToVessels' -import { Vessel } from '../../../domain/entities/vessel/vessel' +import { VesselFeature } from '../../../domain/entities/vessel/vessel' import { resetIsUpdatingVessels } from '../../../domain/shared_slices/Global' import { getUniqueSpeciesAndDistricts } from '../../../domain/use_cases/species/getUniqueSpeciesAndDistricts' import { customHexToRGB } from '../../../utils' import { setVessels, setVesselsSpeciesAndDistricts } from '../slice' -import type { VesselEnhancedLastPositionWebGLObject, VesselLastPosition } from '../../../domain/entities/vessel/types' import type { MainAppThunk } from '@store' export const showVesselsLastPosition = - (vessels: VesselLastPosition[]): MainAppThunk => + (vessels: Vessel.VesselLastPosition[]): MainAppThunk => async (dispatch, getState) => { const showedFilter = getState().filter?.filters?.find(filter => filter.showed) @@ -45,7 +45,9 @@ export const showVesselsLastPosition = dispatch(resetIsUpdatingVessels()) } -function convertToEnhancedLastPositions(vessels: VesselLastPosition[]): VesselEnhancedLastPositionWebGLObject[] { +function convertToEnhancedLastPositions( + vessels: Vessel.VesselLastPosition[] +): Vessel.VesselEnhancedLastPositionWebGLObject[] { return vessels.map(vessel => ({ ...vessel, coordinates: transform([vessel.longitude, vessel.latitude], WSG84_PROJECTION, OPENLAYERS_PROJECTION), @@ -66,6 +68,6 @@ function convertToEnhancedLastPositions(vessels: VesselLastPosition[]): VesselEn ? Array.from(new Set(vessel.speciesOnboard.map(species => species.species))) : [], speed: vessel.speed, - vesselFeatureId: Vessel.getVesselFeatureId(vessel) + vesselFeatureId: VesselFeature.getVesselFeatureId(vessel) })) } diff --git a/frontend/src/features/Vessel/utils.ts b/frontend/src/features/Vessel/utils.ts index 651ed25c17..4c099c8dc9 100644 --- a/frontend/src/features/Vessel/utils.ts +++ b/frontend/src/features/Vessel/utils.ts @@ -1,17 +1,12 @@ import Feature from 'ol/Feature' import Point from 'ol/geom/Point' -import { - VesselIdentifier, - type VesselEnhancedLastPositionWebGLObject, - type VesselIdentity, - type VesselLastPositionFeature -} from '../../domain/entities/vessel/types' +import { Vessel } from './Vessel.types' -import type { Vessel } from './Vessel.types' +import type { PendingAlert, SilencedAlert } from '@features/Alert/types' import type { Reporting } from '@features/Reporting/types' -export function buildFeature(vessel: VesselEnhancedLastPositionWebGLObject): VesselLastPositionFeature { +export function buildFeature(vessel: Vessel.VesselEnhancedLastPositionWebGLObject): Vessel.VesselLastPositionFeature { /** * The feature does contain ONLY required properties, it does not contain all properties of VesselLastPosition. */ @@ -52,14 +47,21 @@ export function buildFeature(vessel: VesselEnhancedLastPositionWebGLObject): Ves vesselIdentifier: vessel.vesselIdentifier, vesselName: vessel.vesselName, width: vessel.width - }) as VesselLastPositionFeature + }) as Vessel.VesselLastPositionFeature feature.setId(vessel.vesselFeatureId) return feature } export const extractVesselIdentityProps = ( - vessel: Vessel.VesselEnhancedObject | Vessel.SelectedVessel | Vessel.EnrichedVessel | Reporting.Reporting + vessel: + | Vessel.VesselEnhancedObject + | Vessel.SelectedVessel + | Vessel.EnrichedVessel + | Reporting.Reporting + | PendingAlert + | SilencedAlert + | Vessel.VesselIdentity ): Vessel.VesselIdentity => ({ beaconNumber: 'beaconNumber' in vessel && !!vessel.beaconNumber ? vessel.beaconNumber : undefined, districtCode: 'districtCode' in vessel && !!vessel.districtCode ? vessel.districtCode : undefined, @@ -75,12 +77,12 @@ export const extractVesselIdentityProps = ( }) // Type to enforce strong typing: properties specified in `K` will be required, others will remain optional -type VesselProperties = Required< - Pick +type VesselProperties = Required< + Pick > & - Partial> -export function extractVesselPropertiesFromFeature( - feature: VesselLastPositionFeature, + Partial> +export function extractVesselPropertiesFromFeature( + feature: Vessel.VesselLastPositionFeature, requiredProperties: K[] ): VesselProperties { const vesselProperties: any = {} @@ -102,7 +104,9 @@ export function getVesselIdentityPropsAsEmptyStringsWhenUndefined(vesselIdentity } } -export function getVesselIdentityFromLegacyVesselIdentity(legacyVesselIdentity: VesselIdentity): Vessel.VesselIdentity { +export function getVesselIdentityFromLegacyVesselIdentity( + legacyVesselIdentity: Vessel.VesselIdentity +): Vessel.VesselIdentity { return { beaconNumber: legacyVesselIdentity.beaconNumber ?? undefined, districtCode: legacyVesselIdentity.districtCode ?? undefined, @@ -118,44 +122,7 @@ export function getVesselIdentityFromLegacyVesselIdentity(legacyVesselIdentity: } } -export function getVesselIdentityFromVessel(vessel: Vessel.Vessel): Vessel.VesselIdentity { - const vesselIdentifier = getVesselIdentifier(vessel) - - return { - beaconNumber: undefined, - districtCode: vessel.districtCode, - externalReferenceNumber: vessel.externalReferenceNumber, - flagState: vessel.flagState, - internalReferenceNumber: vessel.internalReferenceNumber, - ircs: vessel.ircs, - mmsi: vessel.mmsi, - vesselId: vessel.vesselId, - vesselIdentifier, - vesselLength: vessel.length, - vesselName: vessel.vesselName - } -} - -export function getVesselIdentifier({ - externalReferenceNumber, - internalReferenceNumber, - ircs -}: { - externalReferenceNumber: string | undefined - internalReferenceNumber: string | undefined - ircs: string | undefined -}): VesselIdentifier | undefined { - switch (true) { - case !!internalReferenceNumber: - return VesselIdentifier.INTERNAL_REFERENCE_NUMBER - - case !!externalReferenceNumber: - return VesselIdentifier.EXTERNAL_REFERENCE_NUMBER - - case !!ircs: - return VesselIdentifier.IRCS - - default: - return undefined - } -} +export const getVesselCompositeIdentifier: (vessel) => Vessel.VesselCompositeIdentifier = vessel => + `${vessel.internalReferenceNumber ?? 'UNKNOWN'}/${vessel.ircs ?? 'UNKNOWN'}/${ + vessel.externalReferenceNumber ?? 'UNKNOWN' + }` diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 370a6c0e0a..7daac6c1fc 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -1,3 +1,4 @@ +import { monitorfishApi } from '@api/api' import { RtkCacheTagType } from '@api/constants' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { FrontendApiError } from '@libs/FrontendApiError' @@ -6,12 +7,9 @@ import { displayedErrorActions } from 'domain/shared_slices/DisplayedError' import { displayOrLogError } from 'domain/use_cases/error/displayOrLogError' import { getVesselIdentityPropsAsEmptyStringsWhenUndefined } from './utils' -import { monitorfishApi } from '../../api/api' +import { Vessel } from './Vessel.types' -import type { Vessel } from './Vessel.types' import type { VesselReportings } from '@features/Reporting/types' -import type { RiskFactor } from 'domain/entities/vessel/riskFactor/types' -import type { VesselLastPosition } from 'domain/entities/vessel/types' const GET_VESSEL_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations de ce navire." const GET_VESSEL_REPORTINGS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les signalements de ce navire." @@ -19,11 +17,6 @@ const SEARCH_VESSELS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navire export const vesselApi = monitorfishApi.injectEndpoints({ endpoints: builder => ({ - getRiskFactor: builder.query({ - providesTags: () => [{ type: 'RiskFactor' }], - query: internalReferenceNumber => `/vessels/risk_factor?internalReferenceNumber=${internalReferenceNumber}` - }), - getVessel: builder.query({ providesTags: () => [{ type: RtkCacheTagType.Vessel }], query: id => `/vessels/${id}`, @@ -62,8 +55,10 @@ export const vesselApi = monitorfishApi.injectEndpoints({ transformErrorResponse: response => new FrontendApiError(GET_VESSEL_REPORTINGS_ERROR_MESSAGE, response) }), - getVesselsLastPositions: builder.query({ - query: () => `/vessels` + getVesselsLastPositions: builder.query({ + query: () => `/vessels`, + transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => + baseQueryReturnValue.map(LastPosition => Vessel.VesselLastPositionSchema.parse(LastPosition)) }), searchVessels: builder.query({ diff --git a/frontend/src/features/Vessel/vesselNavApi.ts b/frontend/src/features/Vessel/vesselNavApi.ts index c4efd923d7..2029e34d07 100644 --- a/frontend/src/features/Vessel/vesselNavApi.ts +++ b/frontend/src/features/Vessel/vesselNavApi.ts @@ -1,11 +1,12 @@ -import { monitorfishLightApi } from '../../api/api' - -import type { VesselLastPosition } from '../../domain/entities/vessel/types' +import { monitorfishLightApi } from '@api/api' +import { Vessel } from '@features/Vessel/Vessel.types' export const vesselNavApi = monitorfishLightApi.injectEndpoints({ endpoints: builder => ({ - getVesselsLastPositions: builder.query({ - query: () => `/v1/vessels` + getVesselsLastPositions: builder.query({ + query: () => `/v1/vessels`, + transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => + baseQueryReturnValue.map(LastPosition => Vessel.VesselLastPositionSchema.parse(LastPosition)) }) }) }) diff --git a/frontend/src/features/VesselSearch/VesselSearchResult.tsx b/frontend/src/features/VesselSearch/VesselSearchResult.tsx index da05ecba0b..3b8fc5187e 100644 --- a/frontend/src/features/VesselSearch/VesselSearchResult.tsx +++ b/frontend/src/features/VesselSearch/VesselSearchResult.tsx @@ -1,9 +1,9 @@ +import { getVesselCompositeIdentifier } from '@features/Vessel/utils' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { useMemo } from 'react' import styled from 'styled-components' import { VesselSearchResultItem } from './VesselSearchResultItem' -import { getVesselCompositeIdentifier } from '../../domain/entities/vessel/vessel' export function VesselSearchResult({ foundVessels, searchQuery, selectVessel, showLastSearchedVessels }) { const lastSearchedVessels = useMainAppSelector(state => state.global.lastSearchedVessels) diff --git a/frontend/src/features/VesselSearch/__tests__/mocks/index.ts b/frontend/src/features/VesselSearch/__tests__/mocks/index.ts index 7ffbe44b82..511e807c39 100644 --- a/frontend/src/features/VesselSearch/__tests__/mocks/index.ts +++ b/frontend/src/features/VesselSearch/__tests__/mocks/index.ts @@ -1,609 +1,787 @@ -import { VesselIdentifier } from '../../../../domain/entities/vessel/types' +import { Vessel } from '@features/Vessel/Vessel.types' -import type { VesselIdentity } from '../../../../domain/entities/vessel/types' - -export const dummyVessels: VesselIdentity[] = [ +export const dummyVessels: Vessel.VesselIdentity[] = [ { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF115457', flagState: 'FR', internalReferenceNumber: 'ABC000879296', ircs: 'WF3003', mmsi: '556780654', vesselId: 963, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'EN SOUFFRANCE JUSTICE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'RG598087', flagState: 'FR', internalReferenceNumber: 'ABC000590385', ircs: 'FFF1687', mmsi: '087615227', vesselId: 852, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'IDÉE POSSIBLE GUÈRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF016759', flagState: 'FR', internalReferenceNumber: 'ABC000524216', ircs: 'WTMZ', mmsi: '863050989', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'FOI COMMANDER DE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF226159', flagState: 'FR', internalReferenceNumber: 'ABC000181072', ircs: 'CZ9746', mmsi: '526719212', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SILENCE MOYEN ENTRETENIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NE989151', flagState: 'FR', internalReferenceNumber: 'ABC000141901', ircs: 'KJX1408', mmsi: '711778932', vesselId: 741, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'OFFICIER REMARQUER DIMANCHE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NU904004', flagState: 'FR', internalReferenceNumber: 'ABC000445305', - ircs: null, - mmsi: null, - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + ircs: undefined, + mmsi: undefined, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'AFFAIRE NÉCESSAIRE DÉCIDER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'OX277713', flagState: 'FR', internalReferenceNumber: 'ABC000776983', ircs: 'LMV2444', mmsi: '273738571', vesselId: 789, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'OFFICIER QUALITÉ INQUIÉTER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'HF332930', flagState: 'FR', internalReferenceNumber: 'ABC000311287', ircs: 'TGY9721', mmsi: '290440715', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SUFFIRE ORDRE AINSI' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'KJ824380', flagState: 'FR', internalReferenceNumber: 'ABC000118270', ircs: 'WQWP', mmsi: '450576026', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DIFFICILE PLANTE FLAMME' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JE084345', flagState: 'FR', internalReferenceNumber: 'ABC000884668', ircs: 'TLCK', mmsi: '170953206', vesselId: 456, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CHIFFRE PRÉSIDENT ARMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'ZW031251', flagState: 'FR', internalReferenceNumber: 'ABC000848435', ircs: 'NQPD', mmsi: '122626260', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SOUFFRANCE ATTACHER VIE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JZ103068', flagState: 'FR', internalReferenceNumber: 'ABC000984112', ircs: 'QYB7543', mmsi: '013996100', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'BAS ÉTOUFFER DAVANTAGE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'MZ884865', flagState: 'FR', internalReferenceNumber: 'ABC000308039', ircs: 'NZO0385', mmsi: '860689344', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ÉCLAT SOUFFLER PRESSER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JM712206', flagState: 'FR', internalReferenceNumber: 'ABC000166726', ircs: 'HE7480', mmsi: '525379682', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DERNIER SUFFIRE PARFAITEMENT' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'AB992315', flagState: 'FR', internalReferenceNumber: 'ABC000394671', ircs: 'DH3450', mmsi: '667035940', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ANCIEN SOUFFLER HABILLER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'PA446960', flagState: 'FR', internalReferenceNumber: 'ABC000149343', ircs: 'WRP7197', - mmsi: null, - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + mmsi: undefined, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DANGER FEU SUFFIRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'EA455411', flagState: 'FR', internalReferenceNumber: 'ABC000140641', ircs: 'JDUO', mmsi: '274259742', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CONSCIENCE CHIFFRE REGARD' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'ZR522262', flagState: 'FR', internalReferenceNumber: 'ABC000035772', ircs: 'NC1001', mmsi: '029546102', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ROND FOLIE SOUFFRANCE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FJ373455', flagState: 'FR', internalReferenceNumber: 'ABC000540522', ircs: 'CEU6005', mmsi: '881553203', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'FERMER FAVEUR OFFRIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JR478558', flagState: 'FR', internalReferenceNumber: 'ABC000765593', ircs: 'WBHK', mmsi: '101230463', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'COU DERNIER SOUFFRANCE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'UY390404', flagState: 'FR', internalReferenceNumber: 'ABC000072345', ircs: 'XC6966', mmsi: '090689089', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'PROFITER IDÉE AFFIRMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'YY649269', flagState: 'FR', internalReferenceNumber: 'ABC000979712', ircs: 'FNQ7400', mmsi: '169370366', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SERRER PEINE DIFFÉRENT' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'LG001137', flagState: 'FR', internalReferenceNumber: 'ABC000828264', ircs: 'NTYW', mmsi: '141147149', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'COURT OCCUPER AFFIRMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'KY031972', flagState: 'FR', internalReferenceNumber: 'ABC000455406', ircs: 'YHT3571', mmsi: '059995227', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'AUSSI TENIR SOUFFRIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'WU651219', flagState: 'FR', internalReferenceNumber: 'ABC000180200', ircs: 'IIHU', mmsi: '905961726', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CHEF MEMBRE ÉTOUFFER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'HT165892', flagState: 'FR', internalReferenceNumber: 'ABC000369225', ircs: 'YLPJ', mmsi: '904787795', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ENTRER ESPÈCE DIFFICILE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'CM401753', flagState: 'FR', internalReferenceNumber: 'ABC000418234', ircs: 'CJ2803', mmsi: '456215392', vesselId: 123, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'VIVRE INVENTER OFFICIER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'AX959623', flagState: 'FR', internalReferenceNumber: 'ABC000300531', ircs: 'NHH9883', mmsi: '191556941', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'PROUVER HIVER SOUFFLER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NK758789', flagState: 'FR', internalReferenceNumber: 'ABC000689105', ircs: 'GLJ6104', mmsi: '136720331', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'EXPLIQUER PLEURER AFFAIRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'WG177222', flagState: 'FR', internalReferenceNumber: 'ABC000603340', ircs: 'EVB8126', mmsi: '771294761', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ENGAGER TROUBLER SOUFFLER' } ] -export const anotherDummyVessels: VesselIdentity[] = [ +export const anotherDummyVessels: Vessel.VesselIdentity[] = [ { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF115457', flagState: 'FR', internalReferenceNumber: 'DEF000879296', ircs: 'WF3003', mmsi: '556780654', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'EN SOUFFRANCE JUSTICE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'RG598087', flagState: 'FR', internalReferenceNumber: 'DEF000590385', ircs: 'FFF1687', mmsi: '087615227', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'IDÉE POSSIBLE GUÈRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF016759', flagState: 'FR', internalReferenceNumber: 'DEF000524216', ircs: 'WTMZ', mmsi: '863050989', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'FOI COMMANDER DE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FF226159', flagState: 'FR', internalReferenceNumber: 'DEF000181072', ircs: 'CZ9746', mmsi: '526719212', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SILENCE MOYEN ENTRETENIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NE989151', flagState: 'FR', internalReferenceNumber: 'DEF000141901', ircs: 'KJX1408', mmsi: '711778932', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'OFFICIER REMARQUER DIMANCHE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NU904004', flagState: 'FR', internalReferenceNumber: 'DEF000445305', - ircs: null, - mmsi: null, - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + ircs: undefined, + mmsi: undefined, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'AFFAIRE NÉCESSAIRE DÉCIDER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'OX277713', flagState: 'FR', internalReferenceNumber: 'DEF000776983', ircs: 'LMV2444', mmsi: '273738571', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'OFFICIER QUALITÉ INQUIÉTER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'HF332930', flagState: 'FR', internalReferenceNumber: 'DEF000311287', ircs: 'TGY9721', mmsi: '290440715', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SUFFIRE ORDRE AINSI' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'KJ824380', flagState: 'FR', internalReferenceNumber: 'DEF000118270', ircs: 'WQWP', mmsi: '450576026', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DIFFICILE PLANTE FLAMME' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JE084345', flagState: 'FR', internalReferenceNumber: 'DEF000884668', ircs: 'TLCK', mmsi: '170953206', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CHIFFRE PRÉSIDENT ARMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'ZW031251', flagState: 'FR', internalReferenceNumber: 'DEF000848435', ircs: 'NQPD', mmsi: '122626260', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SOUFFRANCE ATTACHER VIE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JZ103068', flagState: 'FR', internalReferenceNumber: 'DEF000984112', ircs: 'QYB7543', mmsi: '013996100', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'BAS ÉTOUFFER DAVANTAGE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'MZ884865', flagState: 'FR', internalReferenceNumber: 'DEF000308039', ircs: 'NZO0385', mmsi: '860689344', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ÉCLAT SOUFFLER PRESSER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JM712206', flagState: 'FR', internalReferenceNumber: 'DEF000166726', ircs: 'HE7480', mmsi: '525379682', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DERNIER SUFFIRE PARFAITEMENT' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'AB992315', flagState: 'FR', internalReferenceNumber: 'DEF000394671', ircs: 'DH3450', mmsi: '667035940', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ANCIEN SOUFFLER HABILLER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'PA446960', flagState: 'FR', internalReferenceNumber: 'DEF000149343', ircs: 'WRP7197', - mmsi: null, - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + mmsi: undefined, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'DANGER FEU SUFFIRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'EA455411', flagState: 'FR', internalReferenceNumber: 'DEF000140641', ircs: 'JDUO', mmsi: '274259742', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CONSCIENCE CHIFFRE REGARD' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'ZR522262', flagState: 'FR', internalReferenceNumber: 'DEF000035772', ircs: 'NC1001', mmsi: '029546102', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ROND FOLIE SOUFFRANCE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'FJ373455', flagState: 'FR', internalReferenceNumber: 'DEF000540522', ircs: 'CEU6005', mmsi: '881553203', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'FERMER FAVEUR OFFRIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'JR478558', flagState: 'FR', internalReferenceNumber: 'DEF000765593', ircs: 'WBHK', mmsi: '101230463', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'COU DERNIER SOUFFRANCE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'UY390404', flagState: 'FR', internalReferenceNumber: 'DEF000072345', ircs: 'XC6966', mmsi: '090689089', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'PROFITER IDÉE AFFIRMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'YY649269', flagState: 'FR', internalReferenceNumber: 'DEF000979712', ircs: 'FNQ7400', mmsi: '169370366', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'SERRER PEINE DIFFÉRENT' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'LG001137', flagState: 'FR', internalReferenceNumber: 'DEF000828264', ircs: 'NTYW', mmsi: '141147149', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'COURT OCCUPER AFFIRMER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'KY031972', flagState: 'FR', internalReferenceNumber: 'DEF000455406', ircs: 'YHT3571', mmsi: '059995227', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'AUSSI TENIR SOUFFRIR' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'WU651219', flagState: 'FR', internalReferenceNumber: 'DEF000180200', ircs: 'IIHU', mmsi: '905961726', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'CHEF MEMBRE ÉTOUFFER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'HT165892', flagState: 'FR', internalReferenceNumber: 'DEF000369225', ircs: 'YLPJ', mmsi: '904787795', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ENTRER ESPÈCE DIFFICILE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'CM401753', flagState: 'FR', internalReferenceNumber: 'DEF000418234', ircs: 'CJ2803', mmsi: '456215392', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'VIVRE INVENTER OFFICIER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'AX959623', flagState: 'FR', internalReferenceNumber: 'DEF000300531', ircs: 'NHH9883', mmsi: '191556941', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'PROUVER HIVER SOUFFLER' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'NK758789', flagState: 'FR', internalReferenceNumber: 'DEF000689105', ircs: 'GLJ6104', mmsi: '136720331', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'EXPLIQUER PLEURER AFFAIRE' }, { + beaconNumber: undefined, + districtCode: undefined, externalReferenceNumber: 'WG177222', flagState: 'FR', internalReferenceNumber: 'DEF000603340', ircs: 'EVB8126', mmsi: '771294761', - vesselId: null, - vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselId: undefined, + vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER, + vesselLength: undefined, vesselName: 'ENGAGER TROUBLER SOUFFLER' } ] diff --git a/frontend/src/features/VesselSearch/constants.ts b/frontend/src/features/VesselSearch/constants.ts index 925d96bd45..3760e549ae 100644 --- a/frontend/src/features/VesselSearch/constants.ts +++ b/frontend/src/features/VesselSearch/constants.ts @@ -1,7 +1,7 @@ -import type { VesselEnhancedLastPositionWebGLObject } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { IFuseOptions } from 'fuse.js' -export const VESSEL_SEARCH_OPTIONS: IFuseOptions = { +export const VESSEL_SEARCH_OPTIONS: IFuseOptions = { distance: 50, // eslint-disable-next-line @typescript-eslint/naming-convention includeScore: true, diff --git a/frontend/src/features/VesselSearch/index.tsx b/frontend/src/features/VesselSearch/index.tsx index 19c228bf94..fe1ab895e9 100644 --- a/frontend/src/features/VesselSearch/index.tsx +++ b/frontend/src/features/VesselSearch/index.tsx @@ -1,3 +1,4 @@ +import { extractVesselIdentityProps } from '@features/Vessel/utils' import { useClickOutsideWhenOpenedWithinRef } from '@hooks/useClickOutsideWhenOpenedWithinRef' import { useEscapeFromKeyboard } from '@hooks/useEscapeFromKeyboard' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' @@ -11,25 +12,24 @@ import styled from 'styled-components' import { VESSEL_SEARCH_OPTIONS } from './constants' import { enrichWithVesselIdentifierIfNotFound, removeDuplicatedFoundVessels } from './utils' import { VesselSearchResult } from './VesselSearchResult' -import { getOnlyVesselIdentityProperties } from '../../domain/entities/vessel/vessel' import { searchVessels as searchVesselsAction } from '../../domain/use_cases/vessel/searchVessels' import { showVessel } from '../../domain/use_cases/vessel/showVessel' import { vesselSelectors } from '../Vessel/slice' -import type { VesselIdentity } from '../../domain/entities/vessel/types' +import type { Vessel } from '@features/Vessel/Vessel.types' import type { ChangeEvent, InputHTMLAttributes, MutableRefObject } from 'react' import type { Promisable } from 'type-fest' type VesselSearchProps = Omit, 'defaultValue' | 'onChange'> & { baseRef?: MutableRefObject | undefined - defaultValue?: VesselIdentity | undefined + defaultValue?: Vessel.VesselIdentity | undefined extendedWidth?: number | undefined hasError?: boolean | undefined isExtended?: boolean | undefined isLastSearchedVesselsShowed?: boolean isLinkToVesselSidebarDisplayed?: boolean isVesselIdRequiredFromResults?: boolean - onChange: (selectedVessel: VesselIdentity | undefined) => Promisable + onChange: (selectedVessel: Vessel.VesselIdentity | undefined) => Promisable onClickOutsideOrEscape?: () => Promisable onInputClick?: () => Promisable } @@ -57,8 +57,8 @@ export function VesselSearch({ const searchQueryRef = useRef('') const wrapperRef = useRef(null) - const [selectedVessel, setSelectedVessel] = useState(undefined) - const [foundVessels, setFoundVessels] = useState([]) + const [selectedVessel, setSelectedVessel] = useState(undefined) + const [foundVessels, setFoundVessels] = useState([]) const [showLastSearchedVessels, setShowLastSearchedVessels] = useState(false) const escapeFromKeyboard = useEscapeFromKeyboard() @@ -110,7 +110,7 @@ export function VesselSearch({ const findVessels = useCallback( async (searchQuery: string) => { - const vesselsFromMap = fuse.search(searchQuery).map(result => getOnlyVesselIdentityProperties(result.item)) + const vesselsFromMap = fuse.search(searchQuery).map(result => extractVesselIdentityProps(result.item)) const nextFoundVesselsFromAPI = await dispatch(searchVesselsAction(searchQuery.toUpperCase())) if (!nextFoundVesselsFromAPI) { diff --git a/frontend/src/features/VesselSearch/utils.ts b/frontend/src/features/VesselSearch/utils.ts index c1bed2379b..c20226677c 100644 --- a/frontend/src/features/VesselSearch/utils.ts +++ b/frontend/src/features/VesselSearch/utils.ts @@ -1,7 +1,5 @@ -import { VesselIdentifier } from '../../domain/entities/vessel/types' - -import type { VesselIdentity } from '../../domain/entities/vessel/types' -import type { Vessel } from '@features/Vessel/Vessel.types' +import { extractVesselIdentityProps } from '@features/Vessel/utils' +import { Vessel } from '@features/Vessel/Vessel.types' /** * Remove duplicated vessels : keep vessels from APIs when a duplicate is found on either @@ -9,9 +7,9 @@ import type { Vessel } from '@features/Vessel/Vessel.types' * - vesselId (Vessel internal identifier) */ export function removeDuplicatedFoundVessels( - foundVesselsFromAPI: VesselIdentity[], - foundVesselsOnMap: VesselIdentity[] -): VesselIdentity[] { + foundVesselsFromAPI: Vessel.VesselIdentity[], + foundVesselsOnMap: Vessel.VesselIdentity[] +): Vessel.VesselIdentity[] { const filteredVesselsFromMap = foundVesselsOnMap.filter(vesselFromMap => { if (!vesselFromMap.internalReferenceNumber) { return true @@ -28,23 +26,25 @@ export function removeDuplicatedFoundVessels( } export function enrichWithVesselIdentifierIfNotFound( - identity: Vessel.VesselEnhancedObject | VesselIdentity -): VesselIdentity { - if (identity.vesselIdentifier) { - return identity + identityOrVessel: Vessel.VesselEnhancedObject | Vessel.VesselIdentity +): Vessel.VesselIdentity { + const vesselIdentity = extractVesselIdentityProps(identityOrVessel) + + if (vesselIdentity.vesselIdentifier) { + return vesselIdentity } - if (identity.internalReferenceNumber) { - return { ...identity, vesselIdentifier: VesselIdentifier.INTERNAL_REFERENCE_NUMBER } + if (vesselIdentity.internalReferenceNumber) { + return { ...vesselIdentity, vesselIdentifier: Vessel.VesselIdentifier.INTERNAL_REFERENCE_NUMBER } } - if (identity.ircs) { - return { ...identity, vesselIdentifier: VesselIdentifier.IRCS } + if (vesselIdentity.ircs) { + return { ...vesselIdentity, vesselIdentifier: Vessel.VesselIdentifier.IRCS } } - if (identity.externalReferenceNumber) { - return { ...identity, vesselIdentifier: VesselIdentifier.EXTERNAL_REFERENCE_NUMBER } + if (vesselIdentity.externalReferenceNumber) { + return { ...vesselIdentity, vesselIdentifier: Vessel.VesselIdentifier.EXTERNAL_REFERENCE_NUMBER } } - return identity + return vesselIdentity } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index edb6d2f25b..3cbd5d2b31 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1,3 +1,5 @@ +import { z } from 'zod' + import type { Native, OptionValueType, TreeBranchOption, TreeLeafOption } from '@mtes-mct/monitor-ui' import type { ConditionalKeys, Exact } from 'type-fest' @@ -9,8 +11,6 @@ export type CollectionItem = { id: number | string } -export type FormikFormError = Record | undefined - export type MenuItem = { code: T name: string @@ -32,13 +32,6 @@ export type PartialExcept, RequiredKeys extends ke > & Pick -export type PickStringKeys> = Exact< - { - [Key in string]: T[Key] - }, - T -> - export type PickStringKeysWithNativeValues> = Exact< { [Key in string & ConditionalKeys]: T[Key] @@ -46,4 +39,6 @@ export type PickStringKeysWithNativeValues> = Exact< T > -export type StringKeyRecord = PickStringKeys> +export const stringOrUndefined = z.union([z.string(), z.undefined()]) +export const numberOrUndefined = z.union([z.number(), z.undefined()]) +export const booleanOrUndefined = z.union([z.boolean(), z.undefined()]) diff --git a/frontend/src/utils/undefinedize.ts b/frontend/src/utils/undefinedize.ts index d693983024..42b8a6fb8d 100644 --- a/frontend/src/utils/undefinedize.ts +++ b/frontend/src/utils/undefinedize.ts @@ -34,8 +34,6 @@ const undefinedizeObjectPropPair = ([key, value]: [string, NativeAny]) => [key, * The value must be of native type and only contains native types. */ export function undefinedize(value: T): Undefinedized | undefined { - // console.debug(value) - if (value === null || value === undefined) { return undefined } From 1934de6f2ea224987623b5a22e97c8cfbba36029 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Fri, 31 Jan 2025 11:02:58 +0100 Subject: [PATCH 02/21] Fix typings --- .../entities/risk_factor/VesselRiskFactor.kt | 14 +++++----- .../use_cases/vessel/GetVesselRiskFactor.kt | 10 ++++--- .../api/outputs/LastPositionDataOutput.kt | 17 ++++++------ .../api/outputs/RiskFactorDataOutput.kt | 26 +++++++++---------- .../database/entities/LastPositionEntity.kt | 4 +-- .../database/entities/PositionEntity.kt | 8 +++--- .../SilencedAlerts/fields/VesselField.tsx | 26 +++++++++++-------- .../ActionForm/shared/VesselField.tsx | 16 +++++++----- .../useCases/updateActionGearsOnboard.ts | 3 ++- frontend/src/features/RiskFactor/types.ts | 19 +++++++------- frontend/src/features/Vessel/Vessel.types.ts | 18 ++++++------- .../useCases/showVesselsLastPosition.ts | 4 ++- 12 files changed, 88 insertions(+), 77 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt index e54105f249..f0082003cc 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt @@ -37,16 +37,16 @@ data class VesselRiskFactor( val gearOnboard: List? = listOf(), val speciesOnboard: List? = listOf(), val totalWeightOnboard: Double? = null, - val segments: List? = listOf(), + val segments: List, val probableSegments: List? = listOf(), val segmentHighestImpact: String? = null, val segmentHighestPriority: String? = null, val lastControlDatetime: ZonedDateTime? = null, val postControlComments: String? = null, - val numberControlsLastFiveYears: Short? = null, - val numberControlsLastThreeYears: Short? = null, - val numberInfractionsLastFiveYears: Short? = null, - val numberGearSeizuresLastFiveYears: Short? = null, - val numberSpeciesSeizuresLastFiveYears: Short? = null, - val numberVesselSeizuresLastFiveYears: Short? = null, + val numberControlsLastFiveYears: Short, + val numberControlsLastThreeYears: Short, + val numberInfractionsLastFiveYears: Short, + val numberGearSeizuresLastFiveYears: Short, + val numberSpeciesSeizuresLastFiveYears: Short, + val numberVesselSeizuresLastFiveYears: Short, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt index 129ac460d1..d34c4218bb 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt @@ -2,6 +2,8 @@ package fr.gouv.cnsp.monitorfish.domain.use_cases.vessel import fr.gouv.cnsp.monitorfish.config.UseCase import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.VesselRiskFactor +import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageErrorCode +import fr.gouv.cnsp.monitorfish.domain.exceptions.BackendUsageException import fr.gouv.cnsp.monitorfish.domain.repositories.RiskFactorRepository import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -14,10 +16,10 @@ class GetVesselRiskFactor( fun execute(internalReferenceNumber: String): VesselRiskFactor { val riskFactor = riskFactorRepository.findByInternalReferenceNumber(internalReferenceNumber) - - requireNotNull(riskFactor) { - "No risk factor found for vessel $internalReferenceNumber" - } + ?: throw BackendUsageException( + BackendUsageErrorCode.NOT_FOUND_BUT_OK, + message = "No risk factor found for vessel $internalReferenceNumber", + ) return riskFactor } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt index 3155262652..6210d24dc9 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt @@ -32,9 +32,9 @@ data class LastPositionDataOutput( val registryPortName: String? = null, val district: String? = null, val districtCode: String? = null, - val gearOnboard: List? = null, - val segments: List? = listOf(), - val speciesOnboard: List? = null, + val gearOnboard: List, + val segments: List, + val speciesOnboard: List, val totalWeightOnboard: Double? = null, val lastControlDateTime: ZonedDateTime? = null, val lastControlInfraction: Boolean? = null, @@ -46,7 +46,7 @@ data class LastPositionDataOutput( val riskFactor: Double? = null, val underCharter: Boolean? = null, val isAtPort: Boolean? = null, - val alerts: List? = listOf(), + val alerts: List, val beaconMalfunctionId: Int? = null, val reportings: List = listOf(), ) { @@ -77,14 +77,15 @@ data class LastPositionDataOutput( registryPortName = position.registryPortName, district = position.district, districtCode = position.districtCode, - gearOnboard = position.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) }, - segments = position.segments, + gearOnboard = position.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } + ?: listOf(), + segments = position.segments ?: listOf(), speciesOnboard = position.speciesOnboard?.map { SpeciesLastPositionDataOutput.fromSpeciesLastPosition( it, ) - }, + } ?: listOf(), totalWeightOnboard = position.totalWeightOnboard, lastControlDateTime = position.lastControlDateTime, lastControlInfraction = position.lastControlInfraction, @@ -96,7 +97,7 @@ data class LastPositionDataOutput( riskFactor = position.riskFactor, underCharter = position.underCharter, isAtPort = position.isAtPort, - alerts = position.alerts, + alerts = position.alerts ?: listOf(), beaconMalfunctionId = position.beaconMalfunctionId, reportings = position.reportings, ) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt index 03066ec4e2..70f04c29bd 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt @@ -4,19 +4,19 @@ import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.VesselRiskFactor import java.time.ZonedDateTime data class RiskFactorDataOutput( - val gearOnboard: List? = null, - val segments: List? = listOf(), - val speciesOnboard: List? = null, - val controlPriorityLevel: Double? = null, + val gearOnboard: List, + val segments: List, + val speciesOnboard: List, + val controlPriorityLevel: Double, val segmentHighestImpact: String? = null, val segmentHighestPriority: String? = null, - val numberControlsLastFiveYears: Short? = null, - val numberControlsLastThreeYears: Short? = null, - val numberInfractionsLastFiveYears: Short? = null, - val numberGearSeizuresLastFiveYears: Short? = null, - val numberSpeciesSeizuresLastFiveYears: Short? = null, - val numberVesselSeizuresLastFiveYears: Short? = null, - val controlRateRiskFactor: Double? = null, + val numberControlsLastFiveYears: Short, + val numberControlsLastThreeYears: Short, + val numberInfractionsLastFiveYears: Short, + val numberGearSeizuresLastFiveYears: Short, + val numberSpeciesSeizuresLastFiveYears: Short, + val numberVesselSeizuresLastFiveYears: Short, + val controlRateRiskFactor: Double, val lastControlDatetime: ZonedDateTime? = null, val impactRiskFactor: Double, val probabilityRiskFactor: Double, @@ -26,7 +26,7 @@ data class RiskFactorDataOutput( companion object { fun fromVesselRiskFactor(vesselRiskFactor: VesselRiskFactor) = RiskFactorDataOutput( - gearOnboard = vesselRiskFactor.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) }, + gearOnboard = vesselRiskFactor.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } ?: listOf(), segments = vesselRiskFactor.segments, segmentHighestImpact = vesselRiskFactor.segmentHighestImpact, segmentHighestPriority = vesselRiskFactor.segmentHighestPriority, @@ -35,7 +35,7 @@ data class RiskFactorDataOutput( SpeciesLastPositionDataOutput.fromSpeciesLastPosition( it, ) - }, + } ?: listOf(), controlPriorityLevel = vesselRiskFactor.controlPriorityLevel, controlRateRiskFactor = vesselRiskFactor.controlRateRiskFactor, numberControlsLastFiveYears = vesselRiskFactor.numberControlsLastFiveYears, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt index 02729883f2..1a6afc664b 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt @@ -38,9 +38,9 @@ data class LastPositionEntity( @Column(name = "trip_number") val tripNumber: String? = null, @Column(name = "latitude") - val latitude: Double? = null, + val latitude: Double, @Column(name = "longitude") - val longitude: Double? = null, + val longitude: Double, @Column(name = "estimated_current_latitude") val estimatedCurrentLatitude: Double? = null, @Column(name = "estimated_current_longitude") diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PositionEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PositionEntity.kt index ff66e8bb4c..2c1e0f31f2 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PositionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/PositionEntity.kt @@ -47,6 +47,10 @@ data class PositionEntity( @Column(name = "network_type") @Enumerated(EnumType.STRING) val networkType: NetworkType? = null, + @Column(name = "speed") + val speed: Double? = null, + @Column(name = "course") + val course: Double? = null, // Mandatory fields @Enumerated(EnumType.STRING) @Column(name = "position_type") @@ -55,10 +59,6 @@ data class PositionEntity( val latitude: Double, @Column(name = "longitude") val longitude: Double, - @Column(name = "speed") - val speed: Double? = null, - @Column(name = "course") - val course: Double? = null, @Column(name = "date_time") val dateTime: ZonedDateTime, ) { diff --git a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx index 734f8c885c..94a2250421 100644 --- a/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx +++ b/frontend/src/features/Alert/components/SideWindowAlerts/SilencedAlerts/fields/VesselField.tsx @@ -1,3 +1,4 @@ +import { VesselSearch } from '@features/VesselSearch' import { FormError, FormErrorCode } from '@libs/FormError' import { Legend, useNewWindow } from '@mtes-mct/monitor-ui' import { useFormikContext } from 'formik' @@ -5,7 +6,6 @@ import { useMemo } from 'react' import styled from 'styled-components' import { UNKNOWN_VESSEL } from '../../../../../../domain/entities/vessel/vessel' -import { VesselSearch } from '../../../../../VesselSearch' import type { SilencedAlertFormValues } from '../types' import type { Vessel } from '@features/Vessel/Vessel.types' @@ -14,15 +14,19 @@ export function VesselField() { const { errors, setValues, values } = useFormikContext() const { newWindowContainerRef } = useNewWindow() - const defaultValue: Partial = useMemo( + const defaultValue: Vessel.VesselIdentity = useMemo( () => ({ - externalReferenceNumber: values.externalReferenceNumber ?? undefined, - flagState: values.flagState ?? '', - internalReferenceNumber: values.internalReferenceNumber ?? undefined, - ircs: values.ircs ?? undefined, - vesselId: values.vesselId ?? undefined, - vesselIdentifier: values.vesselIdentifier ?? undefined, - vesselName: values.vesselName ?? undefined + beaconNumber: undefined, + districtCode: undefined, + externalReferenceNumber: values.externalReferenceNumber, + flagState: values.flagState ?? UNKNOWN_VESSEL.flagState, + internalReferenceNumber: values.internalReferenceNumber, + ircs: values.ircs, + mmsi: undefined, + vesselId: values.vesselId, + vesselIdentifier: values.vesselIdentifier, + vesselLength: undefined, + vesselName: values.vesselName }), [ values.flagState, @@ -35,7 +39,7 @@ export function VesselField() { ] ) - const handleVesselSearchChange = (nextVessel: Partial | undefined) => { + const handleVesselSearchChange = (nextVessel: Vessel.VesselIdentity | undefined) => { if (!nextVessel) { setValues({ ...values, @@ -59,7 +63,7 @@ export function VesselField() { setValues({ ...values, externalReferenceNumber: nextVessel.externalReferenceNumber ?? undefined, - flagState: nextVessel.flagState?.toUpperCase(), + flagState: nextVessel.flagState.toUpperCase(), internalReferenceNumber: nextVessel.internalReferenceNumber ?? undefined, ircs: nextVessel.ircs ?? undefined, vesselId: nextVessel.vesselId ?? undefined, diff --git a/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx b/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx index 8be8f812c4..ae6f635b63 100644 --- a/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx +++ b/frontend/src/features/Mission/components/MissionForm/ActionForm/shared/VesselField.tsx @@ -31,13 +31,17 @@ export function VesselField() { } return { - districtCode: values.districtCode ?? null, - externalReferenceNumber: values.externalReferenceNumber ?? null, - flagState: values.flagState ?? '', - internalReferenceNumber: values.internalReferenceNumber ?? null, - ircs: values.ircs ?? null, + beaconNumber: undefined, + districtCode: values.districtCode, + externalReferenceNumber: values.externalReferenceNumber, + flagState: values.flagState ?? UNKNOWN_VESSEL.flagState, + internalReferenceNumber: values.internalReferenceNumber, + ircs: values.ircs, + mmsi: undefined, vesselId: values.vesselId, - vesselName: values.vesselName ?? null + vesselIdentifier: undefined, + vesselLength: undefined, + vesselName: values.vesselName } }, [ values.flagState, diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts index 057e2533d7..b2c6a2a38f 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionGearsOnboard.ts @@ -32,8 +32,9 @@ export const updateActionGearsOnboard = } const nextGears = gearOnboard + .filter(gear => !!gear.gear) .map(gear => { - const gearByCode = gearsByCode[gear.gear] + const gearByCode = gearsByCode[gear.gear as string] if (!gearByCode) { throw new FrontendError('`gearByCode` is undefined.') } diff --git a/frontend/src/features/RiskFactor/types.ts b/frontend/src/features/RiskFactor/types.ts index e7cd13d2f7..81f293bd3f 100644 --- a/frontend/src/features/RiskFactor/types.ts +++ b/frontend/src/features/RiskFactor/types.ts @@ -1,28 +1,27 @@ import { Vessel } from '@features/Vessel/Vessel.types' import { z } from 'zod' -import { numberOrUndefined, stringOrUndefined } from '../../types' +import { stringOrUndefined } from '../../types' -// TODO Check which of these types are nullable or not export const RiskFactorSchema = z.strictObject({ controlPriorityLevel: z.number(), controlRateRiskFactor: z.number(), detectabilityRiskFactor: z.number(), - gearOnboard: z.array(Vessel.DeclaredLogbookGearSchema).optional(), + gearOnboard: z.array(Vessel.DeclaredLogbookGearSchema), impactRiskFactor: z.number(), lastControlDatetime: stringOrUndefined, - numberControlsLastFiveYears: numberOrUndefined, - numberControlsLastThreeYears: numberOrUndefined, - numberGearSeizuresLastFiveYears: numberOrUndefined, - numberInfractionsLastFiveYears: numberOrUndefined, - numberSpeciesSeizuresLastFiveYears: numberOrUndefined, - numberVesselSeizuresLastFiveYears: numberOrUndefined, + numberControlsLastFiveYears: z.number(), + numberControlsLastThreeYears: z.number(), + numberGearSeizuresLastFiveYears: z.number(), + numberInfractionsLastFiveYears: z.number(), + numberSpeciesSeizuresLastFiveYears: z.number(), + numberVesselSeizuresLastFiveYears: z.number(), probabilityRiskFactor: z.number(), riskFactor: z.number(), segmentHighestImpact: stringOrUndefined, segmentHighestPriority: stringOrUndefined, segments: z.array(z.string()), - speciesOnboard: z.array(Vessel.DeclaredLogbookSpeciesSchema).optional() + speciesOnboard: z.array(Vessel.DeclaredLogbookSpeciesSchema) }) export type RiskFactor = z.infer diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index 7b6c2526b3..a13fd2b401 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -141,19 +141,17 @@ export namespace Vessel { vessel: Vessel.EnrichedVessel } - // TODO Check which of these types are nullable or not export const DeclaredLogbookGearSchema = z.strictObject({ dimensions: stringOrUndefined, gear: stringOrUndefined, mesh: numberOrUndefined }) - // TODO Check which of these types are nullable or not export const DeclaredLogbookSpeciesSchema = z.strictObject({ - faoZone: stringOrUndefined, - gear: stringOrUndefined, - species: stringOrUndefined, - weight: numberOrUndefined + faoZone: z.string(), + gear: z.string(), + species: z.string(), + weight: z.number() }) export type DeclaredLogbookSpecies = z.infer @@ -162,7 +160,7 @@ export namespace Vessel { alerts: z.array(z.union([z.nativeEnum(PendingAlertValueType), z.literal('PNO_LAN_WEIGHT_TOLERANCE_ALERT')])), beaconMalfunctionId: numberOrUndefined, beaconNumber: numberOrUndefined, - course: z.number(), + course: numberOrUndefined, dateTime: z.string(), departureDateTime: stringOrUndefined, destination: stringOrUndefined, @@ -196,7 +194,7 @@ export namespace Vessel { riskFactor: numberOrUndefined, segments: z.array(z.string()), speciesOnboard: z.array(DeclaredLogbookSpeciesSchema), - speed: z.number(), + speed: numberOrUndefined, totalWeightOnboard: numberOrUndefined, tripNumber: stringOrUndefined, underCharter: booleanOrUndefined, @@ -261,13 +259,13 @@ export namespace Vessel { } export type VesselEnhancedLastPositionWebGLObject = Vessel.VesselEnhancedObject & { coordinates: number[] - course: number + course: number | undefined filterPreview: number // 0 is False, 1 is True - for WebGL hasBeaconMalfunction: boolean isAtPort: boolean | undefined isFiltered: number // 0 is False, 1 is True - for WebGL lastPositionSentAt: number - speed: number + speed: number | undefined vesselFeatureId: VesselFeatureId } diff --git a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts index e32e36e6e7..f5c00c9338 100644 --- a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts +++ b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts @@ -55,7 +55,9 @@ function convertToEnhancedLastPositions( filterPreview: 0, flagState: vessel.flagState, fleetSegmentsArray: vessel.segments ? vessel.segments.map(segment => segment.replace(' ', '')) : [], - gearsArray: vessel.gearOnboard ? Array.from(new Set(vessel.gearOnboard.map(gear => gear.gear))) : [], + gearsArray: vessel.gearOnboard + ? Array.from(new Set(vessel.gearOnboard.map(gear => gear.gear).filter((gear): gear is string => !!gear))) + : [], hasAlert: !!vessel.alerts?.length, hasBeaconMalfunction: !!vessel.beaconMalfunctionId, hasInfractionSuspicion: From c76e19421ee229ca7127f21605131fda31d612f2 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Fri, 31 Jan 2025 12:19:18 +0100 Subject: [PATCH 03/21] Migrate alerts and fleet segments apis to rtk --- .../entities/risk_factor/VesselRiskFactor.kt | 14 +- frontend/src/api/alert.ts | 132 ++++++------------ frontend/src/api/api.ts | 9 -- .../use_cases/alert/addSilencedAlert.ts | 10 +- .../use_cases/alert/getOperationalAlerts.ts | 23 +-- .../use_cases/alert/getSilencedAlerts.ts | 21 +-- .../alert/reactivateSilencedAlert.ts | 20 ++- .../domain/use_cases/alert/silenceAlert.ts | 11 +- .../domain/use_cases/alert/validateAlert.ts | 11 +- frontend/src/features/FleetSegment/apis.ts | 114 ++++++--------- .../index.tsx | 4 +- .../useCases/addFleetSegmentYear.ts | 19 ++- .../useCases/createFleetSegment.ts | 4 +- .../useCases/deleteFleetSegment.ts | 6 +- .../useCases/getFleetSegmentsYearEntries.ts | 16 ++- .../useCases/updateActionSpeciesOnboard.ts | 3 +- frontend/src/features/Vessel/Vessel.types.ts | 2 +- 17 files changed, 171 insertions(+), 248 deletions(-) diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt index f0082003cc..8635a16fa9 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt @@ -37,16 +37,16 @@ data class VesselRiskFactor( val gearOnboard: List? = listOf(), val speciesOnboard: List? = listOf(), val totalWeightOnboard: Double? = null, - val segments: List, + val segments: List = listOf(), val probableSegments: List? = listOf(), val segmentHighestImpact: String? = null, val segmentHighestPriority: String? = null, val lastControlDatetime: ZonedDateTime? = null, val postControlComments: String? = null, - val numberControlsLastFiveYears: Short, - val numberControlsLastThreeYears: Short, - val numberInfractionsLastFiveYears: Short, - val numberGearSeizuresLastFiveYears: Short, - val numberSpeciesSeizuresLastFiveYears: Short, - val numberVesselSeizuresLastFiveYears: Short, + val numberControlsLastFiveYears: Short = 0, + val numberControlsLastThreeYears: Short = 0, + val numberInfractionsLastFiveYears: Short = 0, + val numberGearSeizuresLastFiveYears: Short = 0, + val numberSpeciesSeizuresLastFiveYears: Short = 0, + val numberVesselSeizuresLastFiveYears: Short = 0, ) diff --git a/frontend/src/api/alert.ts b/frontend/src/api/alert.ts index b6a1f60853..9957a3af85 100644 --- a/frontend/src/api/alert.ts +++ b/frontend/src/api/alert.ts @@ -2,7 +2,7 @@ import { FrontendApiError } from '@libs/FrontendApiError' -import { monitorfishApi, monitorfishApiKy } from './api' +import { monitorfishApi } from './api' import type { LEGACY_PendingAlert, @@ -41,97 +41,45 @@ export const alertApi = monitorfishApi.injectEndpoints({ url: `/operational_alerts/silenced` }), transformErrorResponse: response => new FrontendApiError(CREATE_SILENCED_ALERT_ERROR_MESSAGE, response) + }), + deleteSilencedAlert: builder.mutation({ + query: id => ({ + method: 'DELETE', + url: `/operational_alerts/silenced/${id}` + }), + transformErrorResponse: response => new FrontendApiError(DELETE_SILENCED_ALERT_ERROR_MESSAGE, response) + }), + getOperationalAlerts: builder.query({ + query: () => '/bff/v1/operational_alerts', + transformErrorResponse: response => new FrontendApiError(ALERTS_ERROR_MESSAGE, response), + transformResponse: (response: PendingAlert[]) => response.map(normalizePendingAlert) + }), + getSilencedAlerts: builder.query({ + query: () => '/operational_alerts/silenced', + transformErrorResponse: response => new FrontendApiError(ALERTS_ERROR_MESSAGE, response), + transformResponse: (response: SilencedAlert[]) => response + }), + silenceAlert: builder.mutation< + LEGACY_SilencedAlert, + { id: string; silencedAlertPeriodRequest: SilencedAlertPeriodRequest } + >({ + query: ({ id, silencedAlertPeriodRequest }) => ({ + body: { + beforeDateTime: silencedAlertPeriodRequest.beforeDateTime?.toISOString() ?? '', + silencedAlertPeriod: silencedAlertPeriodRequest.silencedAlertPeriod ?? '' + }, + method: 'PUT', + url: `/operational_alerts/${id}/silence` + }), + transformErrorResponse: response => new FrontendApiError(SILENCE_ALERT_ERROR_MESSAGE, response), + transformResponse: (response: SilencedAlert) => response + }), + validateAlert: builder.mutation({ + query: id => ({ + method: 'PUT', + url: `/operational_alerts/${id}/validate` + }), + transformErrorResponse: response => new FrontendApiError(VALIDATE_ALERT_ERROR_MESSAGE, response) }) }) }) - -export const { useCreateSilencedAlertMutation } = alertApi - -/** - * Get operational alerts - * - * @throws {@link FrontendApiError} - */ -async function getOperationalAlertsFromAPI(): Promise { - try { - const data = await monitorfishApiKy.get('/bff/v1/operational_alerts').json() - - return data.map(normalizePendingAlert) - } catch (err) { - throw new FrontendApiError(ALERTS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Validate an alert - * - * @throws {@link FrontendApiError} - */ -async function validateAlertFromAPI(id: string): Promise { - try { - await monitorfishApiKy.put(`/bff/v1/operational_alerts/${id}/validate`) - } catch (err) { - throw new FrontendApiError(VALIDATE_ALERT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Silence an alert and returns the saved silenced alert - * - * @throws {@link FrontendApiError} - */ -async function silenceAlertFromAPI( - id: string, - silencedAlertPeriodRequest: SilencedAlertPeriodRequest -): Promise { - // TODO Normalize this data before calling the api service rather than here. - const silencedAlertPeriod = silencedAlertPeriodRequest.silencedAlertPeriod ?? '' - const beforeDateTime = silencedAlertPeriodRequest.beforeDateTime?.toISOString() ?? '' - - try { - return await monitorfishApiKy - .put(`/bff/v1/operational_alerts/${id}/silence`, { - json: { - beforeDateTime, - silencedAlertPeriod - } - }) - .json() - } catch (err) { - throw new FrontendApiError(SILENCE_ALERT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get silenced alerts - * - * @throws {@link FrontendApiError} - */ -async function getSilencedAlertsFromAPI(): Promise { - try { - return await monitorfishApiKy.get('/bff/v1/operational_alerts/silenced').json() - } catch (err) { - throw new FrontendApiError(ALERTS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Delete a silenced alert - * - * @throws {@link FrontendApiError} - */ -async function deleteSilencedAlertFromAPI(id: string): Promise { - try { - await monitorfishApiKy.delete(`/bff/v1/operational_alerts/silenced/${id}`) - } catch (err) { - throw new FrontendApiError(DELETE_SILENCED_ALERT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -export { - getOperationalAlertsFromAPI, - validateAlertFromAPI, - silenceAlertFromAPI, - getSilencedAlertsFromAPI, - deleteSilencedAlertFromAPI -} diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index 3d7e9c4fea..e7afc6dbd4 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -7,7 +7,6 @@ import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react' import { setMeasurement, startSpan } from '@sentry/react' import { normalizeRtkBaseQuery } from '@utils/normalizeRtkBaseQuery' import { sha256 } from '@utils/sha256' -import { undefinedize } from '@utils/undefinedize' import ky, { HTTPError } from 'ky' import { RTK_MAX_RETRIES, RtkCacheTagType } from './constants' @@ -209,14 +208,6 @@ export const monitorfishPublicApi = createApi({ export const monitorfishApiKy = ky.extend({ hooks: { - afterResponse: [ - async (_input, _options, response) => { - const responseData = await response.json() - const normalizedResponse = undefinedize(responseData) - - return new Response(JSON.stringify(normalizedResponse), response) - } - ], beforeError: [ async error => { const { request, response } = error diff --git a/frontend/src/domain/use_cases/alert/addSilencedAlert.ts b/frontend/src/domain/use_cases/alert/addSilencedAlert.ts index 35686d3d90..148f10cefe 100644 --- a/frontend/src/domain/use_cases/alert/addSilencedAlert.ts +++ b/frontend/src/domain/use_cases/alert/addSilencedAlert.ts @@ -12,6 +12,7 @@ import type { SilencedAlertData } from '@features/Alert/types' export const addSilencedAlert = (silencedAlert: SilencedAlertData): MainAppThunk => async (dispatch, getState) => { + // @ts-ignore const previousSilencedAlerts = getState().alert.silencedAlerts try { @@ -19,14 +20,7 @@ export const addSilencedAlert = * TODO Why is there this TS type issue as `data` is part of the response : * TS2339: Property 'data' does not exist on type '{ data: SilencedAlert; } | { error: FetchBaseQueryError | SerializedError; }'. */ - // @ts-ignore - const { data: savedSilencedAlert, error: silencedAlertError } = await dispatch( - alertApi.endpoints.createSilencedAlert.initiate(silencedAlert) - ) - if (silencedAlertError) { - // eslint-disable-next-line @typescript-eslint/no-throw-literal - throw silencedAlertError - } + const savedSilencedAlert = await dispatch(alertApi.endpoints.createSilencedAlert.initiate(silencedAlert)).unwrap() const nextSilencedAlerts = [savedSilencedAlert, ...previousSilencedAlerts] dispatch(setSilencedAlerts(nextSilencedAlerts)) diff --git a/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts b/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts index e43a7536ad..c507219eb2 100644 --- a/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts +++ b/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts @@ -1,16 +1,19 @@ +import { alertApi } from '@api/alert' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { setPendingAlerts } from '@features/Alert/components/SideWindowAlerts/slice' -import { getOperationalAlertsFromAPI } from '../../../api/alert' import { setError } from '../../shared_slices/Global' -import type { MainAppThunk } from '../../../store' +import type { MainAppThunk } from '@store' -export const getOperationalAlerts = (): MainAppThunk => dispatch => { - getOperationalAlertsFromAPI() - .then(alerts => { - dispatch(setPendingAlerts(alerts)) - }) - .catch(error => { - dispatch(setError(error)) - }) +export const getOperationalAlerts = (): MainAppThunk => async dispatch => { + try { + const alerts = await dispatch( + alertApi.endpoints.getOperationalAlerts.initiate(undefined, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + + dispatch(setPendingAlerts(alerts)) + } catch (error) { + dispatch(setError(error)) + } } diff --git a/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts b/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts index b1edb1e4ef..a63f61c22f 100644 --- a/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts +++ b/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts @@ -1,14 +1,17 @@ +import { alertApi } from '@api/alert' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { setSilencedAlerts } from '@features/Alert/components/SideWindowAlerts/slice' -import { getSilencedAlertsFromAPI } from '../../../api/alert' import { setError } from '../../shared_slices/Global' -export const getSilencedAlerts = () => dispatch => { - getSilencedAlertsFromAPI() - .then(silencedAlerts => { - dispatch(setSilencedAlerts(silencedAlerts)) - }) - .catch(error => { - dispatch(setError(error)) - }) +export const getSilencedAlerts = () => async dispatch => { + try { + const silencedAlerts = await dispatch( + alertApi.endpoints.getSilencedAlerts.initiate(undefined, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + + dispatch(setSilencedAlerts(silencedAlerts)) + } catch (error) { + dispatch(setError(error)) + } } diff --git a/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts b/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts index 37369b4aef..030e033549 100644 --- a/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts +++ b/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts @@ -1,16 +1,16 @@ +import { alertApi } from '@api/alert' import { setSilencedAlerts } from '@features/Alert/components/SideWindowAlerts/slice' +import { deleteListItems } from '@utils/deleteListItems' +import { updateListItemsProp } from '@utils/updateListItemsProp' -import { deleteSilencedAlertFromAPI } from '../../../api/alert' -import { deleteListItems } from '../../../utils/deleteListItems' -import { updateListItemsProp } from '../../../utils/updateListItemsProp' import { setError } from '../../shared_slices/Global' -import type { MainAppThunk } from '../../../store' import type { LEGACY_SilencedAlert } from '@features/Alert/types' +import type { MainAppThunk } from '@store' export const reactivateSilencedAlert = (id: string): MainAppThunk => - (dispatch, getState) => { + async (dispatch, getState) => { const previousSilencedAlerts = getState().alert.silencedAlerts const previousSilencedAlertsWithReactivatedFlag = setAlertAsReactivated(previousSilencedAlerts, id) dispatch(setSilencedAlerts(previousSilencedAlertsWithReactivatedFlag)) @@ -20,11 +20,13 @@ export const reactivateSilencedAlert = dispatch(setSilencedAlerts(previousSilencedAlertsWithoutReactivatedFlag)) }, 3200) - deleteSilencedAlertFromAPI(id).catch(error => { + try { + await dispatch(alertApi.endpoints.deleteSilencedAlert.initiate(id)).unwrap() + } catch (error) { clearTimeout(timeout) dispatch(setSilencedAlerts(previousSilencedAlerts)) dispatch(setError(error)) - }) + } } function setAlertAsReactivated(previousSilencedAlerts: LEGACY_SilencedAlert[], id: string) { @@ -32,7 +34,3 @@ function setAlertAsReactivated(previousSilencedAlerts: LEGACY_SilencedAlert[], i isReactivated: true }) } - -export function removeAlert(previousAlerts: LEGACY_SilencedAlert[], id: string) { - return deleteListItems(previousAlerts, 'id', id) -} diff --git a/frontend/src/domain/use_cases/alert/silenceAlert.ts b/frontend/src/domain/use_cases/alert/silenceAlert.ts index 0d2cf32da6..953cceab93 100644 --- a/frontend/src/domain/use_cases/alert/silenceAlert.ts +++ b/frontend/src/domain/use_cases/alert/silenceAlert.ts @@ -1,3 +1,4 @@ +import { alertApi } from '@api/alert' import { addToPendingAlertsBeingSilenced, removeFromSilencedAlertsQueue, @@ -6,14 +7,13 @@ import { } from '@features/Alert/components/SideWindowAlerts/slice' import { removeVesselAlertAndUpdateReporting } from '@features/Vessel/slice' import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' +import { deleteListItems } from '@utils/deleteListItems' -import { silenceAlertFromAPI } from '../../../api/alert' -import { deleteListItems } from '../../../utils/deleteListItems' import { VesselFeature } from '../../entities/vessel/vessel' import { setError } from '../../shared_slices/Global' -import type { MainAppThunk } from '../../../store' import type { SilencedAlertPeriodRequest } from '@features/Alert/types' +import type { MainAppThunk } from '@store' /** * Silence an alert @@ -38,7 +38,10 @@ export const silenceAlert = }, 3200) try { - const silencedAlert = await silenceAlertFromAPI(pendingAlertId, silencedAlertPeriodRequest) + const silencedAlert = await dispatch( + alertApi.endpoints.silenceAlert.initiate({ id: pendingAlertId, silencedAlertPeriodRequest }) + ).unwrap() + dispatch( removeVesselAlertAndUpdateReporting({ alertType: silencedAlert.value.type, diff --git a/frontend/src/domain/use_cases/alert/validateAlert.ts b/frontend/src/domain/use_cases/alert/validateAlert.ts index 0a45f8b6bc..435f2f4e8b 100644 --- a/frontend/src/domain/use_cases/alert/validateAlert.ts +++ b/frontend/src/domain/use_cases/alert/validateAlert.ts @@ -1,17 +1,17 @@ +import { alertApi } from '@api/alert' import { RtkCacheTagType } from '@api/constants' import { setPendingAlerts } from '@features/Alert/components/SideWindowAlerts/slice' import { removeVesselAlertAndUpdateReporting } from '@features/Vessel/slice' import { renderVesselFeatures } from '@features/Vessel/useCases/renderVesselFeatures' import { vesselApi } from '@features/Vessel/vesselApi' +import { deleteListItems } from '@utils/deleteListItems' +import { updateListItemsProp } from '@utils/updateListItemsProp' -import { validateAlertFromAPI } from '../../../api/alert' -import { deleteListItems } from '../../../utils/deleteListItems' -import { updateListItemsProp } from '../../../utils/updateListItemsProp' import { VesselFeature } from '../../entities/vessel/vessel' import { setError } from '../../shared_slices/Global' -import type { MainAppThunk } from '../../../store' import type { LEGACY_PendingAlert } from '@features/Alert/types' +import type { MainAppThunk } from '@store' export const validateAlert = (id: string): MainAppThunk => @@ -26,7 +26,8 @@ export const validateAlert = }, 3200) try { - await validateAlertFromAPI(id) + await dispatch(alertApi.endpoints.validateAlert.initiate(id)).unwrap() + // We dispatch this action to update the reporting list // since it depends on the alerts list that we just updated dispatch(vesselApi.util.invalidateTags([RtkCacheTagType.Reportings])) diff --git a/frontend/src/features/FleetSegment/apis.ts b/frontend/src/features/FleetSegment/apis.ts index 1205417f3d..b0595255a1 100644 --- a/frontend/src/features/FleetSegment/apis.ts +++ b/frontend/src/features/FleetSegment/apis.ts @@ -1,4 +1,4 @@ -import { monitorfishApi, monitorfishApiKy } from '@api/api' +import { monitorfishApi } from '@api/api' import { FleetSegmentSchema } from '@features/FleetSegment/types' import { MissionAction } from '@features/Mission/missionAction.types' import { FrontendApiError } from '@libs/FrontendApiError' @@ -19,8 +19,23 @@ export type UpdateFleetSegmentParams = { updatedSegment: FleetSegment } +export const UPDATE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu modifier le segment de flotte" +export const CREATE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu créer le segment de flotte" +export const DELETE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu supprimer le segment de flotte" +export const GET_FLEET_SEGMENT_YEAR_ENTRIES_ERROR_MESSAGE = + "Nous n'avons pas pu récupérer les années des segments de flotte" +export const ADD_FLEET_SEGMENT_YEAR_ERROR_MESSAGE = + "Nous n'avons pas pu ajouter une nouvelle année de segments de flotte" + export const fleetSegmentApi = monitorfishApi.injectEndpoints({ endpoints: builder => ({ + addFleetSegmentYear: builder.mutation({ + query: nextYear => ({ + method: 'POST', + url: `/bff/v1/admin/fleet_segments/${nextYear}` + }), + transformErrorResponse: response => new FrontendApiError(ADD_FLEET_SEGMENT_YEAR_ERROR_MESSAGE, response) + }), computeFleetSegments: builder.query({ query: params => ({ body: params, @@ -32,6 +47,26 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ .map(segment => FleetSegmentSchema.parse(segment)) .sort((a, b) => a.segment.localeCompare(b.segment)) }), + createFleetSegment: builder.mutation({ + query: segmentFields => ({ + body: segmentFields, + method: 'POST', + url: '/bff/v1/admin/fleet_segments' + }), + transformErrorResponse: response => new FrontendApiError(CREATE_FLEET_SEGMENT_ERROR_MESSAGE, response), + transformResponse: (baseQueryReturnValue: FleetSegment) => FleetSegmentSchema.parse(baseQueryReturnValue) + }), + deleteFleetSegment: builder.mutation({ + query: ({ segment, year }) => ({ + method: 'DELETE', + url: `/bff/v1/admin/fleet_segments?year=${year}&segment=${segment}` + }), + transformErrorResponse: response => new FrontendApiError(DELETE_FLEET_SEGMENT_ERROR_MESSAGE, response), + transformResponse: (baseQueryReturnValue: FleetSegment[]) => + baseQueryReturnValue + .map(segment => FleetSegmentSchema.parse(segment)) + .sort((a, b) => a.segment.localeCompare(b.segment)) + }), getFleetSegments: builder.query({ providesTags: () => [{ type: 'FleetSegments' }], query: year => { @@ -44,6 +79,10 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ .map(segment => FleetSegmentSchema.parse(segment)) .sort((a, b) => a.segment.localeCompare(b.segment)) }), + getFleetSegmentYearEntries: builder.query({ + query: () => '/bff/v1/admin/fleet_segments/years', + transformErrorResponse: response => new FrontendApiError(GET_FLEET_SEGMENT_YEAR_ENTRIES_ERROR_MESSAGE, response) + }), updateFleetSegment: builder.query({ query: params => { const updatedSegment = FleetSegmentSchema.parse(params.updatedSegment) @@ -61,76 +100,3 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ }) export const { useGetFleetSegmentsQuery } = fleetSegmentApi - -export const UPDATE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu modifier le segment de flotte" -export const CREATE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu créer le segment de flotte" -export const DELETE_FLEET_SEGMENT_ERROR_MESSAGE = "Nous n'avons pas pu supprimer le segment de flotte" -export const GET_FLEET_SEGMENT_YEAR_ENTRIES_ERROR_MESSAGE = - "Nous n'avons pas pu récupérer les années des segments de flotte" -export const ADD_FLEET_SEGMENT_YEAR_ERROR_MESSAGE = - "Nous n'avons pas pu ajouter une nouvelle année de segments de flotte" - -/** - * Delete a fleet segment - * - * @throws {@link FrontendApiError} - */ -async function deleteFleetSegmentFromAPI(segment: string, year: number): Promise { - try { - return await monitorfishApiKy - .delete(`/bff/v1/admin/fleet_segments?year=${year}&segment=${segment}`) - .json() - } catch (err) { - throw new FrontendApiError(DELETE_FLEET_SEGMENT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Create a fleet segment - * - * @throws {@link FrontendApiError} - */ -async function createFleetSegmentFromAPI(segmentFields: FleetSegment): Promise { - try { - return await monitorfishApiKy - .post('/bff/v1/admin/fleet_segments', { - json: segmentFields - }) - .json() - } catch (err) { - throw new FrontendApiError(CREATE_FLEET_SEGMENT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Add a new fleet segments year - * - * @throws {@link FrontendApiError} - */ -async function addFleetSegmentYearFromAPI(nextYear: number) { - try { - return await monitorfishApiKy.post(`/bff/v1/admin/fleet_segments/${nextYear}`) - } catch (err) { - throw new FrontendApiError(ADD_FLEET_SEGMENT_YEAR_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get fleet segment year entries - * - * @throws {@link FrontendApiError} - */ -async function getFleetSegmentYearEntriesFromAPI(): Promise { - try { - return await monitorfishApiKy.get('/bff/v1/admin/fleet_segments/years').json() - } catch (err) { - throw new FrontendApiError(GET_FLEET_SEGMENT_YEAR_ENTRIES_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -export { - deleteFleetSegmentFromAPI, - createFleetSegmentFromAPI, - getFleetSegmentYearEntriesFromAPI, - addFleetSegmentYearFromAPI -} diff --git a/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/index.tsx b/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/index.tsx index 4aa4955878..324ce1d754 100644 --- a/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/index.tsx +++ b/frontend/src/features/FleetSegment/components/VesselCurrentFleetSegmentDetails/index.tsx @@ -55,7 +55,7 @@ function VesselCurrentFleetSegmentDetailsWithRef( value: gearsWithName?.length ? ( <> {gearsWithName?.map(gear => ( - + {gear.gearName} ({gear.gear}) ))} @@ -71,7 +71,7 @@ function VesselCurrentFleetSegmentDetailsWithRef( value: ( <> {gearsWithName?.map(gear => ( - + {gear.mesh ? <>{gear.mesh} mm : -} ({gear.gear}) ))} diff --git a/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts b/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts index c308087887..bc6f38f0d2 100644 --- a/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts +++ b/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts @@ -1,4 +1,4 @@ -import { addFleetSegmentYearFromAPI } from '@features/FleetSegment/apis' +import { fleetSegmentApi } from '@features/FleetSegment/apis' import { getFleetSegmentsYearEntries } from './getFleetSegmentsYearEntries' import { setError } from '../../../domain/shared_slices/Global' @@ -6,9 +6,14 @@ import { setError } from '../../../domain/shared_slices/Global' /** * Add a new fleet segment year */ -export const addFleetSegmentYear = (year: number) => dispatch => - addFleetSegmentYearFromAPI(year) - .then(() => dispatch(getFleetSegmentsYearEntries())) - .catch(error => { - dispatch(setError(error)) - }) +export const addFleetSegmentYear = (year: number) => async dispatch => { + try { + await dispatch(fleetSegmentApi.endpoints.addFleetSegmentYear.initiate(year)).unwrap() + + return dispatch(getFleetSegmentsYearEntries()) + } catch (error) { + dispatch(setError(error)) + + return undefined + } +} diff --git a/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts b/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts index 38b2a4f06f..6c98aa36d5 100644 --- a/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts +++ b/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts @@ -1,4 +1,4 @@ -import { createFleetSegmentFromAPI } from '@features/FleetSegment/apis' +import { fleetSegmentApi } from '@features/FleetSegment/apis' import { setError } from '../../../domain/shared_slices/Global' @@ -18,7 +18,7 @@ export const createFleetSegment = throw new Error("Le segment de flotte n'a pas d'année") } - const newSegment = await createFleetSegmentFromAPI(segmentFields) + const newSegment = await dispatch(fleetSegmentApi.endpoints.createFleetSegment.initiate(segmentFields)).unwrap() return addFleetSegments(previousFleetSegments, newSegment) } catch (error) { diff --git a/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts b/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts index 549e036b0f..5ac99d02f1 100644 --- a/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts +++ b/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts @@ -1,4 +1,4 @@ -import { deleteFleetSegmentFromAPI } from '@features/FleetSegment/apis' +import { fleetSegmentApi } from '@features/FleetSegment/apis' import { setError } from '../../../domain/shared_slices/Global' @@ -11,7 +11,9 @@ export const deleteFleetSegment = (segment: string, year: number) => async (dispatch): Promise => { try { - const updatedFleetSegments = await deleteFleetSegmentFromAPI(segment, year) + const updatedFleetSegments = await dispatch( + fleetSegmentApi.endpoints.deleteFleetSegment.initiate({ segment, year }) + ).unwrap() return (Object.assign([], updatedFleetSegments) as FleetSegment[]).sort((a, b) => a.segment.localeCompare(b.segment) diff --git a/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts b/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts index fd33380c8d..b0ccbabd8b 100644 --- a/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts +++ b/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts @@ -1,8 +1,16 @@ -import { getFleetSegmentYearEntriesFromAPI } from '@features/FleetSegment/apis' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { fleetSegmentApi } from '@features/FleetSegment/apis' import { setError } from '../../../domain/shared_slices/Global' -export const getFleetSegmentsYearEntries = () => dispatch => - getFleetSegmentYearEntriesFromAPI().catch(error => { +export const getFleetSegmentsYearEntries = () => async dispatch => { + try { + return dispatch( + fleetSegmentApi.endpoints.getFleetSegmentYearEntries.initiate(undefined, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + } catch (error) { dispatch(setError(error)) - }) + + return undefined + } +} diff --git a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts index 188a5dfb8c..57cef0f38c 100644 --- a/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts +++ b/frontend/src/features/Mission/components/MissionForm/useCases/updateActionSpeciesOnboard.ts @@ -26,7 +26,8 @@ export const updateActionSpeciesOnboard = const summedSpeciesOnboard = getSummedSpeciesOnBoard(speciesOnboard) const nextSpeciesOnboard = summedSpeciesOnboard - .sort((a, b) => b.weight - a.weight) + .filter(specy => !!specy.weight) + .sort((a, b) => (b.weight as number) - (a.weight as number)) .map(specy => ({ controlledWeight: undefined, declaredWeight: specy.weight, diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index a13fd2b401..0e28b25288 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -151,7 +151,7 @@ export namespace Vessel { faoZone: z.string(), gear: z.string(), species: z.string(), - weight: z.number() + weight: numberOrUndefined }) export type DeclaredLogbookSpecies = z.infer From 47a7df3e094e89bf1016699286d7d495332a163c Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 15:36:23 +0100 Subject: [PATCH 04/21] Migrate beacon malfunctions apis to RTK --- frontend/src/api/APIWorker.tsx | 6 +- frontend/src/api/beaconMalfunction.ts | 133 ------------------ frontend/src/api/reporting.ts | 0 .../getAllBeaconMalfunctions.ts | 16 --- .../openBeaconMalfunction.js | 39 ----- .../openBeaconMalfunctionInKanban.ts | 16 --- .../saveBeaconMalfunctionCommentFromKanban.js | 50 ------- .../beaconMalfunction/sendNotification.ts | 25 ---- .../updateBeaconMalfunctionFromKanban.js | 48 ------- .../getMalfunctionStartDateText.test.ts | 6 +- .../src/features/BeaconMalfunction/apis.ts | 93 ++++++++++++ .../BeaconMalfunctionCard.tsx | 15 +- .../BeaconMalfunctionDetailsFollowUp.tsx | 2 +- .../SendNotification.tsx | 2 +- .../VesselStatusSelect.tsx | 4 +- .../BeaconMalfunctionBoard/index.tsx | 19 ++- .../BeaconMalfunctionBoard/utils.tsx | 2 +- .../src/features/BeaconMalfunction/types.ts | 20 +-- .../useCases/getAllBeaconMalfunctions.ts | 19 +++ .../useCases}/getVesselBeaconMalfunctions.ts | 22 +-- .../useCases/openBeaconMalfunction.ts | 45 ++++++ .../useCases/openBeaconMalfunctionInKanban.ts | 21 +++ .../saveBeaconMalfunctionCommentFromKanban.ts | 59 ++++++++ .../useCases/sendNotification.ts | 34 +++++ .../updateBeaconMalfunctionFromKanban.ts | 62 ++++++++ frontend/src/features/SideWindow/index.tsx | 2 +- .../Equipment/VesselEquipment.tsx | 2 +- .../resume/CurrentBeaconMalfunctionBody.tsx | 3 +- .../warnings/BeaconMalfunctionWarning.tsx | 2 +- 29 files changed, 393 insertions(+), 374 deletions(-) delete mode 100644 frontend/src/api/beaconMalfunction.ts delete mode 100644 frontend/src/api/reporting.ts delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.ts delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts delete mode 100644 frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js create mode 100644 frontend/src/features/BeaconMalfunction/apis.ts create mode 100644 frontend/src/features/BeaconMalfunction/useCases/getAllBeaconMalfunctions.ts rename frontend/src/{domain/use_cases/beaconMalfunction => features/BeaconMalfunction/useCases}/getVesselBeaconMalfunctions.ts (69%) create mode 100644 frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunction.ts create mode 100644 frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban.ts create mode 100644 frontend/src/features/BeaconMalfunction/useCases/saveBeaconMalfunctionCommentFromKanban.ts create mode 100644 frontend/src/features/BeaconMalfunction/useCases/sendNotification.ts create mode 100644 frontend/src/features/BeaconMalfunction/useCases/updateBeaconMalfunctionFromKanban.ts diff --git a/frontend/src/api/APIWorker.tsx b/frontend/src/api/APIWorker.tsx index 42879db593..a2adc0bced 100644 --- a/frontend/src/api/APIWorker.tsx +++ b/frontend/src/api/APIWorker.tsx @@ -1,3 +1,6 @@ +import { getAllBeaconMalfunctions } from '@features/BeaconMalfunction/useCases/getAllBeaconMalfunctions' +import { getVesselBeaconMalfunctions } from '@features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions' +import { openBeaconMalfunctionInKanban } from '@features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban' import { fleetSegmentApi } from '@features/FleetSegment/apis' import { getAllRegulatoryLayers } from '@features/Regulation/useCases/getAllRegulatoryLayers' import { reportingApi } from '@features/Reporting/reportingApi' @@ -9,9 +12,6 @@ import { VesselSidebarTab } from '../domain/entities/vessel/vessel' import { setIsUpdatingVessels } from '../domain/shared_slices/Global' import { getOperationalAlerts } from '../domain/use_cases/alert/getOperationalAlerts' import { getSilencedAlerts } from '../domain/use_cases/alert/getSilencedAlerts' -import { getAllBeaconMalfunctions } from '../domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions' -import { getVesselBeaconMalfunctions } from '../domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions' -import { openBeaconMalfunctionInKanban } from '../domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban' import { getAllGearCodes } from '../domain/use_cases/gearCode/getAllGearCodes' import { getInfractions } from '../domain/use_cases/infraction/getInfractions' import { getVesselControls } from '../domain/use_cases/mission/getVesselControls' diff --git a/frontend/src/api/beaconMalfunction.ts b/frontend/src/api/beaconMalfunction.ts deleted file mode 100644 index 3eaa6dc191..0000000000 --- a/frontend/src/api/beaconMalfunction.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { FrontendApiError } from '@libs/FrontendApiError' - -import { monitorfishApiKy } from './api' - -import type { NOTIFICATION_TYPE, UserType } from '@features/BeaconMalfunction/constants' -import type { - BeaconMalfunction, - BeaconMalfunctionResumeAndDetails, - UpdateBeaconMalfunction, - VesselBeaconMalfunctionsResumeAndHistory -} from '@features/BeaconMalfunction/types' -import type { Vessel } from '@features/Vessel/Vessel.types' - -export const ARCHIVE_BEACON_MALFUNCTION = "Nous n'avons pas pu archiver les avaries VMS" -export const GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les avaries VMS" -export const GET_BEACON_MALFUNCTION_ERROR_MESSAGE = "Nous n'avons pas pu récupérer l'avarie VMS" -export const UPDATE_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu mettre à jour le statut de l'avarie VMS" -export const SAVE_BEACON_MALFUNCTION_COMMENT_ERROR_MESSAGE = - "Nous n'avons pas pu ajouter le commentaire sur l'avarie VMS" -export const GET_VESSEL_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les avaries de ce navire" -export const SEND_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu envoyer la notification" - -/** - * Get all beacon malfunctions - * - * @throws {@link FrontendApiError} - */ -async function getAllBeaconMalfunctionsFromAPI(): Promise { - try { - return await monitorfishApiKy.get('/bff/v1/beacon_malfunctions').json() - } catch (err) { - throw new FrontendApiError(GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Update a beacon malfunction - * - * @throws {@link FrontendApiError} - */ -async function updateBeaconMalfunctionFromAPI( - id: number, - updatedFields: UpdateBeaconMalfunction -): Promise { - try { - return await monitorfishApiKy - .put(`/bff/v1/beacon_malfunctions/${id}`, { - json: updatedFields - }) - .json() - } catch (err) { - throw new FrontendApiError(UPDATE_BEACON_MALFUNCTIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get a beacon malfunction - * - * @throws {@link FrontendApiError} - */ -async function getBeaconMalfunctionFromAPI(id: number): Promise { - try { - return await monitorfishApiKy.get(`/bff/v1/beacon_malfunctions/${id}`).json() - } catch (err) { - throw new FrontendApiError(GET_BEACON_MALFUNCTION_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Save a new comment attached to a beacon malfunction - * - * @throws {@link FrontendApiError} - */ -async function saveBeaconMalfunctionCommentFromAPI( - id: number, - comment: { comment: string; userType: keyof typeof UserType } -): Promise { - try { - return await monitorfishApiKy - .post(`/bff/v1/beacon_malfunctions/${id}/comments`, { - json: comment - }) - .json() - } catch (err) { - throw new FrontendApiError(SAVE_BEACON_MALFUNCTION_COMMENT_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get vessel beacon malfunctions - * - * @throws {@link FrontendApiError} - */ -async function getVesselBeaconsMalfunctionsFromAPI( - vesselId: Vessel.VesselId, - fromDate: Date -): Promise { - try { - return await monitorfishApiKy - .get(`/bff/v1/vessels/beacon_malfunctions?vesselId=${vesselId}&afterDateTime=${fromDate.toISOString()}`) - .json() - } catch (err) { - throw new FrontendApiError(GET_VESSEL_BEACON_MALFUNCTIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Send a notification - Update the request notification column to asynchronously send the message - * - * @throws {@link FrontendApiError} - */ -async function sendNotificationFromAPI( - id: number, - notificationType: keyof typeof NOTIFICATION_TYPE, - foreignFmcCode?: string -): Promise { - try { - await monitorfishApiKy.put( - `/bff/v1/beacon_malfunctions/${id}/${notificationType}?requestedNotificationForeignFmcCode=${foreignFmcCode}` - ) - } catch (err) { - throw new FrontendApiError(SEND_NOTIFICATION_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -export { - getVesselBeaconsMalfunctionsFromAPI, - saveBeaconMalfunctionCommentFromAPI, - getBeaconMalfunctionFromAPI, - updateBeaconMalfunctionFromAPI, - getAllBeaconMalfunctionsFromAPI, - sendNotificationFromAPI -} diff --git a/frontend/src/api/reporting.ts b/frontend/src/api/reporting.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.ts b/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.ts deleted file mode 100644 index d2bd482bae..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getAllBeaconMalfunctionsFromAPI } from '@api/beaconMalfunction' -import { setBeaconMalfunctions } from 'domain/shared_slices/BeaconMalfunction' -import { setError } from 'domain/shared_slices/Global' - -import type { MainAppThunk } from '@store' - -export const getAllBeaconMalfunctions = (): MainAppThunk> => async dispatch => { - try { - const beaconMalfunctions = await getAllBeaconMalfunctionsFromAPI() - - dispatch(setBeaconMalfunctions(beaconMalfunctions)) - } catch (err) { - console.error(err) - dispatch(setError(err)) - } -} diff --git a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js deleted file mode 100644 index 23fb7dab1b..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js +++ /dev/null @@ -1,39 +0,0 @@ -import { getBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' -import { setOpenedBeaconMalfunction } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' - -/** - * Open a single beacon malfunction - * @function openBeaconMalfunction - * @param {BeaconMalfunctionResumeAndDetails} beaconMalfunction - the beacon malfunction to open - * @param {boolean} isFromUserAction - if the use case is called from the API Worker - */ -export const openBeaconMalfunction = (beaconMalfunction, isFromUserAction) => (dispatch, getState) => { - const previousBeaconMalfunction = getState().beaconMalfunction.openedBeaconMalfunction - dispatch( - setOpenedBeaconMalfunction({ - beaconMalfunction, - showTab: isFromUserAction - }) - ) - - getBeaconMalfunctionFromAPI(beaconMalfunction.beaconMalfunction?.id) - .then(beaconMalfunctionWithDetails => { - dispatch( - setOpenedBeaconMalfunction({ - beaconMalfunction: beaconMalfunctionWithDetails, - showTab: isFromUserAction - }) - ) - }) - .catch(error => { - console.error(error) - dispatch(setError(error)) - dispatch( - setOpenedBeaconMalfunction({ - beaconMalfunction: previousBeaconMalfunction, - showTab: isFromUserAction - }) - ) - }) -} diff --git a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts deleted file mode 100644 index 634b9eedac..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' -import { setOpenedBeaconMalfunctionsInKanban } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' - -/** - * Open a single beacon malfunction - */ -export const openBeaconMalfunctionInKanban = id => dispatch => { - getBeaconMalfunctionFromAPI(id) - .then(beaconMalfunctionWithDetails => { - dispatch(setOpenedBeaconMalfunctionsInKanban(beaconMalfunctionWithDetails)) - }) - .catch(error => { - dispatch(setError(error)) - }) -} diff --git a/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js b/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js deleted file mode 100644 index eba68fb588..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js +++ /dev/null @@ -1,50 +0,0 @@ -import { saveBeaconMalfunctionCommentFromAPI } from '../../../api/beaconMalfunction' -import { - setOpenedBeaconMalfunction, - setOpenedBeaconMalfunctionsInKanban, - updateVesselBeaconMalfunctionsResumeAndHistory -} from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' - -/** - * Save a new comment to a beacon malfunction - * @function showVesselTrack - * @param {number} beaconMalfunctionId - * @param {string} comment - */ -export const saveBeaconMalfunctionCommentFromKanban = (beaconMalfunctionId, comment) => (dispatch, getState) => { - const { userType } = getState().global - const newCommentInput = { - comment, - userType - } - const beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction = - getState().beaconMalfunction.vesselBeaconMalfunctionsResumeAndHistory?.current?.beaconMalfunction?.id === - beaconMalfunctionId - const beaconMalfunctionToUpdateIsOpened = - getState().beaconMalfunction.openedBeaconMalfunction?.beaconMalfunction?.id === beaconMalfunctionId - const beaconMalfunctionToUpdateIsOpenedInKanban = - getState().beaconMalfunction.openedBeaconMalfunctionInKanban?.beaconMalfunction?.id === beaconMalfunctionId - - return saveBeaconMalfunctionCommentFromAPI(beaconMalfunctionId, newCommentInput) - .then(beaconMalfunctionWithDetails => { - if (beaconMalfunctionToUpdateIsOpened) { - dispatch( - setOpenedBeaconMalfunction({ - beaconMalfunction: beaconMalfunctionWithDetails, - showTab: false - }) - ) - } - if (beaconMalfunctionToUpdateIsOpenedInKanban) { - dispatch(setOpenedBeaconMalfunctionsInKanban(beaconMalfunctionWithDetails)) - } - if (beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction) { - dispatch(updateVesselBeaconMalfunctionsResumeAndHistory(beaconMalfunctionWithDetails)) - } - }) - .catch(error => { - console.error(error) - dispatch(setError(error)) - }) -} diff --git a/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts b/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts deleted file mode 100644 index 8e54db9466..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { NOTIFICATION_TYPE } from '@features/BeaconMalfunction/constants' - -import { sendNotificationFromAPI } from '../../../api/beaconMalfunction' -import { setError } from '../../shared_slices/Global' - -/** - * Send a notification message - */ -export const sendNotification = - (beaconMalfunctionId: number, notificationType: string | null, foreignFmcCode?: string) => - (dispatch): Promise => { - if (!notificationType || !Object.keys(NOTIFICATION_TYPE).find(type => type === notificationType)) { - return Promise.resolve() - } - - return sendNotificationFromAPI( - beaconMalfunctionId, - notificationType as keyof typeof NOTIFICATION_TYPE, - foreignFmcCode - ) - .then(() => notificationType) - .catch(error => { - dispatch(setError(error)) - }) - } diff --git a/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js b/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js deleted file mode 100644 index 1ffcf24f61..0000000000 --- a/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js +++ /dev/null @@ -1,48 +0,0 @@ -import { updateBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' -import { - setBeaconMalfunctions, - updateLocalBeaconMalfunction, - setOpenedBeaconMalfunctionsInKanban, - setOpenedBeaconMalfunction, - updateVesselBeaconMalfunctionsResumeAndHistory -} from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' - -/** - * Update a beacon malfunction - * @param {number} id - The id of the beacon malfunction - * @param {BeaconMalfunction} nextBeaconMalfunction - The next beacon malfunction - * @param {UpdateBeaconMalfunction} updatedFields - The fields to update - */ -export const updateBeaconMalfunctionFromKanban = (id, nextBeaconMalfunction, updatedFields) => (dispatch, getState) => { - const previousBeaconMalfunctions = getState().beaconMalfunction.beaconMalfunctions - dispatch(updateLocalBeaconMalfunction(nextBeaconMalfunction)) - const beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction = - getState().beaconMalfunction.vesselBeaconMalfunctionsResumeAndHistory?.current?.beaconMalfunction?.id === id - const beaconMalfunctionToUpdateIsOpened = - getState().beaconMalfunction.openedBeaconMalfunction?.beaconMalfunction?.id === id - const beaconMalfunctionToUpdateIsOpenedInKanban = - getState().beaconMalfunction.openedBeaconMalfunctionInKanban?.beaconMalfunction?.id === id - - return updateBeaconMalfunctionFromAPI(id, updatedFields) - .then(updatedBeaconMalfunctionWithDetails => { - if (beaconMalfunctionToUpdateIsOpened) { - dispatch( - setOpenedBeaconMalfunction({ - beaconMalfunction: updatedBeaconMalfunctionWithDetails, - showTab: false - }) - ) - } - if (beaconMalfunctionToUpdateIsOpenedInKanban) { - dispatch(setOpenedBeaconMalfunctionsInKanban(updatedBeaconMalfunctionWithDetails)) - } - if (beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction) { - dispatch(updateVesselBeaconMalfunctionsResumeAndHistory(updatedBeaconMalfunctionWithDetails)) - } - }) - .catch(error => { - dispatch(setError(error)) - dispatch(setBeaconMalfunctions(previousBeaconMalfunctions)) - }) -} diff --git a/frontend/src/features/BeaconMalfunction/__tests__/getMalfunctionStartDateText.test.ts b/frontend/src/features/BeaconMalfunction/__tests__/getMalfunctionStartDateText.test.ts index 0c10f891a6..8355592042 100644 --- a/frontend/src/features/BeaconMalfunction/__tests__/getMalfunctionStartDateText.test.ts +++ b/frontend/src/features/BeaconMalfunction/__tests__/getMalfunctionStartDateText.test.ts @@ -13,10 +13,10 @@ describe('domain/entities/beaconMalfunction/index.getMalfunctionStartDateText()' id: 5, internalReferenceNumber: 'FR263465414', ircs: 'IR123', - malfunctionEndDateTime: null, + malfunctionEndDateTime: undefined, malfunctionStartDateTime: '2023-08-21T11:17:22.997231Z', - notificationRequested: null, - riskFactor: null, + notificationRequested: undefined, + riskFactor: undefined, stage: 'ARCHIVED', vesselId: 12, vesselIdentifier: 'EXTERNAL_REFERENCE_NUMBER', diff --git a/frontend/src/features/BeaconMalfunction/apis.ts b/frontend/src/features/BeaconMalfunction/apis.ts new file mode 100644 index 0000000000..e4eeb85c3f --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/apis.ts @@ -0,0 +1,93 @@ +import { monitorfishApi } from '@api/api' +import { FrontendApiError } from '@libs/FrontendApiError' + +import type { NOTIFICATION_TYPE, UserType } from '@features/BeaconMalfunction/constants' +import type { + BeaconMalfunction, + BeaconMalfunctionResumeAndDetails, + UpdateBeaconMalfunction, + VesselBeaconMalfunctionsResumeAndHistory +} from '@features/BeaconMalfunction/types' +import type { Vessel } from '@features/Vessel/Vessel.types' + +export const ARCHIVE_BEACON_MALFUNCTION = "Nous n'avons pas pu archiver les avaries VMS" +export const GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les avaries VMS" +export const GET_BEACON_MALFUNCTION_ERROR_MESSAGE = "Nous n'avons pas pu récupérer l'avarie VMS" +export const UPDATE_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu mettre à jour le statut de l'avarie VMS" +export const SAVE_BEACON_MALFUNCTION_COMMENT_ERROR_MESSAGE = + "Nous n'avons pas pu ajouter le commentaire sur l'avarie VMS" +export const GET_VESSEL_BEACON_MALFUNCTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les avaries de ce navire" +export const SEND_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu envoyer la notification" + +export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ + endpoints: builder => ({ + getAllBeaconMalfunctions: builder.query({ + query: () => ({ + method: 'GET', + url: '/bff/v1/beacon_malfunctions' + }), + transformErrorResponse: response => new FrontendApiError(GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) + }), + + getBeaconMalfunction: builder.query({ + query: id => ({ + method: 'GET', + url: `/bff/v1/beacon_malfunctions/${id}` + }), + transformErrorResponse: response => new FrontendApiError(GET_BEACON_MALFUNCTION_ERROR_MESSAGE, response) + }), + + getVesselBeaconsMalfunctions: builder.query< + VesselBeaconMalfunctionsResumeAndHistory, + { fromDate: Date; vesselId: Vessel.VesselId } + >({ + query: ({ fromDate, vesselId }) => ({ + method: 'GET', + params: { + afterDateTime: fromDate.toISOString(), + vesselId + }, + url: '/bff/v1/vessels/beacon_malfunctions' + }), + transformErrorResponse: response => new FrontendApiError(GET_VESSEL_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) + }), + + saveBeaconMalfunctionComment: builder.mutation< + BeaconMalfunctionResumeAndDetails, + { comment: { comment: string; userType: keyof typeof UserType }; id: number } + >({ + query: ({ comment, id }) => ({ + body: comment, + method: 'POST', + url: `/bff/v1/beacon_malfunctions/${id}/comments` + }), + transformErrorResponse: response => new FrontendApiError(SAVE_BEACON_MALFUNCTION_COMMENT_ERROR_MESSAGE, response) + }), + + sendNotification: builder.mutation< + void, + { foreignFmcCode: string | undefined; id: number; notificationType: keyof typeof NOTIFICATION_TYPE } + >({ + query: ({ foreignFmcCode, id, notificationType }) => ({ + method: 'PUT', + params: { + requestedNotificationForeignFmcCode: foreignFmcCode + }, + url: `/bff/v1/beacon_malfunctions/${id}/${notificationType}` + }), + transformErrorResponse: response => new FrontendApiError(SEND_NOTIFICATION_ERROR_MESSAGE, response) + }), + + updateBeaconMalfunction: builder.mutation< + BeaconMalfunctionResumeAndDetails, + { id: number; updatedFields: UpdateBeaconMalfunction } + >({ + query: ({ id, updatedFields }) => ({ + body: updatedFields, + method: 'PUT', + url: `/bff/v1/beacon_malfunctions/${id}` + }), + transformErrorResponse: response => new FrontendApiError(UPDATE_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) + }) + }) +}) diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionCard.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionCard.tsx index a681d4ea97..86f3b93f48 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionCard.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionCard.tsx @@ -5,9 +5,9 @@ import styled from 'styled-components' import { getBeaconCreationOrModificationDate } from './utils' import { VesselStatusSelect } from './VesselStatusSelect' -import { openBeaconMalfunctionInKanban } from '../../../../domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban' import { showVesselFromBeaconMalfunctionsKanban } from '../../../../domain/use_cases/vessel/showVesselFromBeaconMalfunctionsKanban' import { END_OF_MALFUNCTION_REASON_RECORD, VESSEL_STATUS } from '../../constants' +import { openBeaconMalfunctionInKanban } from '../../useCases/openBeaconMalfunctionInKanban' import { getMalfunctionStartDateText } from '../../utils' import type { BeaconMalfunction } from '../../types' @@ -20,7 +20,7 @@ export type BeaconMalfunctionCardProps = { isDragging: boolean isDroppedId: boolean | undefined isShowed: boolean - updateVesselStatus: (beaconMalfunction: BeaconMalfunction | undefined, status: string | null) => void + updateVesselStatus: (beaconMalfunction: BeaconMalfunction | undefined, status: string) => void verticalScrollRef: MutableRefObject | undefined } @@ -42,10 +42,13 @@ export function BeaconMalfunctionCard({ ? verticalScrollRef?.current?.scrollHeight > verticalScrollRef?.current?.clientHeight : false - const endOfBeaconMalfunctionReason = useMemo( - () => END_OF_MALFUNCTION_REASON_RECORD[beaconMalfunction?.endOfBeaconMalfunctionReason], - [beaconMalfunction] - ) + const endOfBeaconMalfunctionReason = useMemo(() => { + if (beaconMalfunction?.endOfBeaconMalfunctionReason) { + return END_OF_MALFUNCTION_REASON_RECORD[beaconMalfunction?.endOfBeaconMalfunctionReason] + } + + return undefined + }, [beaconMalfunction]) useEffect(() => { if (isShowed && beaconMalfunction && wrapperRef.current) { diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx index f657328d81..9063267bcf 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx @@ -12,10 +12,10 @@ import { BeaconMalfunctionDetailsFollowUpItem } from './BeaconMalfunctionDetails import { BeaconMalfunctionDetailsFollowUpRow } from './BeaconMalfunctionDetailsFollowUpRow' import { BeaconMalfunctionDetailsType, getContent } from './utils' import { setUserType } from '../../../../domain/shared_slices/Global' -import { saveBeaconMalfunctionCommentFromKanban } from '../../../../domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban' import { getDate, mergeObjects } from '../../../../utils' import CommentsSVG from '../../../icons/Commentaires.svg?react' import { UserType, VESSEL_STATUS } from '../../constants' +import { saveBeaconMalfunctionCommentFromKanban } from '../../useCases/saveBeaconMalfunctionCommentFromKanban' import type { BeaconMalfunctionFollowUpItem, BeaconMalfunctionStatusValue } from '../../types' import type { CSSProperties } from 'react' diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/SendNotification.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/SendNotification.tsx index c1280b93e1..4670a939c2 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/SendNotification.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/SendNotification.tsx @@ -5,8 +5,8 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { SelectPicker } from 'rsuite' import styled from 'styled-components' -import { sendNotification } from '../../../../domain/use_cases/beaconMalfunction/sendNotification' import { NOTIFICATION_TYPE, SELECTABLE_NOTIFICATION_TYPES } from '../../constants' +import { sendNotification } from '../../useCases/sendNotification' import type { CSSProperties } from 'react' diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/VesselStatusSelect.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/VesselStatusSelect.tsx index 6c8989a342..0cd1868dad 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/VesselStatusSelect.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/VesselStatusSelect.tsx @@ -13,7 +13,7 @@ type VesselStatusSelectProps = { isAbsolute?: boolean isCleanable?: boolean marginTop?: number | undefined - updateVesselStatus: (beaconMalfunction: BeaconMalfunction | undefined, status: string | null) => void + updateVesselStatus: (beaconMalfunction: BeaconMalfunction | undefined, status: string) => void // TODO Type vesselStatus in constants.tsx vesselStatus: { color: string; icon: JSX.Element; label: string; textColor: string; value: string } | undefined } @@ -61,7 +61,7 @@ export function VesselStatusSelect({ ? { marginLeft: 40, marginTop: 120, position: 'absolute' } : { marginLeft: -5, marginTop: marginTop ?? -67, position: 'relative' } } - onChange={status => updateVesselStatus(beaconMalfunction, status)} + onChange={status => updateVesselStatus(beaconMalfunction, status as string)} placeholder="Statut" renderValue={(_, item) => } searchable={false} diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/index.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/index.tsx index b8ab5d364a..c68d4fd47a 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/index.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/index.tsx @@ -8,7 +8,12 @@ import { useSensor, useSensors } from '@dnd-kit/core' -import { STAGE_RECORD, VESSEL_STATUS } from '@features/BeaconMalfunction/constants' +import { + BeaconMalfunctionsStage, + BeaconMalfunctionVesselStatus, + STAGE_RECORD, + VESSEL_STATUS +} from '@features/BeaconMalfunction/constants' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { THEME } from '@mtes-mct/monitor-ui' @@ -23,10 +28,10 @@ import { StageColumn } from './StageColumn' import { getBeaconMalfunctionsByStage, searchInBeaconMalfunctions } from './utils' import { VesselStatusSelect } from './VesselStatusSelect' import { setError } from '../../../../domain/shared_slices/Global' -import { getAllBeaconMalfunctions } from '../../../../domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions' -import { updateBeaconMalfunctionFromKanban } from '../../../../domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban' import { LegacyRsuiteComponentsWrapper } from '../../../../ui/LegacyRsuiteComponentsWrapper' import SearchIconSVG from '../../../icons/Loupe_dark.svg?react' +import { getAllBeaconMalfunctions } from '../../useCases/getAllBeaconMalfunctions' +import { updateBeaconMalfunctionFromKanban } from '../../useCases/updateBeaconMalfunctionFromKanban' import type { BeaconMalfunction, @@ -125,22 +130,22 @@ export function BeaconMalfunctionBoard() { ) const updateVesselStatus = useCallback( - (beaconMalfunction: BeaconMalfunction | undefined, status: string | null) => { + (beaconMalfunction: BeaconMalfunction | undefined, status: string) => { if (!beaconMalfunction) { return } const nextBeaconMalfunction = { ...beaconMalfunction, - vesselStatus: status, + vesselStatus: status as BeaconMalfunctionVesselStatus, vesselStatusLastModificationDateTime: new Date().toISOString() } setIsDroppedId(beaconMalfunction.id) dispatch( - // @ts-ignore updateBeaconMalfunctionFromKanban(beaconMalfunction.id, nextBeaconMalfunction, { - vesselStatus: nextBeaconMalfunction.vesselStatus + stage: nextBeaconMalfunction.stage as BeaconMalfunctionsStage | undefined, + vesselStatus: nextBeaconMalfunction.vesselStatus as BeaconMalfunctionVesselStatus | undefined }) ) }, diff --git a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/utils.tsx b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/utils.tsx index 5901170774..d147bc029b 100644 --- a/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/utils.tsx +++ b/frontend/src/features/BeaconMalfunction/components/BeaconMalfunctionBoard/utils.tsx @@ -92,7 +92,7 @@ const getFirstStatusAction = (vesselStatus: BeaconMalfunctionStatusValue, malfun export function getActionText( action: BeaconMalfunctionAction, - endOfBeaconMalfunctionReason: EndOfBeaconMalfunctionReason | null + endOfBeaconMalfunctionReason: EndOfBeaconMalfunctionReason | undefined ) { switch (action.propertyName) { case BeaconMalfunctionPropertyName.VESSEL_STATUS: { diff --git a/frontend/src/features/BeaconMalfunction/types.ts b/frontend/src/features/BeaconMalfunction/types.ts index 915729a085..3c490e90eb 100644 --- a/frontend/src/features/BeaconMalfunction/types.ts +++ b/frontend/src/features/BeaconMalfunction/types.ts @@ -9,15 +9,15 @@ import type { Vessel } from '@features/Vessel/Vessel.types' import type { Integer } from 'type-fest' export type BeaconMalfunction = { - endOfBeaconMalfunctionReason: EndOfBeaconMalfunctionReason + endOfBeaconMalfunctionReason: EndOfBeaconMalfunctionReason | undefined externalReferenceNumber: string flagState: string id: number internalReferenceNumber: string ircs: string - malfunctionEndDateTime: string | null + malfunctionEndDateTime: string | undefined malfunctionStartDateTime: string - notificationRequested: string | null + notificationRequested: string | undefined stage: string vesselIdentifier: string vesselName: string @@ -26,8 +26,8 @@ export type BeaconMalfunction = { } export type UpdateBeaconMalfunction = { - stage: BeaconMalfunctionsStage | null - vesselStatus: BeaconMalfunctionVesselStatus | null + stage: BeaconMalfunctionsStage | undefined + vesselStatus: BeaconMalfunctionVesselStatus | undefined } export type BeaconMalfunctionComment = { @@ -58,13 +58,13 @@ export type BeaconMalfunctionNotification = { beaconMalfunctionId: Integer communicationMeans: string dateTime: string - errorMessage: string | null + errorMessage: string | undefined id: number notificationType: string recipientAddressOrNumber: string recipientFunction: string recipientName: string - success: boolean | null + success: boolean | undefined } export type BeaconMalfunctionNotifications = { @@ -83,15 +83,15 @@ export type BeaconMalfunctionResumeAndDetails = { } export type VesselBeaconMalfunctionsResumeAndHistory = { - current: BeaconMalfunctionResumeAndDetails | null + current: BeaconMalfunctionResumeAndDetails | undefined history: BeaconMalfunctionResumeAndDetails[] resume: VesselBeaconMalfunctionsResume vesselIdentity: Vessel.VesselIdentity } export type VesselBeaconMalfunctionsResume = { - lastBeaconMalfunctionDateTime: string | null - lastBeaconMalfunctionVesselStatus: string | null + lastBeaconMalfunctionDateTime: string | undefined + lastBeaconMalfunctionVesselStatus: string | undefined numberOfBeaconsAtPort: number numberOfBeaconsAtSea: number } diff --git a/frontend/src/features/BeaconMalfunction/useCases/getAllBeaconMalfunctions.ts b/frontend/src/features/BeaconMalfunction/useCases/getAllBeaconMalfunctions.ts new file mode 100644 index 0000000000..93d8f9597f --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/getAllBeaconMalfunctions.ts @@ -0,0 +1,19 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' + +import { setBeaconMalfunctions } from '../../../domain/shared_slices/BeaconMalfunction' +import { setError } from '../../../domain/shared_slices/Global' + +import type { MainAppThunk } from '@store' + +export const getAllBeaconMalfunctions = (): MainAppThunk> => async dispatch => { + try { + const beaconMalfunctions = await dispatch( + beaconMalfunctionApi.endpoints.getAllBeaconMalfunctions.initiate(undefined, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + + dispatch(setBeaconMalfunctions(beaconMalfunctions)) + } catch (err) { + dispatch(setError(err)) + } +} diff --git a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts b/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts similarity index 69% rename from frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts rename to frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts index ffe8bd934f..06c65b0894 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts +++ b/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts @@ -1,16 +1,17 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' import { extractVesselIdentityProps } from '@features/Vessel/utils' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { openBeaconMalfunction } from './openBeaconMalfunction' -import { getVesselBeaconsMalfunctionsFromAPI } from '../../../api/beaconMalfunction' import { loadVesselBeaconMalfunctions, resetVesselBeaconMalfunctionsResumeAndHistory, setVesselBeaconMalfunctionsResumeAndHistory -} from '../../shared_slices/BeaconMalfunction' -import { displayedErrorActions } from '../../shared_slices/DisplayedError' -import { removeError } from '../../shared_slices/Global' -import { displayOrLogError } from '../error/displayOrLogError' +} from '../../../domain/shared_slices/BeaconMalfunction' +import { displayedErrorActions } from '../../../domain/shared_slices/DisplayedError' +import { removeError } from '../../../domain/shared_slices/Global' +import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' export const getVesselBeaconMalfunctions = (isFromUserAction: boolean) => async (dispatch, getState) => { const { selectedVessel } = getState().vessel @@ -30,10 +31,13 @@ export const getVesselBeaconMalfunctions = (isFromUserAction: boolean) => async } try { - const vesselBeaconsMalfunctions = await getVesselBeaconsMalfunctionsFromAPI( - selectedVessel.vesselId, - vesselBeaconMalfunctionsFromDate - ) + const vesselBeaconsMalfunctions = await dispatch( + beaconMalfunctionApi.endpoints.getVesselBeaconsMalfunctions.initiate( + { fromDate: vesselBeaconMalfunctionsFromDate, vesselId: selectedVessel.vesselId }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + )().unwrap() + dispatch( setVesselBeaconMalfunctionsResumeAndHistory({ ...vesselBeaconsMalfunctions, diff --git a/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunction.ts b/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunction.ts new file mode 100644 index 0000000000..8f8c06aa70 --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunction.ts @@ -0,0 +1,45 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' + +import { setOpenedBeaconMalfunction } from '../../../domain/shared_slices/BeaconMalfunction' +import { setError } from '../../../domain/shared_slices/Global' + +import type { BeaconMalfunctionResumeAndDetails } from '@features/BeaconMalfunction/types' +import type { MainAppThunk } from '@store' + +export const openBeaconMalfunction = + (beaconMalfunction: BeaconMalfunctionResumeAndDetails, isFromUserAction: boolean): MainAppThunk> => + async (dispatch, getState) => { + const previousBeaconMalfunction = getState().beaconMalfunction.openedBeaconMalfunction + + dispatch( + setOpenedBeaconMalfunction({ + beaconMalfunction, + showTab: isFromUserAction + }) + ) + + try { + const beaconMalfunctionWithDetails = await dispatch( + beaconMalfunctionApi.endpoints.getBeaconMalfunction.initiate( + beaconMalfunction.beaconMalfunction?.id as number, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() + + dispatch( + setOpenedBeaconMalfunction({ + beaconMalfunction: beaconMalfunctionWithDetails, + showTab: isFromUserAction + }) + ) + } catch (error) { + dispatch(setError(error)) + dispatch( + setOpenedBeaconMalfunction({ + beaconMalfunction: previousBeaconMalfunction, + showTab: isFromUserAction + }) + ) + } + } diff --git a/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban.ts b/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban.ts new file mode 100644 index 0000000000..377bbdfc7d --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban.ts @@ -0,0 +1,21 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' + +import { setOpenedBeaconMalfunctionsInKanban } from '../../../domain/shared_slices/BeaconMalfunction' +import { setError } from '../../../domain/shared_slices/Global' + +import type { MainAppThunk } from '@store' + +export const openBeaconMalfunctionInKanban = + (id: number): MainAppThunk> => + async dispatch => { + try { + const beaconMalfunctionWithDetails = await dispatch( + beaconMalfunctionApi.endpoints.getBeaconMalfunction.initiate(id, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + + dispatch(setOpenedBeaconMalfunctionsInKanban(beaconMalfunctionWithDetails)) + } catch (error) { + dispatch(setError(error)) + } + } diff --git a/frontend/src/features/BeaconMalfunction/useCases/saveBeaconMalfunctionCommentFromKanban.ts b/frontend/src/features/BeaconMalfunction/useCases/saveBeaconMalfunctionCommentFromKanban.ts new file mode 100644 index 0000000000..f8646c9d86 --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/saveBeaconMalfunctionCommentFromKanban.ts @@ -0,0 +1,59 @@ +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' + +import { + setOpenedBeaconMalfunction, + setOpenedBeaconMalfunctionsInKanban, + updateVesselBeaconMalfunctionsResumeAndHistory +} from '../../../domain/shared_slices/BeaconMalfunction' +import { setError } from '../../../domain/shared_slices/Global' + +import type { UserType } from '@features/BeaconMalfunction/constants' +import type { MainAppThunk } from '@store' + +export const saveBeaconMalfunctionCommentFromKanban = + (beaconMalfunctionId: number, comment: string): MainAppThunk> => + async (dispatch, getState) => { + const state = getState() + const { userType } = getState().global + + const newCommentInput = { + comment, + userType: userType as keyof typeof UserType + } + + const beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction = + state.beaconMalfunction.vesselBeaconMalfunctionsResumeAndHistory?.current?.beaconMalfunction?.id === + beaconMalfunctionId + const beaconMalfunctionToUpdateIsOpened = + state.beaconMalfunction.openedBeaconMalfunction?.beaconMalfunction?.id === beaconMalfunctionId + const beaconMalfunctionToUpdateIsOpenedInKanban = + state.beaconMalfunction.openedBeaconMalfunctionInKanban?.beaconMalfunction?.id === beaconMalfunctionId + + try { + const beaconMalfunctionWithDetails = await dispatch( + beaconMalfunctionApi.endpoints.saveBeaconMalfunctionComment.initiate({ + comment: newCommentInput, + id: beaconMalfunctionId + }) + ).unwrap() + + if (beaconMalfunctionToUpdateIsOpened) { + dispatch( + setOpenedBeaconMalfunction({ + beaconMalfunction: beaconMalfunctionWithDetails, + showTab: false + }) + ) + } + // If the malfunction is open in the Kanban view, update that slice. + if (beaconMalfunctionToUpdateIsOpenedInKanban) { + dispatch(setOpenedBeaconMalfunctionsInKanban(beaconMalfunctionWithDetails)) + } + // If the malfunction is the current vessel malfunction, update that slice. + if (beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction) { + dispatch(updateVesselBeaconMalfunctionsResumeAndHistory(beaconMalfunctionWithDetails)) + } + } catch (error) { + dispatch(setError(error)) + } + } diff --git a/frontend/src/features/BeaconMalfunction/useCases/sendNotification.ts b/frontend/src/features/BeaconMalfunction/useCases/sendNotification.ts new file mode 100644 index 0000000000..441b252edf --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/sendNotification.ts @@ -0,0 +1,34 @@ +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' +import { NOTIFICATION_TYPE } from '@features/BeaconMalfunction/constants' + +import { setError } from '../../../domain/shared_slices/Global' + +import type { MainAppThunk } from '@store' + +export const sendNotification = + ( + beaconMalfunctionId: number, + notificationType: string | null, + foreignFmcCode?: string + ): MainAppThunk> => + async dispatch => { + if (!notificationType || !Object.keys(NOTIFICATION_TYPE).includes(notificationType)) { + return Promise.resolve() + } + + try { + await dispatch( + beaconMalfunctionApi.endpoints.sendNotification.initiate({ + foreignFmcCode, + id: beaconMalfunctionId, + notificationType: notificationType as keyof typeof NOTIFICATION_TYPE + }) + ).unwrap() + + return notificationType + } catch (error) { + dispatch(setError(error)) + + return undefined + } + } diff --git a/frontend/src/features/BeaconMalfunction/useCases/updateBeaconMalfunctionFromKanban.ts b/frontend/src/features/BeaconMalfunction/useCases/updateBeaconMalfunctionFromKanban.ts new file mode 100644 index 0000000000..2771f8395e --- /dev/null +++ b/frontend/src/features/BeaconMalfunction/useCases/updateBeaconMalfunctionFromKanban.ts @@ -0,0 +1,62 @@ +import { beaconMalfunctionApi } from '@features/BeaconMalfunction/apis' + +import { + setBeaconMalfunctions, + setOpenedBeaconMalfunction, + setOpenedBeaconMalfunctionsInKanban, + updateLocalBeaconMalfunction, + updateVesselBeaconMalfunctionsResumeAndHistory +} from '../../../domain/shared_slices/BeaconMalfunction' +import { setError } from '../../../domain/shared_slices/Global' + +import type { BeaconMalfunction, UpdateBeaconMalfunction } from '@features/BeaconMalfunction/types' +import type { MainAppThunk } from '@store' + +export const updateBeaconMalfunctionFromKanban = + ( + id: number, + nextBeaconMalfunction: BeaconMalfunction, + updatedFields: UpdateBeaconMalfunction + ): MainAppThunk> => + async (dispatch, getState) => { + // Save the current beacon malfunctions for potential rollback. + const previousBeaconMalfunctions = getState().beaconMalfunction.beaconMalfunctions + + // Perform an optimistic update in the local state. + dispatch(updateLocalBeaconMalfunction(nextBeaconMalfunction)) + + const state = getState() + const beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction = + state.beaconMalfunction.vesselBeaconMalfunctionsResumeAndHistory?.current?.beaconMalfunction?.id === id + const beaconMalfunctionToUpdateIsOpened = + state.beaconMalfunction.openedBeaconMalfunction?.beaconMalfunction?.id === id + const beaconMalfunctionToUpdateIsOpenedInKanban = + state.beaconMalfunction.openedBeaconMalfunctionInKanban?.beaconMalfunction?.id === id + + try { + const updatedBeaconMalfunctionWithDetails = await dispatch( + beaconMalfunctionApi.endpoints.updateBeaconMalfunction.initiate({ + id, + updatedFields + }) + ).unwrap() + + if (beaconMalfunctionToUpdateIsOpened) { + dispatch( + setOpenedBeaconMalfunction({ + beaconMalfunction: updatedBeaconMalfunctionWithDetails, + showTab: false + }) + ) + } + if (beaconMalfunctionToUpdateIsOpenedInKanban) { + dispatch(setOpenedBeaconMalfunctionsInKanban(updatedBeaconMalfunctionWithDetails)) + } + if (beaconMalfunctionToUpdateIsOpenedAsCurrentVesselMalfunction) { + dispatch(updateVesselBeaconMalfunctionsResumeAndHistory(updatedBeaconMalfunctionWithDetails)) + } + } catch (error) { + dispatch(setError(error)) + dispatch(setBeaconMalfunctions(previousBeaconMalfunctions)) + } + } diff --git a/frontend/src/features/SideWindow/index.tsx b/frontend/src/features/SideWindow/index.tsx index 0fe53838ae..13a477b314 100644 --- a/frontend/src/features/SideWindow/index.tsx +++ b/frontend/src/features/SideWindow/index.tsx @@ -27,13 +27,13 @@ import { SideWindowMenuKey } from '../../domain/entities/sideWindow/constants' import { closeBeaconMalfunctionInKanban } from '../../domain/shared_slices/BeaconMalfunction' import { getOperationalAlerts } from '../../domain/use_cases/alert/getOperationalAlerts' import { getSilencedAlerts } from '../../domain/use_cases/alert/getSilencedAlerts' -import { getAllBeaconMalfunctions } from '../../domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions' import { getAllGearCodes } from '../../domain/use_cases/gearCode/getAllGearCodes' import { getInfractions } from '../../domain/use_cases/infraction/getInfractions' import { useMainAppDispatch } from '../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../hooks/useMainAppSelector' import { SideWindowAlerts } from '../Alert/components/SideWindowAlerts' import { BeaconMalfunctionBoard } from '../BeaconMalfunction/components/BeaconMalfunctionBoard' +import { getAllBeaconMalfunctions } from '../BeaconMalfunction/useCases/getAllBeaconMalfunctions' import { Loader as MissionFormLoader } from '../Mission/components/MissionForm/Loader' import { MissionList } from '../Mission/components/MissionList' import { PriorNotificationList } from '../PriorNotification/components/PriorNotificationList' diff --git a/frontend/src/features/Vessel/components/VesselSidebar/Equipment/VesselEquipment.tsx b/frontend/src/features/Vessel/components/VesselSidebar/Equipment/VesselEquipment.tsx index 45f8f4d1de..7e6edd889f 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/Equipment/VesselEquipment.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/Equipment/VesselEquipment.tsx @@ -9,10 +9,10 @@ import { useIsSuperUser } from '../../../../../auth/hooks/useIsSuperUser' import { COLORS } from '../../../../../constants/constants' import { vesselsAreEquals } from '../../../../../domain/entities/vessel/vessel' import { setBeaconMalfunctionsTab } from '../../../../../domain/shared_slices/BeaconMalfunction' -import { getVesselBeaconMalfunctions } from '../../../../../domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions' import { useMainAppDispatch } from '../../../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../../../hooks/useMainAppSelector' import { EquipmentTab } from '../../../../BeaconMalfunction/constants' +import { getVesselBeaconMalfunctions } from '../../../../BeaconMalfunction/useCases/getVesselBeaconMalfunctions' export function VesselEquipment() { const dispatch = useMainAppDispatch() diff --git a/frontend/src/features/Vessel/components/VesselSidebar/Equipment/resume/CurrentBeaconMalfunctionBody.tsx b/frontend/src/features/Vessel/components/VesselSidebar/Equipment/resume/CurrentBeaconMalfunctionBody.tsx index 59d44787d7..86c98804a6 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/Equipment/resume/CurrentBeaconMalfunctionBody.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/Equipment/resume/CurrentBeaconMalfunctionBody.tsx @@ -4,9 +4,9 @@ import { SelectPicker } from 'rsuite' import styled from 'styled-components' import { COLORS } from '../../../../../../constants/constants' -import { updateBeaconMalfunctionFromKanban } from '../../../../../../domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban' import { useMainAppDispatch } from '../../../../../../hooks/useMainAppDispatch' import { VESSEL_STATUS } from '../../../../../BeaconMalfunction/constants' +import { updateBeaconMalfunctionFromKanban } from '../../../../../BeaconMalfunction/useCases/updateBeaconMalfunctionFromKanban' import { getMalfunctionStartDateText } from '../../../../../BeaconMalfunction/utils' import TimeAgoSVG from '../../../../../icons/Label_horaire_VMS.svg?react' @@ -49,6 +49,7 @@ export function CurrentBeaconMalfunctionBody({ dispatch( updateBeaconMalfunctionFromKanban(beaconMalfunction.id, nextBeaconMalfunction, { + stage: nextBeaconMalfunction.stage, vesselStatus: nextBeaconMalfunction.vesselStatus }) ) diff --git a/frontend/src/features/Vessel/components/VesselSidebar/warnings/BeaconMalfunctionWarning.tsx b/frontend/src/features/Vessel/components/VesselSidebar/warnings/BeaconMalfunctionWarning.tsx index c2d8e352cc..8b8eaea125 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/warnings/BeaconMalfunctionWarning.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/warnings/BeaconMalfunctionWarning.tsx @@ -1,8 +1,8 @@ +import { openBeaconMalfunctionInKanban } from '@features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban' import { openSideWindowPath } from '@features/SideWindow/useCases/openSideWindowPath' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { THEME } from '@mtes-mct/monitor-ui' import { SideWindowMenuKey } from 'domain/entities/sideWindow/constants' -import { openBeaconMalfunctionInKanban } from 'domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban' import styled from 'styled-components' import BeaconMalfunctionSVG from '../../../../icons/Icone_VMS_dark.svg?react' From 80bcf37f0da329792a5f89b4f046c3d8b3144179 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 16:07:01 +0100 Subject: [PATCH 05/21] Migrate last vessels apis to rtk --- .../api/bff/VesselController.kt | 2 +- frontend/src/api/BackendApi.types.ts | 1 + frontend/src/api/vessel.ts | 85 ------------------ .../domain/use_cases/vessel/searchVessels.ts | 24 ++++-- .../src/domain/use_cases/vessel/showVessel.ts | 11 ++- .../use_cases/vessel/showVesselTrack.ts | 10 ++- .../updateSelectedVesselTrackRequest.ts | 10 ++- frontend/src/features/Vessel/vesselApi.ts | 86 ++++++++++++++++++- 8 files changed, 130 insertions(+), 99 deletions(-) delete mode 100644 frontend/src/api/vessel.ts diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt index 3f4a7a62d0..445f44c605 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/bff/VesselController.kt @@ -54,7 +54,7 @@ class VesselController( @GetMapping("/find") @Operation(summary = "Get vessel information and positions") - fun getVessel( + fun getVesselAndPositions( @Parameter(description = "Vessel internal id") @RequestParam(name = "vesselId") vesselId: Int?, diff --git a/frontend/src/api/BackendApi.types.ts b/frontend/src/api/BackendApi.types.ts index e3110c139e..1ed8853eb3 100644 --- a/frontend/src/api/BackendApi.types.ts +++ b/frontend/src/api/BackendApi.types.ts @@ -77,5 +77,6 @@ export namespace BackendApi { export interface Meta { response?: { headers: Headers + status: number } } diff --git a/frontend/src/api/vessel.ts b/frontend/src/api/vessel.ts deleted file mode 100644 index 73ece38c06..0000000000 --- a/frontend/src/api/vessel.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - getVesselIdentityFromLegacyVesselIdentity, - getVesselIdentityPropsAsEmptyStringsWhenUndefined -} from '@features/Vessel/utils' -import { Vessel } from '@features/Vessel/Vessel.types' -import { FrontendApiError } from '@libs/FrontendApiError' - -import { monitorfishApiKy } from './api' -import { HttpStatusCode } from './constants' - -import type { TrackRequest } from '../domain/entities/vessel/types' - -const VESSEL_POSITIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations du navire" -const VESSEL_SEARCH_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires dans notre base" - -/** - * Get vessel information and positions - * - * @throws {@link FrontendApiError} - */ -async function getVesselFromAPI(vesselIdentity: Vessel.VesselIdentity, trackRequest: TrackRequest) { - const { externalReferenceNumber, internalReferenceNumber, ircs, vesselId, vesselIdentifier } = - getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) - const trackDepth = trackRequest.trackDepth ?? '' - const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' - const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' - - try { - return await monitorfishApiKy - .get( - `/bff/v1/vessels/find?vesselId=${vesselId}&internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` - ) - .then(response => - response.json().then(vesselAndPositions => ({ - isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, - vesselAndPositions - })) - ) - } catch (err) { - throw new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** - * Get vessel positions - * - * @throws {@link FrontendApiError} - */ -async function getVesselPositionsFromAPI(identity: Vessel.VesselIdentity, trackRequest: TrackRequest) { - const { externalReferenceNumber, internalReferenceNumber, ircs, vesselIdentifier } = - getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(identity)) - const trackDepth = trackRequest.trackDepth ?? '' - const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' - const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' - - try { - return await monitorfishApiKy - .get( - `/bff/v1/vessels/positions?internalReferenceNumber=${internalReferenceNumber}&externalReferenceNumber=${externalReferenceNumber}&IRCS=${ircs}&vesselIdentifier=${vesselIdentifier}&trackDepth=${trackDepth}&afterDateTime=${afterDateTime}&beforeDateTime=${beforeDateTime}` - ) - .then(response => - response.json().then(positions => ({ - isTrackDepthModified: response.status === HttpStatusCode.ACCEPTED, - positions - })) - ) - } catch (err) { - throw new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -/** @deprecated Use Redux RTK `searchVessels()` query. */ -async function searchVesselsFromAPI(searched: string) { - const encodedSearched = encodeURI(searched) || '' - - try { - return await monitorfishApiKy - .get(`/bff/v1/vessels/search?searched=${encodedSearched}`) - .json() - } catch (err) { - throw new FrontendApiError(VESSEL_SEARCH_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} - -export { searchVesselsFromAPI, getVesselPositionsFromAPI, getVesselFromAPI } diff --git a/frontend/src/domain/use_cases/vessel/searchVessels.ts b/frontend/src/domain/use_cases/vessel/searchVessels.ts index ddf433b2e7..de8a1f5c13 100644 --- a/frontend/src/domain/use_cases/vessel/searchVessels.ts +++ b/frontend/src/domain/use_cases/vessel/searchVessels.ts @@ -1,8 +1,22 @@ -import { searchVesselsFromAPI } from '../../../api/vessel' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { Vessel } from '@features/Vessel/Vessel.types' +import { vesselApi } from '@features/Vessel/vesselApi' + import { setError } from '../../shared_slices/Global' +import type { MainAppThunk } from '@store' + /** @deprecated Use Redux RTK `searchVessels()` query. */ -export const searchVessels = searched => dispatch => - searchVesselsFromAPI(searched).catch(error => { - dispatch(setError(error)) - }) +export const searchVessels = + (searched: string): MainAppThunk> => + async dispatch => { + try { + return await dispatch( + vesselApi.endpoints.searchVessels.initiate({ searched }, RTK_FORCE_REFETCH_QUERY_OPTIONS) + ).unwrap() + } catch (error) { + dispatch(setError(error)) + + return undefined + } + } diff --git a/frontend/src/domain/use_cases/vessel/showVessel.ts b/frontend/src/domain/use_cases/vessel/showVessel.ts index 15d4afe6c6..d9f08f903f 100644 --- a/frontend/src/domain/use_cases/vessel/showVessel.ts +++ b/frontend/src/domain/use_cases/vessel/showVessel.ts @@ -1,7 +1,8 @@ -import { getVesselFromAPI } from '@api/vessel' +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { logbookActions } from '@features/Logbook/slice' import { doNotAnimate } from '@features/Map/slice' import { loadingVessel, resetLoadingVessel, setSelectedVessel, vesselSelectors } from '@features/Vessel/slice' +import { vesselApi } from '@features/Vessel/vesselApi' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { captureMessage } from '@sentry/react' import { omit } from 'lodash' @@ -59,7 +60,13 @@ export const showVessel = dispatch(addSearchedVessel(vesselIdentity)) } - const { isTrackDepthModified, vesselAndPositions } = await getVesselFromAPI(vesselIdentity, nextTrackRequest) + const { isTrackDepthModified, vesselAndPositions } = await dispatch( + vesselApi.endpoints.getVesselAndPositions.initiate( + { trackRequest: nextTrackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() + try { throwCustomErrorFromAPIFeedback(vesselAndPositions.positions, isTrackDepthModified, isFromUserAction) } catch (error) { diff --git a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts index d420eb394a..387902601b 100644 --- a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts +++ b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts @@ -1,10 +1,11 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '@features/Map/constants' import { doNotAnimate } from '@features/Map/slice' import { addVesselTrackShowed, resetLoadingVessel } from '@features/Vessel/slice' import { getVesselCompositeIdentifier } from '@features/Vessel/utils' +import { vesselApi } from '@features/Vessel/vesselApi' import { transform } from 'ol/proj' -import { getVesselPositionsFromAPI } from '../../../api/vessel' import { getCustomOrDefaultTrackRequest, throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { removeError, setError } from '../../shared_slices/Global' @@ -30,7 +31,12 @@ export const showVesselTrack = dispatch(doNotAnimate(!isFromUserAction)) dispatch(removeError()) - const { isTrackDepthModified, positions } = await getVesselPositionsFromAPI(vesselIdentity, nextTrackRequest) + const { isTrackDepthModified, positions } = await dispatch( + vesselApi.endpoints.getVesselPositions.initiate( + { trackRequest: nextTrackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() try { throwCustomErrorFromAPIFeedback(positions, isTrackDepthModified, isFromUserAction) dispatch(removeError()) diff --git a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts index 1de1e43d44..a31d687277 100644 --- a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts +++ b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts @@ -1,3 +1,4 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' import { animateToExtent, doNotAnimate } from '@features/Map/slice' import { resetLoadingVessel, @@ -5,8 +6,8 @@ import { updateSelectedVesselPositions, updatingVesselTrackDepth } from '@features/Vessel/slice' +import { vesselApi } from '@features/Vessel/vesselApi' -import { getVesselPositionsFromAPI } from '../../../api/vessel' import { logbookActions } from '../../../features/Logbook/slice' import { throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' import { removeError, setError } from '../../shared_slices/Global' @@ -30,7 +31,12 @@ export const updateSelectedVesselTrackRequest = dispatchUpdatingVessel(dispatch, true) - const { isTrackDepthModified, positions } = await getVesselPositionsFromAPI(vesselIdentity, trackRequest) + const { isTrackDepthModified, positions } = await dispatch( + vesselApi.endpoints.getVesselPositions.initiate( + { trackRequest, vesselIdentity }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() throwCustomErrorFromAPIFeedback(positions, isTrackDepthModified, false) dispatch(removeError()) diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 7daac6c1fc..b87c8d6773 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -1,19 +1,22 @@ import { monitorfishApi } from '@api/api' -import { RtkCacheTagType } from '@api/constants' +import { HttpStatusCode, RtkCacheTagType } from '@api/constants' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { FrontendApiError } from '@libs/FrontendApiError' import { getUrlOrPathWithQueryParams } from '@utils/getUrlOrPathWithQueryParams' import { displayedErrorActions } from 'domain/shared_slices/DisplayedError' import { displayOrLogError } from 'domain/use_cases/error/displayOrLogError' -import { getVesselIdentityPropsAsEmptyStringsWhenUndefined } from './utils' +import { getVesselIdentityFromLegacyVesselIdentity, getVesselIdentityPropsAsEmptyStringsWhenUndefined } from './utils' import { Vessel } from './Vessel.types' +import type { TrackRequest } from '../../domain/entities/vessel/types' +import type { Meta } from '@api/BackendApi.types' import type { VesselReportings } from '@features/Reporting/types' const GET_VESSEL_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations de ce navire." const GET_VESSEL_REPORTINGS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les signalements de ce navire." const SEARCH_VESSELS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les navires correspondants à cette recherche." +const VESSEL_POSITIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les informations du navire" export const vesselApi = monitorfishApi.injectEndpoints({ endpoints: builder => ({ @@ -23,6 +26,85 @@ export const vesselApi = monitorfishApi.injectEndpoints({ transformErrorResponse: response => new FrontendApiError(GET_VESSEL_ERROR_MESSAGE, response) }), + /** + * Get vessel information and positions. + * + * Transforms the response by reading the JSON body and setting + * `isTrackDepthModified` based on the response status. + */ + getVesselAndPositions: builder.query< + { isTrackDepthModified: boolean; vesselAndPositions: Vessel.VesselAndPositions }, + { trackRequest: TrackRequest; vesselIdentity: Vessel.VesselIdentity } + >({ + query: ({ trackRequest, vesselIdentity }) => { + const { externalReferenceNumber, internalReferenceNumber, ircs, vesselId, vesselIdentifier } = + getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) + const trackDepth = trackRequest.trackDepth ?? '' + const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' + const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' + + return { + method: 'GET', + params: { + afterDateTime, + beforeDateTime, + externalReferenceNumber, + internalReferenceNumber, + IRCS: ircs, + trackDepth, + vesselId, + vesselIdentifier + }, + url: `/bff/v1/vessels/find` + } + }, + transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), + transformResponse: async (baseQueryReturnValue: Vessel.VesselAndPositions, meta: Meta) => ({ + isTrackDepthModified: meta?.response?.status === HttpStatusCode.ACCEPTED, + vesselAndPositions: baseQueryReturnValue + }) + }), + + /** + * Get vessel positions. + * + * Transforms the response by reading the JSON body and setting + * `isTrackDepthModified` based on the response status. + */ + getVesselPositions: builder.query< + { isTrackDepthModified: boolean; positions: Vessel.VesselPosition[] }, + { trackRequest: TrackRequest; vesselIdentity: Vessel.VesselIdentity } + >({ + query: ({ trackRequest, vesselIdentity }) => { + const { externalReferenceNumber, internalReferenceNumber, ircs, vesselIdentifier } = + getVesselIdentityPropsAsEmptyStringsWhenUndefined(getVesselIdentityFromLegacyVesselIdentity(vesselIdentity)) + const trackDepth = trackRequest.trackDepth ?? '' + const afterDateTime = trackRequest.afterDateTime?.toISOString() ?? '' + const beforeDateTime = trackRequest.beforeDateTime?.toISOString() ?? '' + + return { + method: 'GET', + // Pass query parameters + params: { + afterDateTime, + beforeDateTime, + externalReferenceNumber, + internalReferenceNumber, + IRCS: ircs, + trackDepth, + vesselIdentifier + }, + + url: `/bff/v1/vessels/positions` + } + }, + transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), + transformResponse: async (baseQueryReturnValue: Vessel.VesselPosition[], meta: Meta) => ({ + isTrackDepthModified: meta?.response?.status === HttpStatusCode.ACCEPTED, + positions: baseQueryReturnValue + }) + }), + getVesselReportingsByVesselIdentity: builder.query< VesselReportings, { fromDate: string; vesselIdentity: Vessel.VesselIdentity } From e2c341d150fff934d7b2b8472df253b47ff8a622 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 16:28:49 +0100 Subject: [PATCH 06/21] Fix imports --- frontend/.eslintrc.cjs | 2 +- .../regulation_table/table.spec.ts | 2 +- .../form.spec.ts | 6 +- .../filter_bar.spec.ts | 2 +- frontend/package-lock.json | 2 +- frontend/src/domain/entities/vessel/types.ts | 2 +- frontend/src/domain/types/GeoJSON.ts | 16 ++--- frontend/src/env.d.ts | 1 - .../PriorNotification/priorNotificationApi.ts | 6 +- frontend/src/features/Regulation/slice.ts | 2 +- frontend/src/features/Regulation/types.ts | 2 +- frontend/src/features/Reporting/types.ts | 4 +- frontend/src/features/Vessel/Vessel.types.ts | 53 +---------------- .../schemas/VesselLastPositionSchema.ts | 59 +++++++++++++++++++ frontend/src/features/Vessel/vesselApi.ts | 3 +- frontend/src/features/Vessel/vesselNavApi.ts | 3 +- frontend/src/store/index.ts | 2 +- frontend/src/store/types.ts | 2 +- frontend/src/types.test.ts | 2 +- frontend/src/utils/nullify.ts | 2 +- frontend/src/utils/undefinedize.ts | 2 +- 21 files changed, 93 insertions(+), 82 deletions(-) create mode 100644 frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index 44229260d2..558ffffe4c 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -126,7 +126,7 @@ module.exports = { } }, { - files: ['src/domain/types/*.ts', 'src/domain/**/types.ts', 'src/domain/shared_slices/**/*.ts', 'src/**/slice.ts'], + files: ['src/domain/schemas/*.ts', 'src/domain/**/schemas.ts', 'src/domain/shared_slices/**/*.ts', 'src/**/slice.ts'], plugins: ['no-null'], rules: { 'no-param-reassign': 'off' diff --git a/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts b/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts index 3ae4915da3..780e052d8d 100644 --- a/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts +++ b/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts @@ -7,7 +7,7 @@ context('BackOffice > Regulation Table > Table', () => { cy.wait(1000) }) - it('regulatory zones are displayed by layer name and law types', () => { + it('regulatory zones are displayed by layer name and law schemas', () => { cy.get('[data-cy="backoffice-search-regulation"]').type('dra') cy.get('[data-cy="Reg. NAMO"]').eq(0).click() cy.get('[data-cy="backoffice-search-regulation"]').type('{backspace}{backspace}{backspace}') diff --git a/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts b/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts index cbef9c5cc4..58ae31596b 100644 --- a/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts +++ b/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts @@ -276,7 +276,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { cy.contains('Créer le préavis').should('be.enabled') }) - it('Should calculate and display manual prior notification fleet segments, risk factor & types', () => { + it('Should calculate and display manual prior notification fleet segments, risk factor & schemas', () => { // ------------------------------------------------------------------------- // Add @@ -402,7 +402,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { }) }) - it('Should only recalculate manual prior notification fleet segments, risk factor & types when necessary (creation)', () => { + it('Should only recalculate manual prior notification fleet segments, risk factor & schemas when necessary (creation)', () => { cy.intercept('POST', '/bff/v1/prior_notifications/manual/compute').as('computePriorNotification') cy.resetCountRequestsByAlias('@computePriorNotification') @@ -446,7 +446,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { cy.countRequestsByAlias('@computePriorNotification', 1500).should('be.equal', 1) }) - it('Should only recalculate manual prior notification fleet segments, risk factor & types when necessary (edition)', () => { + it('Should only recalculate manual prior notification fleet segments, risk factor & schemas when necessary (edition)', () => { cy.intercept('POST', '/bff/v1/prior_notifications/manual/compute').as('computePriorNotification') cy.resetCountRequestsByAlias('@computePriorNotification') diff --git a/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts b/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts index 2f054fb3bb..38bf8abd91 100644 --- a/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts +++ b/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts @@ -227,7 +227,7 @@ context('Side Window > Prior Notification List > VesselFilter Bar', () => { cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0) }) - it('Should filter prior notifications by types', () => { + it('Should filter prior notifications by schemas', () => { openSideWindowPriorNotificationListAsSuperUser() cy.intercept('GET', `${apiPathBase}*priorNotificationTypes=${encodeURI('Préavis type A,Préavis type C')}*`).as( diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cc363863c7..eea1ba66d7 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -62,7 +62,7 @@ "worker-loader": "3.0.8", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "yup": "1.6.1", - "zod": "3.24.1" + "zod": "^3.24.1" }, "devDependencies": { "@faker-js/faker": "9.3.0", diff --git a/frontend/src/domain/entities/vessel/types.ts b/frontend/src/domain/entities/vessel/types.ts index 7082a1136c..b62e166a90 100644 --- a/frontend/src/domain/entities/vessel/types.ts +++ b/frontend/src/domain/entities/vessel/types.ts @@ -1,4 +1,4 @@ -// TODO This should be moved to `entities/vessel/mission.types.ts` +// TODO This should be moved to `entities/vessel/mission.schemas.ts` import type { VesselTrackDepth } from '../vesselTrackDepth' import type { SelectableVesselTrackDepth } from '@features/Vessel/components/VesselSidebar/actions/TrackRequest/types' diff --git a/frontend/src/domain/types/GeoJSON.ts b/frontend/src/domain/types/GeoJSON.ts index eca87aa15e..0c2bd70767 100644 --- a/frontend/src/domain/types/GeoJSON.ts +++ b/frontend/src/domain/types/GeoJSON.ts @@ -1,6 +1,6 @@ /** - * Typescript types for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for - * ranged number data types. + * Typescript schemas for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for + * ranged number data schemas. * * See https://tools.ietf.org/html/rfc7946 */ @@ -13,13 +13,13 @@ export declare namespace GeoJSON { export type GeometryType = Geometry['type'] /** - * ...the term "GeoJSON types" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the - * geometry types listed above. + * ...the term "GeoJSON schemas" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the + * geometry schemas listed above. */ export type GeoJson = Geometry | Feature | FeatureCollection export type GeoJsonType = GeoJson['type'] - // types + // schemas /** * A position is an array of numbers. There MUST be two or more elements. The first two elements are longitude and @@ -34,7 +34,7 @@ export declare namespace GeoJSON { export type Record = { [key in string | number]: unknown } /** - * Properties inherit to all GeoJSON types + * Properties inherit to all GeoJSON schemas */ export interface GeometryBase extends Record { /** @@ -55,7 +55,7 @@ export declare namespace GeoJSON { */ } - // geometry types + // geometry schemas export interface Point extends GeometryBase { /** @@ -132,7 +132,7 @@ export declare namespace GeoJSON { type: 'GeometryCollection' } - // GeoJSON types + // GeoJSON schemas export interface Feature { /** diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts index 4a9ea48515..6c9c96e523 100644 --- a/frontend/src/env.d.ts +++ b/frontend/src/env.d.ts @@ -1,5 +1,4 @@ // https://vitejs.dev/guide/env-and-mode#intellisense-for-typescript -/// interface ImportMetaEnv { readonly FRONTEND_GEOSERVER_LOCAL_URL: string diff --git a/frontend/src/features/PriorNotification/priorNotificationApi.ts b/frontend/src/features/PriorNotification/priorNotificationApi.ts index e7bc4c0501..6889f78254 100644 --- a/frontend/src/features/PriorNotification/priorNotificationApi.ts +++ b/frontend/src/features/PriorNotification/priorNotificationApi.ts @@ -11,7 +11,7 @@ import type { ListFilter } from './components/PriorNotificationList/types' import type { Logbook } from '@features/Logbook/Logbook.types' const COMPUTE_PRIOR_NOTIFICATION_ERROR_MESSAGE = - "Nous n'avons pas pu calculer note de risque, segments ou types pour ce préavis." + "Nous n'avons pas pu calculer note de risque, segments ou schemas pour ce préavis." const CREATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu créé le préavis." const DELETE_PRIOR_NOTIFICATION_UPLOAD_ERROR_MESSAGE = "Nous n'avons pas pu supprimer ce document attaché." const GET_PRIOR_NOTIFICATION_UPLOADS_ERROR_MESSAGE = @@ -21,7 +21,7 @@ const GET_PRIOR_NOTIFICATION_SENT_MESSAGES_ERROR_MESSAGE = const UPDATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu modifier le préavis." const GET_PRIOR_NOTIFICATION_DETAIL_ERROR_MESSAGE = "Nous n'avons pas pu récupérer le préavis." const GET_PRIOR_NOTIFICATIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des préavis." -const GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des types de préavis." +const GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des schemas de préavis." const GET_PRIOR_NOTIFICATION_PDF_ERROR_MESSAGE = "Nous n'avons pas pu récupérer le PDF du préavis." const VERIFY_AND_SEND_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu vérifier et envoyer le préavis." const INVALIDATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu invalider et envoyer le préavis." @@ -139,7 +139,7 @@ export const priorNotificationApi = monitorfishApi.injectEndpoints({ getPriorNotificationTypes: builder.query({ providesTags: () => [{ type: RtkCacheTagType.PriorNotificationTypes }], - query: () => '/prior_notifications/types', + query: () => '/prior_notifications/schemas', transformErrorResponse: response => new FrontendApiError(GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE, response) }), diff --git a/frontend/src/features/Regulation/slice.ts b/frontend/src/features/Regulation/slice.ts index 7fe4fb7966..4e49d62ccf 100644 --- a/frontend/src/features/Regulation/slice.ts +++ b/frontend/src/features/Regulation/slice.ts @@ -392,7 +392,7 @@ const regulationSlice = createSlice({ } }, - // TODO Fix these types and find a cleaner way to achieve that. Proposal: pass a partial `RegulatoryZoneDraft` as param and use a "deepMerge" function. + // TODO Fix these schemas and find a cleaner way to achieve that. Proposal: pass a partial `RegulatoryZoneDraft` as param and use a "deepMerge" function. updateProcessingRegulationByKeyAndSubKey( state, action: PayloadAction<{ diff --git a/frontend/src/features/Regulation/types.ts b/frontend/src/features/Regulation/types.ts index 803f7bb357..3e69750847 100644 --- a/frontend/src/features/Regulation/types.ts +++ b/frontend/src/features/Regulation/types.ts @@ -55,7 +55,7 @@ export type TimeInterval = { to: Date | undefined } -// TODO It would be safer to use strict array types: `DateRange[]` and `DateAsStringRange[]`. +// TODO It would be safer to use strict array schemas: `DateRange[]` and `DateAsStringRange[]`. export type FishingPeriod = { always: boolean | undefined annualRecurrence: boolean | undefined diff --git a/frontend/src/features/Reporting/types.ts b/frontend/src/features/Reporting/types.ts index 8a8eeec0a5..fa00f78256 100644 --- a/frontend/src/features/Reporting/types.ts +++ b/frontend/src/features/Reporting/types.ts @@ -7,7 +7,7 @@ import type { Infraction } from '../../domain/types/infraction' import type { PendingAlertValue } from '../Alert/types' import type { LegacyControlUnit } from '../ControlUnit/legacyControlUnit' -// TODO Move other types into new `Reporting` namespace. +// TODO Move other schemas into new `Reporting` namespace. export namespace Reporting { export type Reporting = InfractionSuspicionReporting | ObservationReporting | PendingAlertReporting export type EditableReporting = InfractionSuspicionReporting | ObservationReporting @@ -125,7 +125,7 @@ export type Observation = { } type ReportingTypeCharacteristic = { - // TODO It should be useless now that types are discriminated. + // TODO It should be useless now that schemas are discriminated. code: ReportingType displayName: string // TODO This should be named differently to avoid confusion with `ReportingType.INFRACTION_SUSPICION` type. diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index 0e28b25288..703de3efc3 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -1,11 +1,10 @@ -import { PendingAlertValueType } from '@features/Alert/types' -import { ReportingType } from '@features/Reporting/types' import { z } from 'zod' -import { booleanOrUndefined, numberOrUndefined, stringOrUndefined } from '../../types' +import { numberOrUndefined, stringOrUndefined } from '../../types' import type { ProducerOrganizationMembership } from '@features/ProducerOrganizationMembership/types' import type { RiskFactor } from '@features/RiskFactor/types' +import type { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' import type Feature from 'ol/Feature' import type LineString from 'ol/geom/LineString' import type Point from 'ol/geom/Point' @@ -155,54 +154,6 @@ export namespace Vessel { }) export type DeclaredLogbookSpecies = z.infer - // TODO Check which of these types are nullable or not - export const VesselLastPositionSchema = z.strictObject({ - alerts: z.array(z.union([z.nativeEnum(PendingAlertValueType), z.literal('PNO_LAN_WEIGHT_TOLERANCE_ALERT')])), - beaconMalfunctionId: numberOrUndefined, - beaconNumber: numberOrUndefined, - course: numberOrUndefined, - dateTime: z.string(), - departureDateTime: stringOrUndefined, - destination: stringOrUndefined, - detectabilityRiskFactor: numberOrUndefined, - district: stringOrUndefined, - districtCode: stringOrUndefined, - emissionPeriod: numberOrUndefined, - estimatedCurrentLatitude: numberOrUndefined, - estimatedCurrentLongitude: numberOrUndefined, - externalReferenceNumber: stringOrUndefined, - flagState: z.string(), - from: stringOrUndefined, - gearOnboard: z.array(DeclaredLogbookGearSchema), - impactRiskFactor: numberOrUndefined, - internalReferenceNumber: stringOrUndefined, - ircs: stringOrUndefined, - isAtPort: booleanOrUndefined, - lastControlDateTime: stringOrUndefined, - lastControlInfraction: booleanOrUndefined, - lastLogbookMessageDateTime: stringOrUndefined, - latitude: z.number(), - length: numberOrUndefined, - longitude: z.number(), - mmsi: stringOrUndefined, - positionType: z.string(), - postControlComment: stringOrUndefined, - probabilityRiskFactor: numberOrUndefined, - registryPortLocode: stringOrUndefined, - registryPortName: stringOrUndefined, - reportings: z.array(z.nativeEnum(ReportingType)), - riskFactor: numberOrUndefined, - segments: z.array(z.string()), - speciesOnboard: z.array(DeclaredLogbookSpeciesSchema), - speed: numberOrUndefined, - totalWeightOnboard: numberOrUndefined, - tripNumber: stringOrUndefined, - underCharter: booleanOrUndefined, - vesselId: numberOrUndefined, - vesselIdentifier: z.nativeEnum(VesselIdentifier), - vesselName: stringOrUndefined, - width: numberOrUndefined - }) export type VesselLastPosition = z.infer export type VesselPosition = { diff --git a/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts b/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts new file mode 100644 index 0000000000..2670bad7d1 --- /dev/null +++ b/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts @@ -0,0 +1,59 @@ +import { PendingAlertValueType } from '@features/Alert/types' +import { ReportingType } from '@features/Reporting/types' +import { Vessel } from '@features/Vessel/Vessel.types' +import { z } from 'zod' + +import { booleanOrUndefined, numberOrUndefined, stringOrUndefined } from '../../../types' + +import DeclaredLogbookSpeciesSchema = Vessel.DeclaredLogbookSpeciesSchema +import DeclaredLogbookGearSchema = Vessel.DeclaredLogbookGearSchema +import VesselIdentifier = Vessel.VesselIdentifier + +// TODO Check which of these schemas are nullable or not +export const VesselLastPositionSchema = z.strictObject({ + alerts: z.array(z.union([z.nativeEnum(PendingAlertValueType), z.literal('PNO_LAN_WEIGHT_TOLERANCE_ALERT')])), + beaconMalfunctionId: numberOrUndefined, + beaconNumber: numberOrUndefined, + course: numberOrUndefined, + dateTime: z.string(), + departureDateTime: stringOrUndefined, + destination: stringOrUndefined, + detectabilityRiskFactor: numberOrUndefined, + district: stringOrUndefined, + districtCode: stringOrUndefined, + emissionPeriod: numberOrUndefined, + estimatedCurrentLatitude: numberOrUndefined, + estimatedCurrentLongitude: numberOrUndefined, + externalReferenceNumber: stringOrUndefined, + flagState: z.string(), + from: stringOrUndefined, + gearOnboard: z.array(DeclaredLogbookGearSchema), + impactRiskFactor: numberOrUndefined, + internalReferenceNumber: stringOrUndefined, + ircs: stringOrUndefined, + isAtPort: booleanOrUndefined, + lastControlDateTime: stringOrUndefined, + lastControlInfraction: booleanOrUndefined, + lastLogbookMessageDateTime: stringOrUndefined, + latitude: z.number(), + length: numberOrUndefined, + longitude: z.number(), + mmsi: stringOrUndefined, + positionType: z.string(), + postControlComment: stringOrUndefined, + probabilityRiskFactor: numberOrUndefined, + registryPortLocode: stringOrUndefined, + registryPortName: stringOrUndefined, + reportings: z.array(z.nativeEnum(ReportingType)), + riskFactor: numberOrUndefined, + segments: z.array(z.string()), + speciesOnboard: z.array(DeclaredLogbookSpeciesSchema), + speed: numberOrUndefined, + totalWeightOnboard: numberOrUndefined, + tripNumber: stringOrUndefined, + underCharter: booleanOrUndefined, + vesselId: numberOrUndefined, + vesselIdentifier: z.nativeEnum(VesselIdentifier), + vesselName: stringOrUndefined, + width: numberOrUndefined +}) diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index b87c8d6773..85b9a41dc0 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -1,5 +1,6 @@ import { monitorfishApi } from '@api/api' import { HttpStatusCode, RtkCacheTagType } from '@api/constants' +import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { FrontendApiError } from '@libs/FrontendApiError' import { getUrlOrPathWithQueryParams } from '@utils/getUrlOrPathWithQueryParams' @@ -140,7 +141,7 @@ export const vesselApi = monitorfishApi.injectEndpoints({ getVesselsLastPositions: builder.query({ query: () => `/vessels`, transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => - baseQueryReturnValue.map(LastPosition => Vessel.VesselLastPositionSchema.parse(LastPosition)) + baseQueryReturnValue.map(LastPosition => VesselLastPositionSchema.parse(LastPosition)) }), searchVessels: builder.query({ diff --git a/frontend/src/features/Vessel/vesselNavApi.ts b/frontend/src/features/Vessel/vesselNavApi.ts index 2029e34d07..b96691d722 100644 --- a/frontend/src/features/Vessel/vesselNavApi.ts +++ b/frontend/src/features/Vessel/vesselNavApi.ts @@ -1,4 +1,5 @@ import { monitorfishLightApi } from '@api/api' +import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' import { Vessel } from '@features/Vessel/Vessel.types' export const vesselNavApi = monitorfishLightApi.injectEndpoints({ @@ -6,7 +7,7 @@ export const vesselNavApi = monitorfishLightApi.injectEndpoints({ getVesselsLastPositions: builder.query({ query: () => `/v1/vessels`, transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => - baseQueryReturnValue.map(LastPosition => Vessel.VesselLastPositionSchema.parse(LastPosition)) + baseQueryReturnValue.map(LastPosition => VesselLastPositionSchema.parse(LastPosition)) }) }) }) diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts index 6a23f6c82a..df4d4bbca0 100644 --- a/frontend/src/store/index.ts +++ b/frontend/src/store/index.ts @@ -55,7 +55,7 @@ setupListeners(mainStore.dispatch) export const mainStorePersistor = persistStore(mainStore) // https://react-redux.js.org/using-react-redux/usage-with-typescript#define-root-state-and-dispatch-types -// Infer the `MainRootState` and `AppDispatch` types from the store itself +// Infer the `MainRootState` and `AppDispatch` schemas from the store itself export type MainAppDispatch = typeof mainStore.dispatch export type MainAppGetState = () => MainRootState export type MainAppThunk = ThunkAction diff --git a/frontend/src/store/types.ts b/frontend/src/store/types.ts index cd85adb0c9..f64be1ee54 100644 --- a/frontend/src/store/types.ts +++ b/frontend/src/store/types.ts @@ -1,6 +1,6 @@ import type { BackofficeAppDispatch, BackofficeAppThunk, MainAppDispatch, MainAppThunk } from '@store' -// These types are used for store-hybrid thunks (use cases) +// These schemas are used for store-hybrid thunks (use cases) export type HybridAppDispatch = BackofficeAppDispatch | MainAppDispatch export type HybridAppThunk = T extends BackofficeAppDispatch ? BackofficeAppThunk diff --git a/frontend/src/types.test.ts b/frontend/src/types.test.ts index 81bb58693b..595e50c0ac 100644 --- a/frontend/src/types.test.ts +++ b/frontend/src/types.test.ts @@ -24,7 +24,7 @@ describe('types', () => { /** * This test is required by jest to pass the error : "Your test suite must contain at least one test." * - * The actual tests are running when checkin TS types (by `npm run test:type`): + * The actual tests are running when checkin TS schemas (by `npm run test:type`): * - undefinableTestInterface * - undefinableTestInterfaceWithMissingProp */ diff --git a/frontend/src/utils/nullify.ts b/frontend/src/utils/nullify.ts index bfedc57cb7..e115e2f018 100644 --- a/frontend/src/utils/nullify.ts +++ b/frontend/src/utils/nullify.ts @@ -30,7 +30,7 @@ const nullifyObjectPropPair = ([key, value]: [string, NativeAny]) => [key, nulli * Transform all `undefined` values into `null` ones in any type of value * * @description - * The value must be of native type and only contains native types. + * The value must be of native type and only contains native schemas. */ export function nullify(value: T): Nullify | null { if (value === null || value === undefined) { diff --git a/frontend/src/utils/undefinedize.ts b/frontend/src/utils/undefinedize.ts index 42b8a6fb8d..1efff35753 100644 --- a/frontend/src/utils/undefinedize.ts +++ b/frontend/src/utils/undefinedize.ts @@ -31,7 +31,7 @@ const undefinedizeObjectPropPair = ([key, value]: [string, NativeAny]) => [key, * Transform all `null` values into `undefined` ones in any type of value * * @description - * The value must be of native type and only contains native types. + * The value must be of native type and only contains native schemas. */ export function undefinedize(value: T): Undefinedized | undefined { if (value === null || value === undefined) { From cbbd9ffadf0a348f5fd2521323b9dba2399fdd72 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 17:03:23 +0100 Subject: [PATCH 07/21] Fix shortenedimports --- frontend/src/domain/entities/vessel/vessel.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/domain/entities/vessel/vessel.ts b/frontend/src/domain/entities/vessel/vessel.ts index 891cee5465..4ab8b4b086 100644 --- a/frontend/src/domain/entities/vessel/vessel.ts +++ b/frontend/src/domain/entities/vessel/vessel.ts @@ -1,9 +1,13 @@ -import { BaseLayer } from '@features/Map/constants' -import { MonitorFishMap } from '@features/Map/Map.types' -import { getVesselCompositeIdentifier } from '@features/Vessel/utils' +/** + * /!\ Do not shorten these 3 imports as they are required by the web worker `MonitorFishWebWorker` + * It will fail the Vite build : `Rollup failed to resolve import [...]` + */ import countries from 'i18n-iso-countries' import { VesselLabel } from './label/types' +import { BaseLayer } from '../../../features/Map/constants' +import { MonitorFishMap } from '../../../features/Map/Map.types' +import { getVesselCompositeIdentifier } from '../../../features/Vessel/utils' import type { ShowedVesselTrack } from './types' import type { PartialExcept } from '../../../types' From e541327ef71e3e929336c08365e40978827ba5d1 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 17:31:02 +0100 Subject: [PATCH 08/21] Move getVesselControl to Mission feature --- frontend/src/api/APIWorker.tsx | 2 +- frontend/src/domain/types/specy.ts | 7 ---- .../components}/PreviewFilteredVessels.jsx | 6 ++-- frontend/src/features/MainWindow/index.tsx | 2 +- .../Mission/components/MissionForm/index.tsx | 10 +++--- .../Mission/missionActionApi.ts} | 35 ++++++++----------- .../Mission/useCases/autoSaveMissionAction.ts | 2 +- .../Mission/useCases/deleteMissionAction.ts | 2 +- .../Mission/useCases}/getVesselControls.ts | 24 +++++++++---- .../saveMissionAndMissionActionsByDiff.ts | 2 +- .../VesselSidebar/Controls/index.tsx | 2 +- 11 files changed, 46 insertions(+), 48 deletions(-) rename frontend/src/features/{preview_filtered_vessels => MainWindow/components}/PreviewFilteredVessels.jsx (88%) rename frontend/src/{api/missionAction.ts => features/Mission/missionActionApi.ts} (71%) rename frontend/src/{domain/use_cases/mission => features/Mission/useCases}/getVesselControls.ts (71%) diff --git a/frontend/src/api/APIWorker.tsx b/frontend/src/api/APIWorker.tsx index a2adc0bced..5604bd53c9 100644 --- a/frontend/src/api/APIWorker.tsx +++ b/frontend/src/api/APIWorker.tsx @@ -2,6 +2,7 @@ import { getAllBeaconMalfunctions } from '@features/BeaconMalfunction/useCases/g import { getVesselBeaconMalfunctions } from '@features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions' import { openBeaconMalfunctionInKanban } from '@features/BeaconMalfunction/useCases/openBeaconMalfunctionInKanban' import { fleetSegmentApi } from '@features/FleetSegment/apis' +import { getVesselControls } from '@features/Mission/useCases/getVesselControls' import { getAllRegulatoryLayers } from '@features/Regulation/useCases/getAllRegulatoryLayers' import { reportingApi } from '@features/Reporting/reportingApi' import { useCallback, useEffect, useRef, useState } from 'react' @@ -14,7 +15,6 @@ import { getOperationalAlerts } from '../domain/use_cases/alert/getOperationalAl import { getSilencedAlerts } from '../domain/use_cases/alert/getSilencedAlerts' import { getAllGearCodes } from '../domain/use_cases/gearCode/getAllGearCodes' import { getInfractions } from '../domain/use_cases/infraction/getInfractions' -import { getVesselControls } from '../domain/use_cases/mission/getVesselControls' import { getAllSpecies } from '../domain/use_cases/species/getAllSpecies' import { updateVesselTracks } from '../domain/use_cases/vessel/updateVesselTracks' import { useMainAppDispatch } from '../hooks/useMainAppDispatch' diff --git a/frontend/src/domain/types/specy.ts b/frontend/src/domain/types/specy.ts index 33e072ac0f..16d42a27eb 100644 --- a/frontend/src/domain/types/specy.ts +++ b/frontend/src/domain/types/specy.ts @@ -14,10 +14,3 @@ export type SpeciesAndSpeciesGroupsAPIData = { groups: SpecyGroup[] species: Specy[] } - -export type SpeciesAndSpecyGroups = { - groups: SpecyGroup[] - species: Specy[] - // TODO Type this prop. - speciesByCode: Record> -} diff --git a/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx b/frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx similarity index 88% rename from frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx rename to frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx index 839d116a94..e1f0b35a76 100644 --- a/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx +++ b/frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx @@ -1,9 +1,9 @@ import React from 'react' import styled from 'styled-components' import { useDispatch, useSelector } from 'react-redux' -import { COLORS } from '../../constants/constants' -import BackToVesselsListSVG from '../icons/Fleche_navigation_marees_gainsboro.svg?react' -import { undoPreviewVessels } from '@features/Vessel/useCases/undoPreviewVessels' +import { COLORS } from '@constants/constants.js' +import BackToVesselsListSVG from '../../icons/Fleche_navigation_marees_gainsboro.svg?react' +import { undoPreviewVessels } from '@features/Vessel/useCases/undoPreviewVessels.js' const PreviewFilteredVessels = () => { const dispatch = useDispatch() diff --git a/frontend/src/features/MainWindow/index.tsx b/frontend/src/features/MainWindow/index.tsx index 9c68f69242..2badf26860 100644 --- a/frontend/src/features/MainWindow/index.tsx +++ b/frontend/src/features/MainWindow/index.tsx @@ -17,7 +17,7 @@ import { ControlUnitListDialog } from '../ControlUnit/components/ControlUnitList import { DrawLayerModal } from '../Draw/components/DrawModal' import { HealthcheckHeadband } from '../Healthcheck/components/HealthcheckHeadband' import { LayersSidebar } from '../LayersSidebar/components' -import PreviewFilteredVessels from '../preview_filtered_vessels/PreviewFilteredVessels' +import PreviewFilteredVessels from './components/PreviewFilteredVessels' import { SideWindowLauncher } from '../SideWindow/SideWindowLauncher' import { VesselList } from '../Vessel/components/VesselList' import { VesselLoader } from '../Vessel/components/VesselLoader' diff --git a/frontend/src/features/Mission/components/MissionForm/index.tsx b/frontend/src/features/Mission/components/MissionForm/index.tsx index dd560b42f0..bada414ddd 100644 --- a/frontend/src/features/Mission/components/MissionForm/index.tsx +++ b/frontend/src/features/Mission/components/MissionForm/index.tsx @@ -1,9 +1,4 @@ import { monitorfishApi } from '@api/api' -import { - useCreateMissionActionMutation, - useDeleteMissionActionMutation, - useUpdateMissionActionMutation -} from '@api/missionAction' import { FrontendErrorBoundary } from '@components/FrontendErrorBoundary' import { useGetMissionFrontCompletion } from '@features/Mission/components/MissionForm/hooks/useGetMissionFrontCompletion' import { MainFormLiveSchema } from '@features/Mission/components/MissionForm/MainForm/schemas' @@ -11,6 +6,11 @@ import { CompletionStatusTag } from '@features/Mission/components/MissionForm/sh import { isMissionActionFormValid } from '@features/Mission/components/MissionForm/utils/isMissionActionFormValid' import { Mission } from '@features/Mission/mission.types' import { MissionAction } from '@features/Mission/missionAction.types' +import { + useCreateMissionActionMutation, + useDeleteMissionActionMutation, + useUpdateMissionActionMutation +} from '@features/Mission/missionActionApi' import { autoSaveMission } from '@features/Mission/useCases/autoSaveMission' import { autoSaveMissionAction } from '@features/Mission/useCases/autoSaveMissionAction' import { deleteMission } from '@features/Mission/useCases/deleteMission' diff --git a/frontend/src/api/missionAction.ts b/frontend/src/features/Mission/missionActionApi.ts similarity index 71% rename from frontend/src/api/missionAction.ts rename to frontend/src/features/Mission/missionActionApi.ts index f2331fb5ba..f67e303636 100644 --- a/frontend/src/api/missionAction.ts +++ b/frontend/src/features/Mission/missionActionApi.ts @@ -1,9 +1,10 @@ -import { monitorfishApi, monitorfishApiKy } from './api' -import { FrontendApiError } from '../libs/FrontendApiError' +import { monitorfishApi } from '@api/api' +import { FrontendApiError } from '@libs/FrontendApiError' import type { MissionAction } from '@features/Mission/missionAction.types' const GET_MISSION_ACTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les actions de la mission" +export const MISSION_ACTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les contrôles de ce navire" export const missionActionApi = monitorfishApi.injectEndpoints({ endpoints: builder => ({ @@ -27,6 +28,18 @@ export const missionActionApi = monitorfishApi.injectEndpoints({ transformErrorResponse: response => new FrontendApiError(GET_MISSION_ACTIONS_ERROR_MESSAGE, response) }), + getVesselControls: builder.query({ + query: ({ fromDate, vesselId }) => ({ + method: 'GET', + params: { + afterDateTime: fromDate, + vesselId + }, + url: '/bff/v1/mission_actions/controls' + }), + transformErrorResponse: response => new FrontendApiError(MISSION_ACTIONS_ERROR_MESSAGE, response) + }), + updateMissionAction: builder.mutation({ query: missionAction => ({ body: missionAction, @@ -43,21 +56,3 @@ export const { useGetMissionActionsQuery, useUpdateMissionActionMutation } = missionActionApi - -export const MISSION_ACTIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer les contrôles de ce navire" - -/** - * Get vessel controls - * - * @throws {@link FrontendApiError} - * - */ -export async function getVesselControlsFromAPI(vesselId: number, fromDate: string) { - try { - return await monitorfishApiKy - .get(`/bff/v1/mission_actions/controls?vesselId=${vesselId}&afterDateTime=${fromDate}`) - .json() - } catch (err) { - throw new FrontendApiError(MISSION_ACTIONS_ERROR_MESSAGE, (err as FrontendApiError).originalError) - } -} diff --git a/frontend/src/features/Mission/useCases/autoSaveMissionAction.ts b/frontend/src/features/Mission/useCases/autoSaveMissionAction.ts index 70ca783ccc..b5717a87a8 100644 --- a/frontend/src/features/Mission/useCases/autoSaveMissionAction.ts +++ b/frontend/src/features/Mission/useCases/autoSaveMissionAction.ts @@ -1,7 +1,7 @@ -import { missionActionApi } from '@api/missionAction' import { missionFormActions } from '@features/Mission/components/MissionForm/slice' import { getMissionActionDataFromFormValues } from '@features/Mission/components/MissionForm/utils' import { isMissionActionFormValid } from '@features/Mission/components/MissionForm/utils/isMissionActionFormValid' +import { missionActionApi } from '@features/Mission/missionActionApi' import { logSoftError } from '@mtes-mct/monitor-ui' import type { MissionActionFormValues } from '@features/Mission/components/MissionForm/types' diff --git a/frontend/src/features/Mission/useCases/deleteMissionAction.ts b/frontend/src/features/Mission/useCases/deleteMissionAction.ts index f87db6b4cf..b380667a2f 100644 --- a/frontend/src/features/Mission/useCases/deleteMissionAction.ts +++ b/frontend/src/features/Mission/useCases/deleteMissionAction.ts @@ -1,10 +1,10 @@ -import { missionActionApi } from '@api/missionAction' import { portApi } from '@api/port' import { missionFormActions } from '@features/Mission/components/MissionForm/slice' 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 { missionActionApi } from '@features/Mission/missionActionApi' import { monitorfishMissionApi } from '@features/Mission/monitorfishMissionApi' import { MissionAction } from '../missionAction.types' diff --git a/frontend/src/domain/use_cases/mission/getVesselControls.ts b/frontend/src/features/Mission/useCases/getVesselControls.ts similarity index 71% rename from frontend/src/domain/use_cases/mission/getVesselControls.ts rename to frontend/src/features/Mission/useCases/getVesselControls.ts index bc1c021b52..2b6271f198 100644 --- a/frontend/src/domain/use_cases/mission/getVesselControls.ts +++ b/frontend/src/features/Mission/useCases/getVesselControls.ts @@ -1,17 +1,18 @@ +import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' +import { missionActionApi } from '@features/Mission/missionActionApi' import { DisplayedErrorKey } from '@libs/DisplayedError/constants' -import { getVesselControlsFromAPI } from '../../../api/missionAction' -import { NoControlsFoundError } from '../../../errors/NoControlsFoundError' import { loadControls, resetLoadControls, setControlSummary, setNextControlSummary, unsetControlSummary -} from '../../shared_slices/Control' -import { displayedErrorActions } from '../../shared_slices/DisplayedError' -import { removeError, setError } from '../../shared_slices/Global' -import { displayOrLogError } from '../error/displayOrLogError' +} from '../../../domain/shared_slices/Control' +import { displayedErrorActions } from '../../../domain/shared_slices/DisplayedError' +import { removeError, setError } from '../../../domain/shared_slices/Global' +import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' +import { NoControlsFoundError } from '../../../errors/NoControlsFoundError' export const getVesselControls = (isFromUserAction: boolean) => async (dispatch, getState) => { const { selectedVessel } = getState().vessel @@ -35,7 +36,16 @@ export const getVesselControls = (isFromUserAction: boolean) => async (dispatch, } try { - const controlSummary = await getVesselControlsFromAPI(selectedVessel.vesselId, controlsFromDate) + const controlSummary = await dispatch( + missionActionApi.endpoints.getVesselControls.initiate( + { + fromDate: controlsFromDate, + vesselId: selectedVessel.vesselId + }, + RTK_FORCE_REFETCH_QUERY_OPTIONS + ) + ).unwrap() + if (isSameVesselAsCurrentlyShowed && !isFromUserAction) { if (controlSummary.controls?.length > currentControlSummary.missionActions?.length) { dispatch(setNextControlSummary(controlSummary)) diff --git a/frontend/src/features/Mission/useCases/saveMissionAndMissionActionsByDiff.ts b/frontend/src/features/Mission/useCases/saveMissionAndMissionActionsByDiff.ts index 206ba15084..177f97fea0 100644 --- a/frontend/src/features/Mission/useCases/saveMissionAndMissionActionsByDiff.ts +++ b/frontend/src/features/Mission/useCases/saveMissionAndMissionActionsByDiff.ts @@ -1,7 +1,7 @@ import { RTK_FORCE_REFETCH_QUERY_OPTIONS } from '@api/constants' -import { missionActionApi } from '@api/missionAction' import { missionFormActions } from '@features/Mission/components/MissionForm/slice' import { getMissionActionsToCreateUpdateOrDelete } from '@features/Mission/components/MissionForm/utils' +import { missionActionApi } from '@features/Mission/missionActionApi' import { monitorfishMissionApi } from '@features/Mission/monitorfishMissionApi' import { saveMission } from '@features/Mission/useCases/saveMission' import { logSoftError } from '@mtes-mct/monitor-ui' diff --git a/frontend/src/features/Vessel/components/VesselSidebar/Controls/index.tsx b/frontend/src/features/Vessel/components/VesselSidebar/Controls/index.tsx index 0944e09364..d521d39638 100644 --- a/frontend/src/features/Vessel/components/VesselSidebar/Controls/index.tsx +++ b/frontend/src/features/Vessel/components/VesselSidebar/Controls/index.tsx @@ -1,4 +1,5 @@ import { FingerprintSpinner } from '@components/FingerprintSpinner' +import { getVesselControls } from '@features/Mission/useCases/getVesselControls' import { customDayjs } from '@mtes-mct/monitor-ui' import { useCallback, useEffect, useMemo } from 'react' import styled from 'styled-components' @@ -12,7 +13,6 @@ import { setControlFromDate, setControlSummary } from '../../../../../domain/shared_slices/Control' -import { getVesselControls } from '../../../../../domain/use_cases/mission/getVesselControls' import { useMainAppDispatch } from '../../../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../../../hooks/useMainAppSelector' From 50dec49dc9f3cfb44833b8405f26b98a98148737 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Mon, 3 Feb 2025 17:44:26 +0100 Subject: [PATCH 09/21] Fix apis paths --- .../src/features/BeaconMalfunction/apis.ts | 12 +++++----- frontend/src/features/FleetSegment/apis.ts | 8 +++---- ...Vessels.jsx => PreviewFilteredVessels.tsx} | 22 ++++++++----------- frontend/src/features/MainWindow/index.tsx | 2 +- .../src/features/Mission/missionActionApi.ts | 2 +- frontend/src/features/Vessel/vesselApi.ts | 4 ++-- 6 files changed, 23 insertions(+), 27 deletions(-) rename frontend/src/features/MainWindow/components/{PreviewFilteredVessels.jsx => PreviewFilteredVessels.tsx} (67%) diff --git a/frontend/src/features/BeaconMalfunction/apis.ts b/frontend/src/features/BeaconMalfunction/apis.ts index e4eeb85c3f..070db42e69 100644 --- a/frontend/src/features/BeaconMalfunction/apis.ts +++ b/frontend/src/features/BeaconMalfunction/apis.ts @@ -24,7 +24,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ getAllBeaconMalfunctions: builder.query({ query: () => ({ method: 'GET', - url: '/bff/v1/beacon_malfunctions' + url: '/beacon_malfunctions' }), transformErrorResponse: response => new FrontendApiError(GET_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) }), @@ -32,7 +32,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ getBeaconMalfunction: builder.query({ query: id => ({ method: 'GET', - url: `/bff/v1/beacon_malfunctions/${id}` + url: `/beacon_malfunctions/${id}` }), transformErrorResponse: response => new FrontendApiError(GET_BEACON_MALFUNCTION_ERROR_MESSAGE, response) }), @@ -47,7 +47,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ afterDateTime: fromDate.toISOString(), vesselId }, - url: '/bff/v1/vessels/beacon_malfunctions' + url: '/vessels/beacon_malfunctions' }), transformErrorResponse: response => new FrontendApiError(GET_VESSEL_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) }), @@ -59,7 +59,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ query: ({ comment, id }) => ({ body: comment, method: 'POST', - url: `/bff/v1/beacon_malfunctions/${id}/comments` + url: `/beacon_malfunctions/${id}/comments` }), transformErrorResponse: response => new FrontendApiError(SAVE_BEACON_MALFUNCTION_COMMENT_ERROR_MESSAGE, response) }), @@ -73,7 +73,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ params: { requestedNotificationForeignFmcCode: foreignFmcCode }, - url: `/bff/v1/beacon_malfunctions/${id}/${notificationType}` + url: `/beacon_malfunctions/${id}/${notificationType}` }), transformErrorResponse: response => new FrontendApiError(SEND_NOTIFICATION_ERROR_MESSAGE, response) }), @@ -85,7 +85,7 @@ export const beaconMalfunctionApi = monitorfishApi.injectEndpoints({ query: ({ id, updatedFields }) => ({ body: updatedFields, method: 'PUT', - url: `/bff/v1/beacon_malfunctions/${id}` + url: `/beacon_malfunctions/${id}` }), transformErrorResponse: response => new FrontendApiError(UPDATE_BEACON_MALFUNCTIONS_ERROR_MESSAGE, response) }) diff --git a/frontend/src/features/FleetSegment/apis.ts b/frontend/src/features/FleetSegment/apis.ts index b0595255a1..2e1e891e63 100644 --- a/frontend/src/features/FleetSegment/apis.ts +++ b/frontend/src/features/FleetSegment/apis.ts @@ -32,7 +32,7 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ addFleetSegmentYear: builder.mutation({ query: nextYear => ({ method: 'POST', - url: `/bff/v1/admin/fleet_segments/${nextYear}` + url: `/admin/fleet_segments/${nextYear}` }), transformErrorResponse: response => new FrontendApiError(ADD_FLEET_SEGMENT_YEAR_ERROR_MESSAGE, response) }), @@ -51,7 +51,7 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ query: segmentFields => ({ body: segmentFields, method: 'POST', - url: '/bff/v1/admin/fleet_segments' + url: '/admin/fleet_segments' }), transformErrorResponse: response => new FrontendApiError(CREATE_FLEET_SEGMENT_ERROR_MESSAGE, response), transformResponse: (baseQueryReturnValue: FleetSegment) => FleetSegmentSchema.parse(baseQueryReturnValue) @@ -59,7 +59,7 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ deleteFleetSegment: builder.mutation({ query: ({ segment, year }) => ({ method: 'DELETE', - url: `/bff/v1/admin/fleet_segments?year=${year}&segment=${segment}` + url: `/admin/fleet_segments?year=${year}&segment=${segment}` }), transformErrorResponse: response => new FrontendApiError(DELETE_FLEET_SEGMENT_ERROR_MESSAGE, response), transformResponse: (baseQueryReturnValue: FleetSegment[]) => @@ -80,7 +80,7 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ .sort((a, b) => a.segment.localeCompare(b.segment)) }), getFleetSegmentYearEntries: builder.query({ - query: () => '/bff/v1/admin/fleet_segments/years', + query: () => '/admin/fleet_segments/years', transformErrorResponse: response => new FrontendApiError(GET_FLEET_SEGMENT_YEAR_ENTRIES_ERROR_MESSAGE, response) }), updateFleetSegment: builder.query({ diff --git a/frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx b/frontend/src/features/MainWindow/components/PreviewFilteredVessels.tsx similarity index 67% rename from frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx rename to frontend/src/features/MainWindow/components/PreviewFilteredVessels.tsx index e1f0b35a76..a9d6fcd685 100644 --- a/frontend/src/features/MainWindow/components/PreviewFilteredVessels.jsx +++ b/frontend/src/features/MainWindow/components/PreviewFilteredVessels.tsx @@ -1,22 +1,20 @@ -import React from 'react' -import styled from 'styled-components' -import { useDispatch, useSelector } from 'react-redux' import { COLORS } from '@constants/constants.js' +import { undoPreviewVessels } from '@features/Vessel/useCases/undoPreviewVessels' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { useMainAppSelector } from '@hooks/useMainAppSelector' +import styled from 'styled-components' + import BackToVesselsListSVG from '../../icons/Fleche_navigation_marees_gainsboro.svg?react' -import { undoPreviewVessels } from '@features/Vessel/useCases/undoPreviewVessels.js' -const PreviewFilteredVessels = () => { - const dispatch = useDispatch() +export function PreviewFilteredVessels() { + const dispatch = useMainAppDispatch() - const previewFilteredVesselsMode = useSelector(state => state.global.previewFilteredVesselsMode) + const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) return ( <> {previewFilteredVesselsMode ? ( - dispatch(undoPreviewVessels())} - data-cy={'back-to-vessels-list'} - > + dispatch(undoPreviewVessels())}> Revenir à la liste des navires @@ -48,5 +46,3 @@ const Preview = styled.div` padding-top: 9px; cursor: pointer; ` - -export default PreviewFilteredVessels diff --git a/frontend/src/features/MainWindow/index.tsx b/frontend/src/features/MainWindow/index.tsx index 2badf26860..2c7330204e 100644 --- a/frontend/src/features/MainWindow/index.tsx +++ b/frontend/src/features/MainWindow/index.tsx @@ -17,7 +17,7 @@ import { ControlUnitListDialog } from '../ControlUnit/components/ControlUnitList import { DrawLayerModal } from '../Draw/components/DrawModal' import { HealthcheckHeadband } from '../Healthcheck/components/HealthcheckHeadband' import { LayersSidebar } from '../LayersSidebar/components' -import PreviewFilteredVessels from './components/PreviewFilteredVessels' +import { PreviewFilteredVessels } from './components/PreviewFilteredVessels' import { SideWindowLauncher } from '../SideWindow/SideWindowLauncher' import { VesselList } from '../Vessel/components/VesselList' import { VesselLoader } from '../Vessel/components/VesselLoader' diff --git a/frontend/src/features/Mission/missionActionApi.ts b/frontend/src/features/Mission/missionActionApi.ts index f67e303636..75cc73a780 100644 --- a/frontend/src/features/Mission/missionActionApi.ts +++ b/frontend/src/features/Mission/missionActionApi.ts @@ -35,7 +35,7 @@ export const missionActionApi = monitorfishApi.injectEndpoints({ afterDateTime: fromDate, vesselId }, - url: '/bff/v1/mission_actions/controls' + url: '/mission_actions/controls' }), transformErrorResponse: response => new FrontendApiError(MISSION_ACTIONS_ERROR_MESSAGE, response) }), diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 85b9a41dc0..2e54987614 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -56,7 +56,7 @@ export const vesselApi = monitorfishApi.injectEndpoints({ vesselId, vesselIdentifier }, - url: `/bff/v1/vessels/find` + url: `/vessels/find` } }, transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), @@ -96,7 +96,7 @@ export const vesselApi = monitorfishApi.injectEndpoints({ vesselIdentifier }, - url: `/bff/v1/vessels/positions` + url: `/vessels/positions` } }, transformErrorResponse: response => new FrontendApiError(VESSEL_POSITIONS_ERROR_MESSAGE, response), From 571df76c792a8a1adfe5ae11e55e827be102b930 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 4 Feb 2025 10:51:48 +0100 Subject: [PATCH 10/21] Fix URLs after using RTK params --- .github/workflows/cicd.yml | 2 +- .../light/outputs/LogbookMessageDataOutput.kt | 52 +++++++++---------- .../api/outputs/LogbookMessageDataOutput.kt | 45 ++++++++-------- .../BaseLogbookMessageDataOutput.kt | 30 +++++++++++ .../regulation_table/table.spec.ts | 2 +- .../vessel_sidebar/control_buttons.spec.ts | 4 +- .../vessel_sidebar/ers_vms.spec.ts | 10 ++-- .../vessel_sidebar/logbook.spec.ts | 12 ++--- .../vessel_sidebar/offline_management.spec.ts | 20 ++++--- .../beacon_malfunction/board.spec.ts | 10 ++-- .../form.spec.ts | 6 +-- .../mission_form/sea_control.spec.ts | 10 ++-- .../filter_bar.spec.ts | 2 +- frontend/src/api/alert.ts | 6 +-- frontend/src/api/api.ts | 2 +- frontend/src/domain/entities/vessel/types.ts | 2 +- frontend/src/domain/types/GeoJSON.ts | 16 +++--- .../useCases/getVesselBeaconMalfunctions.ts | 2 +- .../PriorNotification/priorNotificationApi.ts | 6 +-- frontend/src/features/Regulation/slice.ts | 2 +- frontend/src/features/Regulation/types.ts | 2 +- frontend/src/features/Reporting/types.ts | 4 +- frontend/src/features/Vessel/Vessel.types.ts | 2 + .../Vessel/components/VesselLoader.tsx | 16 +++--- .../schemas/VesselLastPositionLightSchema.ts | 11 ++++ frontend/src/features/Vessel/vesselApi.ts | 1 - frontend/src/features/Vessel/vesselNavApi.ts | 8 +-- frontend/src/store/index.ts | 2 +- frontend/src/types.test.ts | 2 +- frontend/src/utils/nullify.ts | 2 +- frontend/src/utils/undefinedize.ts | 2 +- 31 files changed, 168 insertions(+), 125 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt create mode 100644 frontend/src/features/Vessel/schemas/VesselLastPositionLightSchema.ts diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 8c6cf2bae4..c0430f69ad 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -259,7 +259,7 @@ jobs: uses: actions/checkout@v4 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-upload@v2.0.0 with: image: monitorfish-app:${{ env.VERSION }} diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt index 8528a35ab1..d3f6f828d2 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt @@ -4,33 +4,32 @@ import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.LogbookMessageValue +import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.interfaces.BaseLogbookMessageDataOutput import java.time.ZonedDateTime data class LogbookMessageDataOutput( - val reportId: String? = null, - val operationNumber: String?, - val tripNumber: String? = null, - val referencedReportId: String? = null, - var isCorrected: Boolean? = false, - val operationType: LogbookOperationType, - val operationDateTime: ZonedDateTime? = null, - val activityDateTime: ZonedDateTime? = null, - val reportDateTime: ZonedDateTime? = null, - val integrationDateTime: ZonedDateTime? = null, - val internalReferenceNumber: String? = null, - val externalReferenceNumber: String? = null, - val ircs: String? = null, - val vesselName: String? = null, - /** ISO Alpha-3 country code. **/ - val flagState: String? = null, - val imo: String? = null, - val messageType: String? = null, - var acknowledgment: Acknowledgment? = null, - var deleted: Boolean? = false, - val message: LogbookMessageValue? = null, - var rawMessage: String? = null, - val isSentByFailoverSoftware: Boolean, -) { + override val reportId: String?, + override val operationNumber: String?, + override val tripNumber: String?, + override val referencedReportId: String?, + override val isCorrectedByNewerMessage: Boolean, + override val operationType: LogbookOperationType, + override val operationDateTime: ZonedDateTime?, + override val activityDateTime: ZonedDateTime?, + override val reportDateTime: ZonedDateTime?, + override val integrationDateTime: ZonedDateTime?, + override val internalReferenceNumber: String?, + override val externalReferenceNumber: String?, + override val ircs: String?, + override val vesselName: String?, + override val flagState: String?, + override val imo: String?, + override val messageType: String?, + override val acknowledgment: Acknowledgment?, + override val isDeleted: Boolean, + override val message: LogbookMessageValue?, + override val isSentByFailoverSoftware: Boolean, +): BaseLogbookMessageDataOutput { companion object { fun fromLogbookMessage(logbookMessage: LogbookMessage) = LogbookMessageDataOutput( @@ -38,9 +37,9 @@ data class LogbookMessageDataOutput( referencedReportId = logbookMessage.referencedReportId, externalReferenceNumber = logbookMessage.externalReferenceNumber, ircs = logbookMessage.ircs, - isCorrected = logbookMessage.isCorrectedByNewerMessage, + isCorrectedByNewerMessage = logbookMessage.isCorrectedByNewerMessage, acknowledgment = logbookMessage.acknowledgment, - deleted = logbookMessage.isDeleted, + isDeleted = logbookMessage.isDeleted, operationDateTime = logbookMessage.operationDateTime, activityDateTime = logbookMessage.activityDateTime, reportDateTime = logbookMessage.reportDateTime, @@ -54,7 +53,6 @@ data class LogbookMessageDataOutput( imo = logbookMessage.imo, messageType = logbookMessage.messageType, message = logbookMessage.message, - rawMessage = null, isSentByFailoverSoftware = logbookMessage.isSentByFailoverSoftware, ) } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt index 17de7b78ff..80f4d39802 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt @@ -4,34 +4,35 @@ import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.LogbookMessageValue +import fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.interfaces.BaseLogbookMessageDataOutput import java.time.ZonedDateTime data class LogbookMessageDataOutput( - val reportId: String?, - val operationNumber: String?, - val tripNumber: String?, - val referencedReportId: String?, - val operationDateTime: ZonedDateTime?, - val activityDateTime: ZonedDateTime?, - val reportDateTime: ZonedDateTime?, - val integrationDateTime: ZonedDateTime?, - val internalReferenceNumber: String?, - val externalReferenceNumber: String?, - val ircs: String?, - val vesselName: String?, - val flagState: String?, - val imo: String?, + override val reportId: String?, + override val operationNumber: String?, + override val tripNumber: String?, + override val referencedReportId: String?, + override val operationDateTime: ZonedDateTime?, + override val activityDateTime: ZonedDateTime?, + override val reportDateTime: ZonedDateTime?, + override val integrationDateTime: ZonedDateTime?, + override val internalReferenceNumber: String?, + override val externalReferenceNumber: String?, + override val ircs: String?, + override val vesselName: String?, + override val flagState: String?, + override val imo: String?, + override val acknowledgment: Acknowledgment?, + override val isCorrectedByNewerMessage: Boolean, + override val isDeleted: Boolean, + override val isSentByFailoverSoftware: Boolean, + override val message: LogbookMessageValue?, + override val messageType: String?, + override val operationType: LogbookOperationType, val rawMessage: String?, - val acknowledgment: Acknowledgment?, - val isCorrectedByNewerMessage: Boolean, - val isDeleted: Boolean, - val isSentByFailoverSoftware: Boolean, - val message: LogbookMessageValue?, - val messageType: String?, - val operationType: LogbookOperationType, val tripGears: List?, val tripSegments: List?, -) { +): BaseLogbookMessageDataOutput { companion object { fun fromLogbookMessage(logbookMessage: LogbookMessage): LogbookMessageDataOutput { val tripGears = diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt new file mode 100644 index 0000000000..c78337e811 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt @@ -0,0 +1,30 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.api.outputs.interfaces + +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.Acknowledgment +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.LogbookMessageValue +import java.time.ZonedDateTime + +interface BaseLogbookMessageDataOutput { + val reportId: String? + val operationNumber: String? + val tripNumber: String? + val referencedReportId: String? + val operationDateTime: ZonedDateTime? + val activityDateTime: ZonedDateTime? + val reportDateTime: ZonedDateTime? + val integrationDateTime: ZonedDateTime? + val internalReferenceNumber: String? + val externalReferenceNumber: String? + val ircs: String? + val vesselName: String? + val flagState: String? + val imo: String? + val acknowledgment: Acknowledgment? + val isCorrectedByNewerMessage: Boolean + val isDeleted: Boolean + val isSentByFailoverSoftware: Boolean + val message: LogbookMessageValue? + val messageType: String? + val operationType: LogbookOperationType +} diff --git a/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts b/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts index 780e052d8d..3ae4915da3 100644 --- a/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts +++ b/frontend/cypress/e2e/back_office/regulation_table/table.spec.ts @@ -7,7 +7,7 @@ context('BackOffice > Regulation Table > Table', () => { cy.wait(1000) }) - it('regulatory zones are displayed by layer name and law schemas', () => { + it('regulatory zones are displayed by layer name and law types', () => { cy.get('[data-cy="backoffice-search-regulation"]').type('dra') cy.get('[data-cy="Reg. NAMO"]').eq(0).click() cy.get('[data-cy="backoffice-search-regulation"]').type('{backspace}{backspace}{backspace}') diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/control_buttons.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/control_buttons.spec.ts index 3ea802417e..adf645b5fa 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/control_buttons.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/control_buttons.spec.ts @@ -124,8 +124,8 @@ context('Vessel sidebar controls buttons', () => { // Then cy.wait('@getPositions').then(({ request }) => { - expect(request.url).contains(`${startDateAsDayjs.format('DD')}T00:00:00.000Z`) - expect(request.url).contains(`${endDateAsDayjs.format('DD')}T23:59:59.000Z`) + expect(request.url).contains(encodeURIComponent(`${startDateAsDayjs.format('DD')}T00:00:00.000Z`)) + expect(request.url).contains(encodeURIComponent(`${endDateAsDayjs.format('DD')}T23:59:59.000Z`)) }) cy.wait(200) diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/ers_vms.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/ers_vms.spec.ts index fa11a54ab3..b4b4d55cd4 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/ers_vms.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/ers_vms.spec.ts @@ -91,8 +91,9 @@ context('Vessel sidebar ers/vms tab', () => { // Go to the detail of a beacon malfunction and go back to resume cy.intercept( 'GET', - 'bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=' + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', ).as('openVessel') openVesselBySearch('Pheno') cy.wait('@openVessel') @@ -110,8 +111,9 @@ context('Vessel sidebar ers/vms tab', () => { // Search for another vessel cy.intercept( 'GET', - 'bff/v1/vessels/find?vesselId=2&internalReferenceNumber=U_W0NTFINDME&externalReferenceNumber=TALK2ME' + - '&IRCS=QGDF&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=' + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=TALK2ME' + + '&internalReferenceNumber=U_W0NTFINDME&IRCS=QGDF&trackDepth=TWELVE_HOURS' + + '&vesselId=2&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', ).as('openVesselTwo') cy.intercept('GET', '/bff/v1/vessels/beacon_malfunctions*').as('vesselTwoBeaconMalfunctions') cy.get('*[data-cy^="vessel-search-selected-vessel-close-title"]', { timeout: 10000 }).click() diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts index 93be32fe08..c054c306eb 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts @@ -152,9 +152,9 @@ context('Vessel sidebar logbook tab', () => { .its('response.url') .should( 'have.string', - '/bff/v1/vessels/positions?internalReferenceNumber=FAK000999999' + - '&externalReferenceNumber=DONTSINK&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=CUSTOM' + - '&afterDateTime=2019-02-16T21:05:00.000Z&beforeDateTime=2019-10-15T13:01:00.000Z' + encodeURIComponent('/bff/v1/vessels/positions?afterDateTime=2019-02-16T21:05:00.000Z' + + '&beforeDateTime=2019-10-15T13:01:00.000Z&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999' + + '&IRCS=CALLME&trackDepth=CUSTOM&vesselIdentifier=INTERNAL_REFERENCE_NUMBER') ) cy.get('*[data-cy^="fishing-activity-name"]').should('exist').should('have.length', 4) @@ -171,9 +171,9 @@ context('Vessel sidebar logbook tab', () => { .its('response.url') .should( 'have.string', - '/bff/v1/vessels/positions?internalReferenceNumber=FAK000999999' + - '&externalReferenceNumber=DONTSINK&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER' + - '&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=' + encodeURIComponent('/bff/v1/vessels/positions?&afterDateTime=&beforeDateTime=' + + '&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999' + + '&IRCS=CALLME&trackDepth=TWELVE_HOURS&vesselIdentifier=INTERNAL_REFERENCE_NUMBER') ) cy.get('*[data-cy^="fishing-activity-name"]').should('not.exist') // Go back to the default track depth diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts index c452fc1d81..2af0fc5d8c 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts @@ -13,8 +13,9 @@ context('Offline management', () => { // Given cy.intercept( 'GET', - 'bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=', + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', { statusCode: 400 } ).as('openVessel') openVesselBySearch('Pheno') @@ -82,8 +83,9 @@ context('Offline management', () => { { method: 'GET', path: - '/bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=', + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', times: 2 }, { statusCode: 400 } @@ -96,8 +98,9 @@ context('Offline management', () => { cy.get('*[data-cy="vessel-sidebar-error"]').contains("Nous n'avons pas pu récupérer les informations du navire") cy.intercept( 'GET', - 'bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=' + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', ).as('openVessel') cy.clickButton('Réessayer') cy.wait('@openVessel') @@ -105,8 +108,9 @@ context('Offline management', () => { // When clicking on Resume tab cy.intercept( - 'bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=', + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', cy.spy().as('openVesselSpyed') ) cy.get('*[data-cy="vessel-menu-summary"').click() diff --git a/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts b/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts index c82f66b49b..640431ea7f 100644 --- a/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts +++ b/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts @@ -238,11 +238,11 @@ context('Side Window > Beacon Malfunction Board', () => { cy.intercept( 'GET', new RegExp( - `bff\\/v1\\/vessels\\/find\\?vesselId=1&internalReferenceNumber=FAK000999999` + - `&externalReferenceNumber=DONTSINK&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER` + - `&trackDepth=CUSTOM` + - `&afterDateTime=${oneWeeksBeforeDate.format('YYYY-MM-DD')}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z` + - `&beforeDateTime=${oneWeeksBeforePlusOneDayDate.format('YYYY-MM-DD')}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z` + `bff\\/v1\\/vessels\\/find\\?` + + `&afterDateTime=${oneWeeksBeforeDate.format('YYYY-MM-DD')}T\\d{2}%3A\\d{2}%3A\\d{2}\\.\\d{3}Z` + + `&beforeDateTime=${oneWeeksBeforePlusOneDayDate.format('YYYY-MM-DD')}T\\d{2}%3A\\d{2}%3A\\d{2}\\.\\d{3}Z` + + `&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999` + + `&IRCS=CALLME&trackDepth=CUSTOM&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER` ) ).as('showVesselPositionsOnMap') cy.get('*[data-cy="side-window-beacon-malfunctions-detail-show-vessel"]').click() diff --git a/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts b/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts index 58ae31596b..cbef9c5cc4 100644 --- a/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts +++ b/frontend/cypress/e2e/side_window/manual_prior_notification_form/form.spec.ts @@ -276,7 +276,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { cy.contains('Créer le préavis').should('be.enabled') }) - it('Should calculate and display manual prior notification fleet segments, risk factor & schemas', () => { + it('Should calculate and display manual prior notification fleet segments, risk factor & types', () => { // ------------------------------------------------------------------------- // Add @@ -402,7 +402,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { }) }) - it('Should only recalculate manual prior notification fleet segments, risk factor & schemas when necessary (creation)', () => { + it('Should only recalculate manual prior notification fleet segments, risk factor & types when necessary (creation)', () => { cy.intercept('POST', '/bff/v1/prior_notifications/manual/compute').as('computePriorNotification') cy.resetCountRequestsByAlias('@computePriorNotification') @@ -446,7 +446,7 @@ context('Side Window > Manual Prior Notification Form > Form', () => { cy.countRequestsByAlias('@computePriorNotification', 1500).should('be.equal', 1) }) - it('Should only recalculate manual prior notification fleet segments, risk factor & schemas when necessary (edition)', () => { + it('Should only recalculate manual prior notification fleet segments, risk factor & types when necessary (edition)', () => { cy.intercept('POST', '/bff/v1/prior_notifications/manual/compute').as('computePriorNotification') cy.resetCountRequestsByAlias('@computePriorNotification') 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 68511ef6fb..c0c129a32e 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 @@ -92,13 +92,9 @@ context('Side Window > Mission Form > Sea Control', () => { cy.intercept( 'GET', - '/bff/v1/vessels/find?vesselId=2&' + - 'internalReferenceNumber=U_W0NTFINDME&' + - 'externalReferenceNumber=TALK2ME&' + - 'IRCS=QGDF&' + - 'vesselIdentifier=&' + - 'trackDepth=TWELVE_HOURS&' + - 'afterDateTime=&beforeDateTime=' + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=TALK2ME' + + '&internalReferenceNumber=U_W0NTFINDME&IRCS=QGDF&trackDepth=TWELVE_HOURS' + + '&vesselId=2&vesselIdentifier=', ).as('showVessel') cy.get('a:contains("Voir la fiche")').click() cy.wait('@showVessel') diff --git a/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts b/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts index 38bf8abd91..2f054fb3bb 100644 --- a/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts +++ b/frontend/cypress/e2e/side_window/prior_notification_list/filter_bar.spec.ts @@ -227,7 +227,7 @@ context('Side Window > Prior Notification List > VesselFilter Bar', () => { cy.get('.Table-SimpleTable tr').should('have.length.to.be.greaterThan', 0) }) - it('Should filter prior notifications by schemas', () => { + it('Should filter prior notifications by types', () => { openSideWindowPriorNotificationListAsSuperUser() cy.intercept('GET', `${apiPathBase}*priorNotificationTypes=${encodeURI('Préavis type A,Préavis type C')}*`).as( diff --git a/frontend/src/api/alert.ts b/frontend/src/api/alert.ts index 9957a3af85..839bbba069 100644 --- a/frontend/src/api/alert.ts +++ b/frontend/src/api/alert.ts @@ -56,8 +56,7 @@ export const alertApi = monitorfishApi.injectEndpoints({ }), getSilencedAlerts: builder.query({ query: () => '/operational_alerts/silenced', - transformErrorResponse: response => new FrontendApiError(ALERTS_ERROR_MESSAGE, response), - transformResponse: (response: SilencedAlert[]) => response + transformErrorResponse: response => new FrontendApiError(ALERTS_ERROR_MESSAGE, response) }), silenceAlert: builder.mutation< LEGACY_SilencedAlert, @@ -71,8 +70,7 @@ export const alertApi = monitorfishApi.injectEndpoints({ method: 'PUT', url: `/operational_alerts/${id}/silence` }), - transformErrorResponse: response => new FrontendApiError(SILENCE_ALERT_ERROR_MESSAGE, response), - transformResponse: (response: SilencedAlert) => response + transformErrorResponse: response => new FrontendApiError(SILENCE_ALERT_ERROR_MESSAGE, response) }), validateAlert: builder.mutation({ query: id => ({ diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts index e7afc6dbd4..a8f854dafb 100644 --- a/frontend/src/api/api.ts +++ b/frontend/src/api/api.ts @@ -108,7 +108,7 @@ export const monitorfishApi = createApi({ if (result.error) { const error: CustomResponseError = { - path: typeof args === 'string' ? args : args.url, + path: `/bff/v1${typeof args === 'string' ? args : args.url}`, requestData: typeof args === 'string' ? undefined : args.body, responseData: result.error.data as BackendApi.ResponseBodyError, status: result.error.status diff --git a/frontend/src/domain/entities/vessel/types.ts b/frontend/src/domain/entities/vessel/types.ts index b62e166a90..7082a1136c 100644 --- a/frontend/src/domain/entities/vessel/types.ts +++ b/frontend/src/domain/entities/vessel/types.ts @@ -1,4 +1,4 @@ -// TODO This should be moved to `entities/vessel/mission.schemas.ts` +// TODO This should be moved to `entities/vessel/mission.types.ts` import type { VesselTrackDepth } from '../vesselTrackDepth' import type { SelectableVesselTrackDepth } from '@features/Vessel/components/VesselSidebar/actions/TrackRequest/types' diff --git a/frontend/src/domain/types/GeoJSON.ts b/frontend/src/domain/types/GeoJSON.ts index 0c2bd70767..eca87aa15e 100644 --- a/frontend/src/domain/types/GeoJSON.ts +++ b/frontend/src/domain/types/GeoJSON.ts @@ -1,6 +1,6 @@ /** - * Typescript schemas for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for - * ranged number data schemas. + * Typescript types for the GeoJSON RFC7946 specification. This is not fully RFC-compliant due to lack of support for + * ranged number data types. * * See https://tools.ietf.org/html/rfc7946 */ @@ -13,13 +13,13 @@ export declare namespace GeoJSON { export type GeometryType = Geometry['type'] /** - * ...the term "GeoJSON schemas" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the - * geometry schemas listed above. + * ...the term "GeoJSON types" refers to nine case-sensitive strings: "Feature", "FeatureCollection", and the + * geometry types listed above. */ export type GeoJson = Geometry | Feature | FeatureCollection export type GeoJsonType = GeoJson['type'] - // schemas + // types /** * A position is an array of numbers. There MUST be two or more elements. The first two elements are longitude and @@ -34,7 +34,7 @@ export declare namespace GeoJSON { export type Record = { [key in string | number]: unknown } /** - * Properties inherit to all GeoJSON schemas + * Properties inherit to all GeoJSON types */ export interface GeometryBase extends Record { /** @@ -55,7 +55,7 @@ export declare namespace GeoJSON { */ } - // geometry schemas + // geometry types export interface Point extends GeometryBase { /** @@ -132,7 +132,7 @@ export declare namespace GeoJSON { type: 'GeometryCollection' } - // GeoJSON schemas + // GeoJSON types export interface Feature { /** diff --git a/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts b/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts index 06c65b0894..bfb0f9bb01 100644 --- a/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts +++ b/frontend/src/features/BeaconMalfunction/useCases/getVesselBeaconMalfunctions.ts @@ -36,7 +36,7 @@ export const getVesselBeaconMalfunctions = (isFromUserAction: boolean) => async { fromDate: vesselBeaconMalfunctionsFromDate, vesselId: selectedVessel.vesselId }, RTK_FORCE_REFETCH_QUERY_OPTIONS ) - )().unwrap() + ).unwrap() dispatch( setVesselBeaconMalfunctionsResumeAndHistory({ diff --git a/frontend/src/features/PriorNotification/priorNotificationApi.ts b/frontend/src/features/PriorNotification/priorNotificationApi.ts index 6889f78254..e7bc4c0501 100644 --- a/frontend/src/features/PriorNotification/priorNotificationApi.ts +++ b/frontend/src/features/PriorNotification/priorNotificationApi.ts @@ -11,7 +11,7 @@ import type { ListFilter } from './components/PriorNotificationList/types' import type { Logbook } from '@features/Logbook/Logbook.types' const COMPUTE_PRIOR_NOTIFICATION_ERROR_MESSAGE = - "Nous n'avons pas pu calculer note de risque, segments ou schemas pour ce préavis." + "Nous n'avons pas pu calculer note de risque, segments ou types pour ce préavis." const CREATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu créé le préavis." const DELETE_PRIOR_NOTIFICATION_UPLOAD_ERROR_MESSAGE = "Nous n'avons pas pu supprimer ce document attaché." const GET_PRIOR_NOTIFICATION_UPLOADS_ERROR_MESSAGE = @@ -21,7 +21,7 @@ const GET_PRIOR_NOTIFICATION_SENT_MESSAGES_ERROR_MESSAGE = const UPDATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu modifier le préavis." const GET_PRIOR_NOTIFICATION_DETAIL_ERROR_MESSAGE = "Nous n'avons pas pu récupérer le préavis." const GET_PRIOR_NOTIFICATIONS_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des préavis." -const GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des schemas de préavis." +const GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE = "Nous n'avons pas pu récupérer la liste des types de préavis." const GET_PRIOR_NOTIFICATION_PDF_ERROR_MESSAGE = "Nous n'avons pas pu récupérer le PDF du préavis." const VERIFY_AND_SEND_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu vérifier et envoyer le préavis." const INVALIDATE_PRIOR_NOTIFICATION_ERROR_MESSAGE = "Nous n'avons pas pu invalider et envoyer le préavis." @@ -139,7 +139,7 @@ export const priorNotificationApi = monitorfishApi.injectEndpoints({ getPriorNotificationTypes: builder.query({ providesTags: () => [{ type: RtkCacheTagType.PriorNotificationTypes }], - query: () => '/prior_notifications/schemas', + query: () => '/prior_notifications/types', transformErrorResponse: response => new FrontendApiError(GET_PRIOR_NOTIFICATION_TYPES_ERROR_MESSAGE, response) }), diff --git a/frontend/src/features/Regulation/slice.ts b/frontend/src/features/Regulation/slice.ts index 4e49d62ccf..7fe4fb7966 100644 --- a/frontend/src/features/Regulation/slice.ts +++ b/frontend/src/features/Regulation/slice.ts @@ -392,7 +392,7 @@ const regulationSlice = createSlice({ } }, - // TODO Fix these schemas and find a cleaner way to achieve that. Proposal: pass a partial `RegulatoryZoneDraft` as param and use a "deepMerge" function. + // TODO Fix these types and find a cleaner way to achieve that. Proposal: pass a partial `RegulatoryZoneDraft` as param and use a "deepMerge" function. updateProcessingRegulationByKeyAndSubKey( state, action: PayloadAction<{ diff --git a/frontend/src/features/Regulation/types.ts b/frontend/src/features/Regulation/types.ts index 3e69750847..803f7bb357 100644 --- a/frontend/src/features/Regulation/types.ts +++ b/frontend/src/features/Regulation/types.ts @@ -55,7 +55,7 @@ export type TimeInterval = { to: Date | undefined } -// TODO It would be safer to use strict array schemas: `DateRange[]` and `DateAsStringRange[]`. +// TODO It would be safer to use strict array types: `DateRange[]` and `DateAsStringRange[]`. export type FishingPeriod = { always: boolean | undefined annualRecurrence: boolean | undefined diff --git a/frontend/src/features/Reporting/types.ts b/frontend/src/features/Reporting/types.ts index fa00f78256..8a8eeec0a5 100644 --- a/frontend/src/features/Reporting/types.ts +++ b/frontend/src/features/Reporting/types.ts @@ -7,7 +7,7 @@ import type { Infraction } from '../../domain/types/infraction' import type { PendingAlertValue } from '../Alert/types' import type { LegacyControlUnit } from '../ControlUnit/legacyControlUnit' -// TODO Move other schemas into new `Reporting` namespace. +// TODO Move other types into new `Reporting` namespace. export namespace Reporting { export type Reporting = InfractionSuspicionReporting | ObservationReporting | PendingAlertReporting export type EditableReporting = InfractionSuspicionReporting | ObservationReporting @@ -125,7 +125,7 @@ export type Observation = { } type ReportingTypeCharacteristic = { - // TODO It should be useless now that schemas are discriminated. + // TODO It should be useless now that types are discriminated. code: ReportingType displayName: string // TODO This should be named differently to avoid confusion with `ReportingType.INFRACTION_SUSPICION` type. diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index 703de3efc3..561f2dd739 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -4,6 +4,7 @@ import { numberOrUndefined, stringOrUndefined } from '../../types' import type { ProducerOrganizationMembership } from '@features/ProducerOrganizationMembership/types' import type { RiskFactor } from '@features/RiskFactor/types' +import type { VesselLastPositionLightSchema } from '@features/Vessel/schemas/VesselLastPositionLightSchema' import type { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' import type Feature from 'ol/Feature' import type LineString from 'ol/geom/LineString' @@ -155,6 +156,7 @@ export namespace Vessel { export type DeclaredLogbookSpecies = z.infer export type VesselLastPosition = z.infer + export type VesselLightLastPosition = z.infer export type VesselPosition = { course: number diff --git a/frontend/src/features/Vessel/components/VesselLoader.tsx b/frontend/src/features/Vessel/components/VesselLoader.tsx index 3c49b3c53e..32edc64857 100644 --- a/frontend/src/features/Vessel/components/VesselLoader.tsx +++ b/frontend/src/features/Vessel/components/VesselLoader.tsx @@ -1,14 +1,15 @@ import { FIVE_MINUTES, TWENTY_MINUTES } from '@api/APIWorker' import { FulfillingBouncingCircleSpinner } from '@components/FulfillingBouncingCircleSpinner' import { showVesselsLastPosition } from '@features/Vessel/useCases/showVesselsLastPosition' +import { Vessel } from '@features/Vessel/Vessel.types' import { useIsInLightMode } from '@hooks/useIsInLightMode' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { THEME } from '@mtes-mct/monitor-ui' import { skipToken } from '@reduxjs/toolkit/query' import { useEffect, useState } from 'react' import styled from 'styled-components' -import { COLORS } from '../../../constants/constants' import { setError } from '../../../domain/shared_slices/Global' import { MapComponent } from '../../commonStyles/MapComponent' import VesselSVG from '../../icons/Icone_navire.svg?react' @@ -43,7 +44,8 @@ export function VesselLoader() { return } - dispatch(showVesselsLastPosition(vessels)) + // TODO include the VesselLightLastPosition type to the redux state + dispatch(showVesselsLastPosition(vessels as Vessel.VesselLastPosition[])) }, [dispatch, vessels, isError, error]) useEffect(() => { @@ -56,7 +58,7 @@ export function VesselLoader() { <> {!isAppLoaded && ( - + Chargement... @@ -64,8 +66,8 @@ export function VesselLoader() { {(isFetching || loadingPositions) && isAppLoaded && ( <> - - + + )} @@ -76,12 +78,12 @@ export function VesselLoader() { const Text = styled.span` margin-top: 10px; font-size: 13px; - color: ${COLORS.white}; + color: ${p => p.theme.color.white}; bottom: -17px; position: relative; ` -const Vessel = styled(VesselSVG)` +const VesselIcon = styled(VesselSVG)` position: absolute; top: 2px; left: 8px; diff --git a/frontend/src/features/Vessel/schemas/VesselLastPositionLightSchema.ts b/frontend/src/features/Vessel/schemas/VesselLastPositionLightSchema.ts new file mode 100644 index 0000000000..35914863c2 --- /dev/null +++ b/frontend/src/features/Vessel/schemas/VesselLastPositionLightSchema.ts @@ -0,0 +1,11 @@ +import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' + +export const VesselLastPositionLightSchema = VesselLastPositionSchema.omit({ + alerts: true, + detectabilityRiskFactor: true, + impactRiskFactor: true, + postControlComment: true, + probabilityRiskFactor: true, + reportings: true, + riskFactor: true +}) diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 2e54987614..9b1d15968e 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -85,7 +85,6 @@ export const vesselApi = monitorfishApi.injectEndpoints({ return { method: 'GET', - // Pass query parameters params: { afterDateTime, beforeDateTime, diff --git a/frontend/src/features/Vessel/vesselNavApi.ts b/frontend/src/features/Vessel/vesselNavApi.ts index b96691d722..aa6bad5944 100644 --- a/frontend/src/features/Vessel/vesselNavApi.ts +++ b/frontend/src/features/Vessel/vesselNavApi.ts @@ -1,13 +1,13 @@ import { monitorfishLightApi } from '@api/api' -import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' +import { VesselLastPositionLightSchema } from '@features/Vessel/schemas/VesselLastPositionLightSchema' import { Vessel } from '@features/Vessel/Vessel.types' export const vesselNavApi = monitorfishLightApi.injectEndpoints({ endpoints: builder => ({ - getVesselsLastPositions: builder.query({ + getVesselsLastPositions: builder.query({ query: () => `/v1/vessels`, - transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => - baseQueryReturnValue.map(LastPosition => VesselLastPositionSchema.parse(LastPosition)) + transformResponse: (baseQueryReturnValue: Vessel.VesselLightLastPosition[]) => + baseQueryReturnValue.map(LastPosition => VesselLastPositionLightSchema.parse(LastPosition)) }) }) }) diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts index df4d4bbca0..6a23f6c82a 100644 --- a/frontend/src/store/index.ts +++ b/frontend/src/store/index.ts @@ -55,7 +55,7 @@ setupListeners(mainStore.dispatch) export const mainStorePersistor = persistStore(mainStore) // https://react-redux.js.org/using-react-redux/usage-with-typescript#define-root-state-and-dispatch-types -// Infer the `MainRootState` and `AppDispatch` schemas from the store itself +// Infer the `MainRootState` and `AppDispatch` types from the store itself export type MainAppDispatch = typeof mainStore.dispatch export type MainAppGetState = () => MainRootState export type MainAppThunk = ThunkAction diff --git a/frontend/src/types.test.ts b/frontend/src/types.test.ts index 595e50c0ac..81bb58693b 100644 --- a/frontend/src/types.test.ts +++ b/frontend/src/types.test.ts @@ -24,7 +24,7 @@ describe('types', () => { /** * This test is required by jest to pass the error : "Your test suite must contain at least one test." * - * The actual tests are running when checkin TS schemas (by `npm run test:type`): + * The actual tests are running when checkin TS types (by `npm run test:type`): * - undefinableTestInterface * - undefinableTestInterfaceWithMissingProp */ diff --git a/frontend/src/utils/nullify.ts b/frontend/src/utils/nullify.ts index e115e2f018..bfedc57cb7 100644 --- a/frontend/src/utils/nullify.ts +++ b/frontend/src/utils/nullify.ts @@ -30,7 +30,7 @@ const nullifyObjectPropPair = ([key, value]: [string, NativeAny]) => [key, nulli * Transform all `undefined` values into `null` ones in any type of value * * @description - * The value must be of native type and only contains native schemas. + * The value must be of native type and only contains native types. */ export function nullify(value: T): Nullify | null { if (value === null || value === undefined) { diff --git a/frontend/src/utils/undefinedize.ts b/frontend/src/utils/undefinedize.ts index 1efff35753..42b8a6fb8d 100644 --- a/frontend/src/utils/undefinedize.ts +++ b/frontend/src/utils/undefinedize.ts @@ -31,7 +31,7 @@ const undefinedizeObjectPropPair = ([key, value]: [string, NativeAny]) => [key, * Transform all `null` values into `undefined` ones in any type of value * * @description - * The value must be of native type and only contains native schemas. + * The value must be of native type and only contains native types. */ export function undefinedize(value: T): Undefinedized | undefined { if (value === null || value === undefined) { From bbf7384edb42415ea43c35b3f23ec473ea78fc90 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 4 Feb 2025 12:55:01 +0100 Subject: [PATCH 11/21] safe parse with zod --- .github/workflows/cicd.yml | 2 +- .../vessel_sidebar/logbook.spec.ts | 10 ++--- .../vessel_sidebar/offline_management.spec.ts | 2 +- .../beacon_malfunction/board.spec.ts | 2 +- frontend/src/api/alert.ts | 2 +- frontend/src/features/FleetSegment/apis.ts | 25 ++++++----- frontend/src/features/Vessel/vesselApi.ts | 3 +- frontend/src/features/Vessel/vesselNavApi.ts | 3 +- .../__tests__/parseResponseOrReturn.test.ts | 44 +++++++++++++++++++ frontend/src/utils/parseResponseOrReturn.ts | 23 ++++++++++ 10 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 frontend/src/utils/__tests__/parseResponseOrReturn.test.ts create mode 100644 frontend/src/utils/parseResponseOrReturn.ts diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index c0430f69ad..6d02e534c1 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -259,7 +259,7 @@ jobs: uses: actions/checkout@v4 - name: Download image - uses: ishworkh/container-image-artifact-upload@v2.0.0 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: image: monitorfish-app:${{ env.VERSION }} diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts index c054c306eb..b5ce681a2e 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts @@ -152,9 +152,9 @@ context('Vessel sidebar logbook tab', () => { .its('response.url') .should( 'have.string', - encodeURIComponent('/bff/v1/vessels/positions?afterDateTime=2019-02-16T21:05:00.000Z' + - '&beforeDateTime=2019-10-15T13:01:00.000Z&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999' + - '&IRCS=CALLME&trackDepth=CUSTOM&vesselIdentifier=INTERNAL_REFERENCE_NUMBER') + `/bff/v1/vessels/positions?afterDateTime=${encodeURIComponent('2019-02-16T21:05:00.000Z')}` + + `&beforeDateTime=${encodeURIComponent('2019-10-15T13:01:00.000Z')}&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999` + + '&IRCS=CALLME&trackDepth=CUSTOM&vesselIdentifier=INTERNAL_REFERENCE_NUMBER' ) cy.get('*[data-cy^="fishing-activity-name"]').should('exist').should('have.length', 4) @@ -171,9 +171,9 @@ context('Vessel sidebar logbook tab', () => { .its('response.url') .should( 'have.string', - encodeURIComponent('/bff/v1/vessels/positions?&afterDateTime=&beforeDateTime=' + + '/bff/v1/vessels/positions?&afterDateTime=&beforeDateTime=' + '&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999' + - '&IRCS=CALLME&trackDepth=TWELVE_HOURS&vesselIdentifier=INTERNAL_REFERENCE_NUMBER') + '&IRCS=CALLME&trackDepth=TWELVE_HOURS&vesselIdentifier=INTERNAL_REFERENCE_NUMBER' ) cy.get('*[data-cy^="fishing-activity-name"]').should('not.exist') // Go back to the default track depth diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts index 2af0fc5d8c..b49ec92c38 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/offline_management.spec.ts @@ -83,7 +83,7 @@ context('Offline management', () => { { method: 'GET', path: - 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '/bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS' + '&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER', times: 2 diff --git a/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts b/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts index 640431ea7f..bc4011c566 100644 --- a/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts +++ b/frontend/cypress/e2e/side_window/beacon_malfunction/board.spec.ts @@ -239,7 +239,7 @@ context('Side Window > Beacon Malfunction Board', () => { 'GET', new RegExp( `bff\\/v1\\/vessels\\/find\\?` + - `&afterDateTime=${oneWeeksBeforeDate.format('YYYY-MM-DD')}T\\d{2}%3A\\d{2}%3A\\d{2}\\.\\d{3}Z` + + `afterDateTime=${oneWeeksBeforeDate.format('YYYY-MM-DD')}T\\d{2}%3A\\d{2}%3A\\d{2}\\.\\d{3}Z` + `&beforeDateTime=${oneWeeksBeforePlusOneDayDate.format('YYYY-MM-DD')}T\\d{2}%3A\\d{2}%3A\\d{2}\\.\\d{3}Z` + `&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999` + `&IRCS=CALLME&trackDepth=CUSTOM&vesselId=1&vesselIdentifier=INTERNAL_REFERENCE_NUMBER` diff --git a/frontend/src/api/alert.ts b/frontend/src/api/alert.ts index 839bbba069..11166bbc64 100644 --- a/frontend/src/api/alert.ts +++ b/frontend/src/api/alert.ts @@ -50,7 +50,7 @@ export const alertApi = monitorfishApi.injectEndpoints({ transformErrorResponse: response => new FrontendApiError(DELETE_SILENCED_ALERT_ERROR_MESSAGE, response) }), getOperationalAlerts: builder.query({ - query: () => '/bff/v1/operational_alerts', + query: () => '/operational_alerts', transformErrorResponse: response => new FrontendApiError(ALERTS_ERROR_MESSAGE, response), transformResponse: (response: PendingAlert[]) => response.map(normalizePendingAlert) }), diff --git a/frontend/src/features/FleetSegment/apis.ts b/frontend/src/features/FleetSegment/apis.ts index 2e1e891e63..cb1330eff6 100644 --- a/frontend/src/features/FleetSegment/apis.ts +++ b/frontend/src/features/FleetSegment/apis.ts @@ -3,6 +3,7 @@ import { FleetSegmentSchema } from '@features/FleetSegment/types' import { MissionAction } from '@features/Mission/missionAction.types' import { FrontendApiError } from '@libs/FrontendApiError' import { customDayjs } from '@mtes-mct/monitor-ui' +import { parseResponseOrReturn } from '@utils/parseResponseOrReturn' import type { FleetSegment } from '@features/FleetSegment/types' @@ -43,9 +44,9 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ url: `/fleet_segments/compute` }), transformResponse: (baseQueryReturnValue: FleetSegment[]) => - baseQueryReturnValue - .map(segment => FleetSegmentSchema.parse(segment)) - .sort((a, b) => a.segment.localeCompare(b.segment)) + parseResponseOrReturn(baseQueryReturnValue, FleetSegmentSchema, true).sort((a, b) => + a.segment.localeCompare(b.segment) + ) }), createFleetSegment: builder.mutation({ query: segmentFields => ({ @@ -54,7 +55,8 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ url: '/admin/fleet_segments' }), transformErrorResponse: response => new FrontendApiError(CREATE_FLEET_SEGMENT_ERROR_MESSAGE, response), - transformResponse: (baseQueryReturnValue: FleetSegment) => FleetSegmentSchema.parse(baseQueryReturnValue) + transformResponse: (baseQueryReturnValue: FleetSegment) => + parseResponseOrReturn(baseQueryReturnValue, FleetSegmentSchema, false) }), deleteFleetSegment: builder.mutation({ query: ({ segment, year }) => ({ @@ -63,9 +65,9 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ }), transformErrorResponse: response => new FrontendApiError(DELETE_FLEET_SEGMENT_ERROR_MESSAGE, response), transformResponse: (baseQueryReturnValue: FleetSegment[]) => - baseQueryReturnValue - .map(segment => FleetSegmentSchema.parse(segment)) - .sort((a, b) => a.segment.localeCompare(b.segment)) + parseResponseOrReturn(baseQueryReturnValue, FleetSegmentSchema, true).sort((a, b) => + a.segment.localeCompare(b.segment) + ) }), getFleetSegments: builder.query({ providesTags: () => [{ type: 'FleetSegments' }], @@ -75,9 +77,9 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ return `fleet_segments/${controlledYear}` }, transformResponse: (baseQueryReturnValue: FleetSegment[]) => - baseQueryReturnValue - .map(segment => FleetSegmentSchema.parse(segment)) - .sort((a, b) => a.segment.localeCompare(b.segment)) + parseResponseOrReturn(baseQueryReturnValue, FleetSegmentSchema, true).sort((a, b) => + a.segment.localeCompare(b.segment) + ) }), getFleetSegmentYearEntries: builder.query({ query: () => '/admin/fleet_segments/years', @@ -94,7 +96,8 @@ export const fleetSegmentApi = monitorfishApi.injectEndpoints({ } }, transformErrorResponse: response => new FrontendApiError(UPDATE_FLEET_SEGMENT_ERROR_MESSAGE, response), - transformResponse: (baseQueryReturnValue: FleetSegment) => FleetSegmentSchema.parse(baseQueryReturnValue) + transformResponse: (baseQueryReturnValue: FleetSegment) => + parseResponseOrReturn(baseQueryReturnValue, FleetSegmentSchema, false) }) }) }) diff --git a/frontend/src/features/Vessel/vesselApi.ts b/frontend/src/features/Vessel/vesselApi.ts index 9b1d15968e..e82840be43 100644 --- a/frontend/src/features/Vessel/vesselApi.ts +++ b/frontend/src/features/Vessel/vesselApi.ts @@ -4,6 +4,7 @@ import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPos import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { FrontendApiError } from '@libs/FrontendApiError' import { getUrlOrPathWithQueryParams } from '@utils/getUrlOrPathWithQueryParams' +import { parseResponseOrReturn } from '@utils/parseResponseOrReturn' import { displayedErrorActions } from 'domain/shared_slices/DisplayedError' import { displayOrLogError } from 'domain/use_cases/error/displayOrLogError' @@ -140,7 +141,7 @@ export const vesselApi = monitorfishApi.injectEndpoints({ getVesselsLastPositions: builder.query({ query: () => `/vessels`, transformResponse: (baseQueryReturnValue: Vessel.VesselLastPosition[]) => - baseQueryReturnValue.map(LastPosition => VesselLastPositionSchema.parse(LastPosition)) + parseResponseOrReturn(baseQueryReturnValue, VesselLastPositionSchema, true) }), searchVessels: builder.query({ diff --git a/frontend/src/features/Vessel/vesselNavApi.ts b/frontend/src/features/Vessel/vesselNavApi.ts index aa6bad5944..d4c62c5ba4 100644 --- a/frontend/src/features/Vessel/vesselNavApi.ts +++ b/frontend/src/features/Vessel/vesselNavApi.ts @@ -1,13 +1,14 @@ import { monitorfishLightApi } from '@api/api' import { VesselLastPositionLightSchema } from '@features/Vessel/schemas/VesselLastPositionLightSchema' import { Vessel } from '@features/Vessel/Vessel.types' +import { parseResponseOrReturn } from '@utils/parseResponseOrReturn' export const vesselNavApi = monitorfishLightApi.injectEndpoints({ endpoints: builder => ({ getVesselsLastPositions: builder.query({ query: () => `/v1/vessels`, transformResponse: (baseQueryReturnValue: Vessel.VesselLightLastPosition[]) => - baseQueryReturnValue.map(LastPosition => VesselLastPositionLightSchema.parse(LastPosition)) + parseResponseOrReturn(baseQueryReturnValue, VesselLastPositionLightSchema, true) }) }) }) diff --git a/frontend/src/utils/__tests__/parseResponseOrReturn.test.ts b/frontend/src/utils/__tests__/parseResponseOrReturn.test.ts new file mode 100644 index 0000000000..40cf6bdc7e --- /dev/null +++ b/frontend/src/utils/__tests__/parseResponseOrReturn.test.ts @@ -0,0 +1,44 @@ +import { FleetSegmentSchema } from '@features/FleetSegment/types' +import { VesselLastPositionSchema } from '@features/Vessel/schemas/VesselLastPositionSchema' +import { expect } from '@jest/globals' +import { parseResponseOrReturn } from '@utils/parseResponseOrReturn' + +describe('utils/parseResponseOrReturn()', () => { + it('should return the original response and print an error', () => { + const object = { dummy: true } + + const result = parseResponseOrReturn(object, VesselLastPositionSchema, false) + + expect(result).toStrictEqual(object) + }) + + it('should return the original response and print an error with an array', () => { + const object = { dummy: true } + + const result = parseResponseOrReturn([object], VesselLastPositionSchema, true) + + expect(result).toStrictEqual([object]) + }) + + it('should validate the type successfully', () => { + const segment = { + faoAreas: [], + gears: [], + impactRiskFactor: 2.0, + mainScipSpeciesType: undefined, + maxMesh: undefined, + minMesh: undefined, + minShareOfTargetSpecies: undefined, + priority: 0, + segment: '', + segmentName: '', + targetSpecies: [], + vesselTypes: [], + year: 2021 + } + + const result = parseResponseOrReturn(segment, FleetSegmentSchema, false) + + expect(result).toStrictEqual(segment) + }) +}) diff --git a/frontend/src/utils/parseResponseOrReturn.ts b/frontend/src/utils/parseResponseOrReturn.ts new file mode 100644 index 0000000000..a768d8eece --- /dev/null +++ b/frontend/src/utils/parseResponseOrReturn.ts @@ -0,0 +1,23 @@ +import { FrontendError } from '@libs/FrontendError' +import { ZodSchema } from 'zod' + +export function parseResponseOrReturn(body: unknown, schema: ZodSchema, isArray: false): T +export function parseResponseOrReturn(body: unknown, schema: ZodSchema, isArray: true): T[] +export function parseResponseOrReturn(body: unknown, schema: ZodSchema, isArray: boolean): T | T[] { + try { + if (!isArray) { + return schema.parse(body) + } + + if (!Array.isArray(body)) { + throw new Error('Expected an array for parsing.') + } + + return body.map(bodyElement => schema.parse(bodyElement)) + } catch (e) { + // eslint-disable-next-line no-new + new FrontendError('Failing validating type', e) + + return body as T | T[] + } +} From b59812636c02b80d43f99be918ea4349666bdc8a Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 4 Feb 2025 15:32:07 +0100 Subject: [PATCH 12/21] Finalize not nullable types --- .../entities/last_position/LastPosition.kt | 16 +++-- .../domain/entities/logbook/LogbookMessage.kt | 2 +- .../logbook/LogbookTransmissionFormat.kt | 1 + .../entities/risk_factor/VesselRiskFactor.kt | 6 +- .../domain/entities/vessel/Vessel.kt | 2 +- .../monitorfish/domain/mappers/ERSMapper.kt | 2 +- .../CreateOrUpdateManualPriorNotification.kt | 2 +- .../reporting/GetVesselReportings.kt | 9 +-- .../use_cases/vessel/GetVesselRiskFactor.kt | 11 ++-- .../light/outputs/LastPositionDataOutput.kt | 4 +- .../light/outputs/LogbookMessageDataOutput.kt | 4 +- .../api/light/outputs/VesselDataOutput.kt | 2 +- .../api/outputs/LastPositionDataOutput.kt | 17 +++--- .../api/outputs/LogbookMessageDataOutput.kt | 4 +- .../api/outputs/RiskFactorDataOutput.kt | 4 +- .../api/outputs/VesselDataOutput.kt | 2 +- .../BaseLogbookMessageDataOutput.kt | 2 +- .../database/entities/LastPositionEntity.kt | 12 ++-- .../database/entities/LogbookReportEntity.kt | 29 +++------ .../entities/ManualPriorNotificationEntity.kt | 2 +- .../database/entities/MissionActionEntity.kt | 2 +- .../database/entities/RiskFactorsEntity.kt | 22 +++---- .../database/entities/VesselEntity.kt | 2 +- .../database/entities/converters/Utils.kt | 21 +++++++ .../V0.290__Update_last_positions_table.sql | 30 ++++++++++ .../testdata/V666.2__Insert_dummy_vessels.sql | 60 +++++++++---------- .../V666.3__Insert_dummy_last_positions.sql | 8 +-- .../json/V666.2__Insert_dummy_vessels.jsonc | 31 ++++++++++ .../monitorfish/fakers/LogbookMessageFaker.kt | 2 +- .../cnsp/monitorfish/fakers/VesselFaker.kt | 2 +- ...ManualPriorNotificationRepositoryITests.kt | 7 +-- .../vessel_sidebar/logbook.spec.ts | 2 +- .../alert_list/alerts_list.spec.ts | 4 +- frontend/src/features/Vessel/Vessel.types.ts | 2 +- .../schemas/VesselLastPositionSchema.ts | 13 ++-- 35 files changed, 206 insertions(+), 135 deletions(-) create mode 100644 backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/converters/Utils.kt create mode 100644 backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/last_position/LastPosition.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/last_position/LastPosition.kt index e5ab6bb5c9..baa8de0969 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/last_position/LastPosition.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/last_position/LastPosition.kt @@ -2,6 +2,10 @@ package fr.gouv.cnsp.monitorfish.domain.entities.last_position import com.neovisionaries.i18n.CountryCode import fr.gouv.cnsp.monitorfish.domain.entities.position.PositionType +import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.defaultDetectabilityRiskFactor +import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.defaultImpactRiskFactor +import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.defaultProbabilityRiskFactor +import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.defaultRiskFactor import fr.gouv.cnsp.monitorfish.domain.entities.vessel.VesselIdentifier import java.time.Duration import java.time.ZonedDateTime @@ -41,17 +45,17 @@ data class LastPosition( val gearOnboard: List? = listOf(), val segments: List? = listOf(), val speciesOnboard: List? = listOf(), - val totalWeightOnboard: Double? = null, + val totalWeightOnboard: Double = 0.0, val lastControlDateTime: ZonedDateTime? = null, val lastControlInfraction: Boolean? = null, val postControlComment: String? = null, val vesselIdentifier: VesselIdentifier, - val impactRiskFactor: Double? = null, - val probabilityRiskFactor: Double? = null, - val detectabilityRiskFactor: Double? = null, - val riskFactor: Double? = null, + val impactRiskFactor: Double = defaultImpactRiskFactor, + val probabilityRiskFactor: Double = defaultProbabilityRiskFactor, + val detectabilityRiskFactor: Double = defaultDetectabilityRiskFactor, + val riskFactor: Double = defaultRiskFactor, val underCharter: Boolean? = null, - val isAtPort: Boolean? = null, + val isAtPort: Boolean = false, val alerts: List? = listOf(), val beaconMalfunctionId: Int? = null, val reportings: List = listOf(), diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt index edd08fedcb..2d4451df24 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookMessage.kt @@ -26,7 +26,7 @@ data class LogbookMessage( // Reception date of the report by the data center val integrationDateTime: ZonedDateTime, var rawMessage: String? = null, - val transmissionFormat: LogbookTransmissionFormat?, + val transmissionFormat: LogbookTransmissionFormat, val software: String? = null, var acknowledgment: Acknowledgment? = null, var isCorrectedByNewerMessage: Boolean = false, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookTransmissionFormat.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookTransmissionFormat.kt index d30ec9a54c..930b9b5525 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookTransmissionFormat.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/logbook/LogbookTransmissionFormat.kt @@ -1,6 +1,7 @@ package fr.gouv.cnsp.monitorfish.domain.entities.logbook enum class LogbookTransmissionFormat { + MANUAL, ERS, FLUX, } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt index 8635a16fa9..7bb7de2400 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/risk_factor/VesselRiskFactor.kt @@ -34,9 +34,9 @@ data class VesselRiskFactor( /** CFR (Community Fleet Register Number). */ val internalReferenceNumber: String? = null, val vesselId: Int? = null, - val gearOnboard: List? = listOf(), - val speciesOnboard: List? = listOf(), - val totalWeightOnboard: Double? = null, + val gearOnboard: List = listOf(), + val speciesOnboard: List = listOf(), + val totalWeightOnboard: Double = 0.0, val segments: List = listOf(), val probableSegments: List? = listOf(), val segmentHighestImpact: String? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/vessel/Vessel.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/vessel/Vessel.kt index a319652208..b01b8f0923 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/vessel/Vessel.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/entities/vessel/Vessel.kt @@ -32,7 +32,7 @@ data class Vessel( val vesselType: String? = null, val sailingCategory: String? = null, val sailingType: String? = null, - val declaredFishingGears: List? = null, + val declaredFishingGears: List = listOf(), val pinger: Boolean? = null, val navigationLicenceExpirationDate: Date? = null, val navigationLicenceExtensionDate: Date? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/mappers/ERSMapper.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/mappers/ERSMapper.kt index ae16174384..db87d1430c 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/mappers/ERSMapper.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/mappers/ERSMapper.kt @@ -10,7 +10,7 @@ import org.springframework.stereotype.Component @Component object ERSMapper { - private const val JSONB_NULL_STRING = "null" + const val JSONB_NULL_STRING = "null" fun getERSMessageValueFromJSON( mapper: ObjectMapper, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt index 14ac649a9e..922efe7eec 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/prior_notification/CreateOrUpdateManualPriorNotification.kt @@ -122,7 +122,7 @@ class CreateOrUpdateManualPriorNotification( reportDateTime = sentAt, integrationDateTime = ZonedDateTime.now(), rawMessage = null, - transmissionFormat = null, + transmissionFormat = LogbookTransmissionFormat.MANUAL, software = null, acknowledgment = null, isCorrectedByNewerMessage = false, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/reporting/GetVesselReportings.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/reporting/GetVesselReportings.kt index 53b1ae6458..abe1a34dd5 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/reporting/GetVesselReportings.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/reporting/GetVesselReportings.kt @@ -78,10 +78,11 @@ class GetVesselReportings( logger.info("TIME_RECORD - 'archivedYearsToReportings' took $archivedYearsToReportingsTimeTaken") val twelveMonthsAgo = ZonedDateTime.now().minusMonths(12) - val lastTwelveMonthsReportings = reportings.filter { reporting -> - reporting.validationDate?.isAfter(twelveMonthsAgo) - ?: reporting.creationDate.isAfter(twelveMonthsAgo) - } + val lastTwelveMonthsReportings = + reportings.filter { reporting -> + reporting.validationDate?.isAfter(twelveMonthsAgo) + ?: reporting.creationDate.isAfter(twelveMonthsAgo) + } val (infractionSuspicionsSummary, infractionSuspicionsSummaryTimeTaken) = measureTimedValue { diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt index d34c4218bb..32fa7e1661 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/domain/use_cases/vessel/GetVesselRiskFactor.kt @@ -15,11 +15,12 @@ class GetVesselRiskFactor( private val logger: Logger = LoggerFactory.getLogger(GetVesselRiskFactor::class.java) fun execute(internalReferenceNumber: String): VesselRiskFactor { - val riskFactor = riskFactorRepository.findByInternalReferenceNumber(internalReferenceNumber) - ?: throw BackendUsageException( - BackendUsageErrorCode.NOT_FOUND_BUT_OK, - message = "No risk factor found for vessel $internalReferenceNumber", - ) + val riskFactor = + riskFactorRepository.findByInternalReferenceNumber(internalReferenceNumber) + ?: throw BackendUsageException( + BackendUsageErrorCode.NOT_FOUND_BUT_OK, + message = "No risk factor found for vessel $internalReferenceNumber", + ) return riskFactor } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LastPositionDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LastPositionDataOutput.kt index 61c93a272f..9426642105 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LastPositionDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LastPositionDataOutput.kt @@ -37,12 +37,12 @@ data class LastPositionDataOutput( val gearOnboard: List? = null, val segments: List? = listOf(), val speciesOnboard: List? = null, - val totalWeightOnboard: Double? = null, + val totalWeightOnboard: Double, val lastControlDateTime: ZonedDateTime? = null, val lastControlInfraction: Boolean? = null, val vesselIdentifier: VesselIdentifier, val underCharter: Boolean? = null, - val isAtPort: Boolean? = null, + val isAtPort: Boolean, val beaconMalfunctionId: Int? = null, ) { companion object { diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt index d3f6f828d2..32a778e751 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/LogbookMessageDataOutput.kt @@ -17,7 +17,7 @@ data class LogbookMessageDataOutput( override val operationDateTime: ZonedDateTime?, override val activityDateTime: ZonedDateTime?, override val reportDateTime: ZonedDateTime?, - override val integrationDateTime: ZonedDateTime?, + override val integrationDateTime: ZonedDateTime, override val internalReferenceNumber: String?, override val externalReferenceNumber: String?, override val ircs: String?, @@ -29,7 +29,7 @@ data class LogbookMessageDataOutput( override val isDeleted: Boolean, override val message: LogbookMessageValue?, override val isSentByFailoverSoftware: Boolean, -): BaseLogbookMessageDataOutput { +) : BaseLogbookMessageDataOutput { companion object { fun fromLogbookMessage(logbookMessage: LogbookMessage) = LogbookMessageDataOutput( diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/VesselDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/VesselDataOutput.kt index b2e5f7aad3..d0deb7577f 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/VesselDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/light/outputs/VesselDataOutput.kt @@ -25,7 +25,7 @@ data class VesselDataOutput( val vesselType: String? = null, val sailingCategory: String? = null, val sailingType: String? = null, - val declaredFishingGears: List? = null, + val declaredFishingGears: List, val pinger: Boolean? = null, val navigationLicenceExpirationDate: Date? = null, val navigationLicenceExtensionDate: Date? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt index 6210d24dc9..4a2b34b3e6 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LastPositionDataOutput.kt @@ -35,17 +35,17 @@ data class LastPositionDataOutput( val gearOnboard: List, val segments: List, val speciesOnboard: List, - val totalWeightOnboard: Double? = null, + val totalWeightOnboard: Double, val lastControlDateTime: ZonedDateTime? = null, val lastControlInfraction: Boolean? = null, val postControlComment: String? = null, val vesselIdentifier: VesselIdentifier, - val impactRiskFactor: Double? = null, - val probabilityRiskFactor: Double? = null, - val detectabilityRiskFactor: Double? = null, - val riskFactor: Double? = null, + val impactRiskFactor: Double, + val probabilityRiskFactor: Double, + val detectabilityRiskFactor: Double, + val riskFactor: Double, val underCharter: Boolean? = null, - val isAtPort: Boolean? = null, + val isAtPort: Boolean, val alerts: List, val beaconMalfunctionId: Int? = null, val reportings: List = listOf(), @@ -77,8 +77,9 @@ data class LastPositionDataOutput( registryPortName = position.registryPortName, district = position.district, districtCode = position.districtCode, - gearOnboard = position.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } - ?: listOf(), + gearOnboard = + position.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } + ?: listOf(), segments = position.segments ?: listOf(), speciesOnboard = position.speciesOnboard?.map { diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt index 80f4d39802..e862ed2995 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/LogbookMessageDataOutput.kt @@ -15,7 +15,7 @@ data class LogbookMessageDataOutput( override val operationDateTime: ZonedDateTime?, override val activityDateTime: ZonedDateTime?, override val reportDateTime: ZonedDateTime?, - override val integrationDateTime: ZonedDateTime?, + override val integrationDateTime: ZonedDateTime, override val internalReferenceNumber: String?, override val externalReferenceNumber: String?, override val ircs: String?, @@ -32,7 +32,7 @@ data class LogbookMessageDataOutput( val rawMessage: String?, val tripGears: List?, val tripSegments: List?, -): BaseLogbookMessageDataOutput { +) : BaseLogbookMessageDataOutput { companion object { fun fromLogbookMessage(logbookMessage: LogbookMessage): LogbookMessageDataOutput { val tripGears = diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt index 70f04c29bd..332fafc211 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/RiskFactorDataOutput.kt @@ -26,7 +26,9 @@ data class RiskFactorDataOutput( companion object { fun fromVesselRiskFactor(vesselRiskFactor: VesselRiskFactor) = RiskFactorDataOutput( - gearOnboard = vesselRiskFactor.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } ?: listOf(), + gearOnboard = + vesselRiskFactor.gearOnboard?.map { GearLastPositionDataOutput.fromGearLastPosition(it) } + ?: listOf(), segments = vesselRiskFactor.segments, segmentHighestImpact = vesselRiskFactor.segmentHighestImpact, segmentHighestPriority = vesselRiskFactor.segmentHighestPriority, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/VesselDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/VesselDataOutput.kt index 74429aa0dd..56f0e5f3ce 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/VesselDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/VesselDataOutput.kt @@ -26,7 +26,7 @@ data class VesselDataOutput( val vesselType: String? = null, val sailingCategory: String? = null, val sailingType: String? = null, - val declaredFishingGears: List? = null, + val declaredFishingGears: List, val pinger: Boolean? = null, val navigationLicenceExpirationDate: Date? = null, val navigationLicenceExtensionDate: Date? = null, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt index c78337e811..f04268eb37 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/api/outputs/interfaces/BaseLogbookMessageDataOutput.kt @@ -13,7 +13,7 @@ interface BaseLogbookMessageDataOutput { val operationDateTime: ZonedDateTime? val activityDateTime: ZonedDateTime? val reportDateTime: ZonedDateTime? - val integrationDateTime: ZonedDateTime? + val integrationDateTime: ZonedDateTime val internalReferenceNumber: String? val externalReferenceNumber: String? val ircs: String? diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt index 1a6afc664b..80e0baf6e1 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LastPositionEntity.kt @@ -82,7 +82,7 @@ data class LastPositionEntity( @Column(name = "species_onboard", columnDefinition = "jsonb") val speciesOnboard: String? = null, @Column(name = "total_weight_onboard") - val totalWeightOnboard: Double? = null, + val totalWeightOnboard: Double, @Column(name = "last_control_datetime_utc") val lastControlDateTime: ZonedDateTime? = null, @Column(name = "last_control_infraction") @@ -93,17 +93,17 @@ data class LastPositionEntity( @Enumerated(EnumType.STRING) val vesselIdentifier: VesselIdentifier, @Column(name = "impact_risk_factor") - val impactRiskFactor: Double? = null, + val impactRiskFactor: Double, @Column(name = "probability_risk_factor") - val probabilityRiskFactor: Double? = null, + val probabilityRiskFactor: Double, @Column(name = "detectability_risk_factor") - val detectabilityRiskFactor: Double? = null, + val detectabilityRiskFactor: Double, @Column(name = "risk_factor") - val riskFactor: Double? = null, + val riskFactor: Double, @Column(name = "under_charter") val underCharter: Boolean? = null, @Column(name = "is_at_port") - val isAtPort: Boolean? = null, + val isAtPort: Boolean, @Column(name = "alerts", columnDefinition = "varchar(200)[]") val alerts: List? = listOf(), @Column(name = "beacon_malfunction_id") diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt index fb4e80642d..6e8cb99ccd 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/LogbookReportEntity.kt @@ -2,7 +2,9 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.entities import com.fasterxml.jackson.databind.ObjectMapper import fr.gouv.cnsp.monitorfish.domain.entities.logbook.* +import fr.gouv.cnsp.monitorfish.domain.mappers.ERSMapper.JSONB_NULL_STRING import fr.gouv.cnsp.monitorfish.domain.mappers.ERSMapper.getERSMessageValueFromJSON +import fr.gouv.cnsp.monitorfish.infrastructure.database.entities.converters.deserializeJSONList import io.hypersistence.utils.hibernate.type.json.JsonBinaryType import jakarta.persistence.* import org.hibernate.annotations.JdbcType @@ -54,23 +56,23 @@ data class LogbookReportEntity( @Column(name = "log_type") val messageType: String?, @Type(JsonBinaryType::class) - @Column(name = "value", nullable = true, columnDefinition = "jsonb") + @Column(name = "value", columnDefinition = "jsonb") val message: String?, @Column(name = "integration_datetime_utc") val integrationDateTime: Instant, @JdbcType(PostgreSQLEnumJdbcType::class) @Column(name = "transmission_format", columnDefinition = "logbook_message_transmission_format") @Enumerated(EnumType.STRING) - val transmissionFormat: LogbookTransmissionFormat?, + val transmissionFormat: LogbookTransmissionFormat, @Column(name = "software") val software: String?, @Column(name = "enriched") - val isEnriched: Boolean = false, + val isEnriched: Boolean, @Type(JsonBinaryType::class) - @Column(name = "trip_gears", nullable = true, columnDefinition = "jsonb") + @Column(name = "trip_gears", columnDefinition = "jsonb") val tripGears: String?, @Type(JsonBinaryType::class) - @Column(name = "trip_segments", nullable = true, columnDefinition = "jsonb") + @Column(name = "trip_segments", columnDefinition = "jsonb") val tripSegments: String?, @Column(name = "is_test_message") val isTestMessage: Boolean = false, @@ -101,8 +103,8 @@ data class LogbookReportEntity( messageType = logbookMessage.messageType, operationCountry = null, operationType = logbookMessage.operationType, - tripGears = null, - tripSegments = null, + tripGears = JSONB_NULL_STRING, + tripSegments = JSONB_NULL_STRING, ) } @@ -137,17 +139,4 @@ data class LogbookReportEntity( tripSegments = tripSegments, ) } - - private fun deserializeJSONList( - mapper: ObjectMapper, - json: String?, - clazz: Class, - ): List = - json?.let { - mapper.readValue( - json, - mapper.typeFactory - .constructCollectionType(MutableList::class.java, clazz), - ) - } ?: listOf() } diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt index b2c91a2dcd..63c0e335fb 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/ManualPriorNotificationEntity.kt @@ -103,7 +103,7 @@ data class ManualPriorNotificationEntity( operationNumber = null, operationType = LogbookOperationType.DAT, reportDateTime = sentAt, - transmissionFormat = null, + transmissionFormat = LogbookTransmissionFormat.MANUAL, tripGears = tripGears, tripSegments = tripSegments, vesselName = vesselName, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/MissionActionEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/MissionActionEntity.kt index 2be143f331..16222d8cb2 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/MissionActionEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/MissionActionEntity.kt @@ -232,7 +232,7 @@ class MissionActionEntity( } ?: listOf(), actionType = actionType, actionDatetimeUtc = actionDatetimeUtc.atZone(ZoneOffset.UTC), - actionEndDatetimeUtc = actionEndDatetimeUtc?.let { it.atZone(ZoneOffset.UTC) }, + actionEndDatetimeUtc = actionEndDatetimeUtc?.atZone(ZoneOffset.UTC), emitsVms = emitsVms, emitsAis = emitsAis, logbookMatchesActivity = logbookMatchesActivity, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/RiskFactorsEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/RiskFactorsEntity.kt index 5eadb689e4..5e594d947a 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/RiskFactorsEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/RiskFactorsEntity.kt @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import fr.gouv.cnsp.monitorfish.domain.entities.last_position.Gear import fr.gouv.cnsp.monitorfish.domain.entities.last_position.Species import fr.gouv.cnsp.monitorfish.domain.entities.risk_factor.VesselRiskFactor +import fr.gouv.cnsp.monitorfish.infrastructure.database.entities.converters.deserializeJSONList import io.hypersistence.utils.hibernate.type.json.JsonBinaryType import jakarta.persistence.Column import jakarta.persistence.Entity @@ -29,10 +30,10 @@ data class RiskFactorsEntity( val riskFactor: Double, @Type(JsonBinaryType::class) @Column(name = "gear_onboard", columnDefinition = "jsonb") - val gearOnboard: String?, + val gearOnboard: String, @Type(JsonBinaryType::class) @Column(name = "species_onboard", columnDefinition = "jsonb") - val speciesOnboard: String?, + val speciesOnboard: String, @Column(name = "segments", columnDefinition = "varchar(50)[]") val segments: List, @Column(name = "segment_highest_impact") @@ -45,6 +46,8 @@ data class RiskFactorsEntity( val lastControlDatetime: ZonedDateTime? = null, @Column(name = "control_rate_risk_factor") val controlRateRiskFactor: Double, + @Column(name = "total_weight_onboard") + val totalWeightOnboard: Double, @Column(name = "infraction_score") val infractionScore: Double? = null, @Column(name = "number_controls_last_5_years") @@ -69,22 +72,13 @@ data class RiskFactorsEntity( detectabilityRiskFactor = detectabilityRiskFactor, riskFactor = riskFactor, internalReferenceNumber = cfr, - gearOnboard = - mapper.readValue( - gearOnboard, - mapper.typeFactory - .constructCollectionType(MutableList::class.java, Gear::class.java), - ), - speciesOnboard = - mapper.readValue( - speciesOnboard, - mapper.typeFactory - .constructCollectionType(MutableList::class.java, Species::class.java), - ), + gearOnboard = deserializeJSONList(mapper, gearOnboard, Gear::class.java), + speciesOnboard = deserializeJSONList(mapper, speciesOnboard, Species::class.java), segments = segments, controlPriorityLevel = controlPriorityLevel, segmentHighestImpact = segmentHighestImpact, segmentHighestPriority = segmentHighestPriority, + totalWeightOnboard = totalWeightOnboard, lastControlDatetime = lastControlDatetime, controlRateRiskFactor = controlRateRiskFactor, numberControlsLastFiveYears = numberControlsLastFiveYears, diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/VesselEntity.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/VesselEntity.kt index 58846e8e0c..6a78746d89 100644 --- a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/VesselEntity.kt +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/VesselEntity.kt @@ -59,7 +59,7 @@ data class VesselEntity( @Column(name = "sailing_type") val sailingType: String? = null, @Column(name = "declared_fishing_gears", columnDefinition = "varchar(100)[]") - val declaredFishingGears: List? = null, + val declaredFishingGears: List, @Column(name = "nav_licence_expiration_date", columnDefinition = "date") val navigationLicenceExpirationDate: Date? = null, @Column(name = "nav_licence_extension_date", columnDefinition = "date") diff --git a/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/converters/Utils.kt b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/converters/Utils.kt new file mode 100644 index 0000000000..8161a71bc4 --- /dev/null +++ b/backend/src/main/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/entities/converters/Utils.kt @@ -0,0 +1,21 @@ +package fr.gouv.cnsp.monitorfish.infrastructure.database.entities.converters + +import com.fasterxml.jackson.databind.ObjectMapper +import fr.gouv.cnsp.monitorfish.domain.mappers.ERSMapper.JSONB_NULL_STRING + +fun deserializeJSONList( + mapper: ObjectMapper, + json: String?, + clazz: Class, +): List = + json?.let { + if (it == JSONB_NULL_STRING) { + return listOf() + } + + mapper.readValue( + it, + mapper.typeFactory + .constructCollectionType(MutableList::class.java, clazz), + ) + } ?: listOf() diff --git a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql new file mode 100644 index 0000000000..99c8cb6e95 --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql @@ -0,0 +1,30 @@ +ALTER TABLE public.last_positions + ALTER COLUMN longitude SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN latitude SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN total_weight_onboard SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN impact_risk_factor SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN probability_risk_factor SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN risk_factor SET NOT NULL; + +ALTER TABLE public.risk_factors + ALTER COLUMN gear_onboard SET NOT NULL; +ALTER TABLE public.risk_factors + ALTER COLUMN species_onboard SET NOT NULL; + +ALTER TABLE public.vessels + ALTER COLUMN declared_fishing_gears SET NOT NULL; + +ALTER TABLE public.logbook_reports + ALTER COLUMN operation_type SET NOT NULL; +ALTER TABLE public.logbook_reports + ALTER COLUMN integration_datetime_utc SET NOT NULL; + +ALTER TYPE public.logbook_message_transmission_format ADD VALUE 'MANUAL' AFTER 'FLUX'; + +ALTER TABLE public.logbook_reports + ALTER COLUMN transmission_format SET NOT NULL; diff --git a/backend/src/main/resources/db/testdata/V666.2__Insert_dummy_vessels.sql b/backend/src/main/resources/db/testdata/V666.2__Insert_dummy_vessels.sql index ae840ea744..c7ac6480de 100644 --- a/backend/src/main/resources/db/testdata/V666.2__Insert_dummy_vessels.sql +++ b/backend/src/main/resources/db/testdata/V666.2__Insert_dummy_vessels.sql @@ -27,62 +27,62 @@ INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, width, length, district, district_code, gauge, registry_port, power, vessel_type, sailing_category, sailing_type, declared_fishing_gears, nav_licence_expiration_date, operator_name, operator_phones, operator_email, proprietor_name, proprietor_phones, proprietor_emails, vessel_emails, vessel_phones, under_charter, has_esacapt, logbook_equipment_status) VALUES (13, 'FR263454484', '', 'FE4864', '8FR6541', 'NO NAME', 'FR', 15.5, 7.6, 'Auray', 'AY', 123, 'LORIENT', 108, 'Pêche côtière', '3', 'Pêche', '{"PTM", "OTM"}', CURRENT_DATE + INTERVAL '5 days', 'DUPOND', '{"+33 6 84 56 32 14"}', 'dupond@gmail.com', 'DURAND', '{"+33 6 45 25 14"}', '{"durand@gmail.com"}', '{"escogriffe@dgse.spy", "henri.duflot@dgse.spy"}', '{"0918273645", "+33 6 00 00 00 00"}', true, false, 'Equipé'); -INSERT INTO vessels (id, cfr, ircs, external_immatriculation, vessel_name) VALUES (-1, 'UNKNOWN', 'UNKNOWN', 'UNKNOWN', 'UNKNOWN'); +INSERT INTO vessels (id, cfr, ircs, declared_fishing_gears, external_immatriculation, vessel_name) VALUES (-1, 'UNKNOWN', 'UNKNOWN', '{}', 'UNKNOWN', 'UNKNOWN'); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (101, 'CFR101', 'MMSI101', 'IRCS101', 'EXTIMM101', 'VIVA ESPANA', 'ES', 15, true); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (101, 'CFR101', 'MMSI101', 'IRCS101', '{}', 'EXTIMM101', 'VIVA ESPANA', 'ES', 15, true); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (102, 'CFR102', 'MMSI102', 'IRCS102', 'EXTIMM102', 'LEVE NEDERLAND', 'NL', 20, true); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (102, 'CFR102', 'MMSI102', 'IRCS102', '{}', 'EXTIMM102', 'LEVE NEDERLAND', 'NL', 20, true); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (103, 'CFR103', 'MMSI103', 'IRCS103', 'EXTIMM103', 'L''OM DU POISSON', 'FR', 11.99, true); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (103, 'CFR103', 'MMSI103', 'IRCS103', '{}', 'EXTIMM103', 'L''OM DU POISSON', 'FR', 11.99, true); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (104, 'CFR104', 'MMSI104', 'IRCS104', 'EXTIMM104', 'DES BARS', NULL, 15, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (104, 'CFR104', 'MMSI104', 'IRCS104', '{}', 'EXTIMM104', 'DES BARS', NULL, 15, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (105, 'CFR105', 'MMSI105', 'IRCS105', 'EXTIMM105', 'CALAMARO', 'FR', 16, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (105, 'CFR105', 'MMSI105', 'IRCS105', '{}', 'EXTIMM105', 'CALAMARO', 'FR', 16, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (106, 'CFR106', 'MMSI106', 'IRCS106', 'EXTIMM106', 'L''ANCRE SÈCHE', 'FR', 7.5, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (106, 'CFR106', 'MMSI106', 'IRCS106', '{}', 'EXTIMM106', 'L''ANCRE SÈCHE', 'FR', 7.5, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (107, 'CFR107', 'MMSI107', 'IRCS107', 'EXTIMM107', 'MERLU L''ENCHANTEUR', 'FR', 18, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (107, 'CFR107', 'MMSI107', 'IRCS107', '{}', 'EXTIMM107', 'MERLU L''ENCHANTEUR', 'FR', 18, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (108, 'CFR108', 'MMSI108', 'IRCS108', 'EXTIMM108', 'LE POISSON AMBULANT', 'FR', 9, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (108, 'CFR108', 'MMSI108', 'IRCS108', '{}', 'EXTIMM108', 'LE POISSON AMBULANT', 'FR', 9, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (109, 'CFR109', 'MMSI109', 'IRCS109', 'EXTIMM109', 'LE POISSON D''AVRIL', 'FR', 25, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (109, 'CFR109', 'MMSI109', 'IRCS109', '{}', 'EXTIMM109', 'LE POISSON D''AVRIL', 'FR', 25, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (110, 'CFR110', 'MMSI110', 'IRCS110', 'EXTIMM110', 'LA MER À BOIRE', 'FR', 12.5, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (110, 'CFR110', 'MMSI110', 'IRCS110', '{}', 'EXTIMM110', 'LA MER À BOIRE', 'FR', 12.5, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (111, 'CFR111', 'MMSI111', 'IRCS111', 'EXTIMM111', 'LE MARIN D''EAU DOUCE', 'FR', 9.5, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (111, 'CFR111', 'MMSI111', 'IRCS111', '{}', 'EXTIMM111', 'LE MARIN D''EAU DOUCE', 'FR', 9.5, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (112, 'CFR112', 'MMSI112', 'IRCS112', 'EXTIMM112', 'POISSON PAS NET', 'FR', 7.3, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (112, 'CFR112', 'MMSI112', 'IRCS112', '{}', 'EXTIMM112', 'POISSON PAS NET', 'FR', 7.3, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (113, 'CFR113', 'MMSI113', 'IRCS113', 'EXTIMM113', 'IN-ARÊTE-ABLE', 'FR', 8.5, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (113, 'CFR113', 'MMSI113', 'IRCS113', '{}', 'EXTIMM113', 'IN-ARÊTE-ABLE', 'FR', 8.5, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (115, 'CFR115', 'MMSI115', 'IRCS115', 'EXTIMM115', 'DOS FIN', 'BE', 9.2, true); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (115, 'CFR115', 'MMSI115', 'IRCS115', '{}', 'EXTIMM115', 'DOS FIN', 'BE', 9.2, true); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (116, 'CFR116', 'MMSI116', 'IRCS116', 'EXTIMM116', 'NAVIRE RENOMMÉ (NOUVEAU NOM)', 'FR', 11, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (116, 'CFR116', 'MMSI116', 'IRCS116', '{}', 'EXTIMM116', 'NAVIRE RENOMMÉ (NOUVEAU NOM)', 'FR', 11, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (117, 'CFR117', 'MMSI117', 'IRCS117', 'EXTIMM117', 'QUEUE DE POISSON', 'FR', 10.9, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (117, 'CFR117', 'MMSI117', 'IRCS117', '{}', 'EXTIMM117', 'QUEUE DE POISSON', 'FR', 10.9, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (118, 'CFR118', 'MMSI118', 'IRCS118', 'EXTIMM118', 'GOUJON BOUGON', 'FR', 11.2, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (118, 'CFR118', 'MMSI118', 'IRCS118', '{}', 'EXTIMM118', 'GOUJON BOUGON', 'FR', 11.2, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (119, 'CFR119', 'MMSI119', 'IRCS119', 'EXTIMM119', 'PAGEOT JO', 'FR', 11.1, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (119, 'CFR119', 'MMSI119', 'IRCS119', '{}', 'EXTIMM119', 'PAGEOT JO', 'FR', 11.1, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (120, 'CFR120', 'MMSI120', 'IRCS120', 'EXTIMM120', 'VIVA L''ITALIA', 'IT', 11.1, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (120, 'CFR120', 'MMSI120', 'IRCS120', '{}', 'EXTIMM120', 'VIVA L''ITALIA', 'IT', 11.1, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (121, 'CFR121', 'MMSI121', 'IRCS121', 'EXTIMM121', 'MARE ET BASS', 'FR', 7.7, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (121, 'CFR121', 'MMSI121', 'IRCS121', '{}', 'EXTIMM121', 'MARE ET BASS', 'FR', 7.7, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (122, 'CFR122', 'MMSI122', 'IRCS122', 'EXTIMM122', 'FILET DOUX', 'FR', 7.8, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (122, 'CFR122', 'MMSI122', 'IRCS122', '{}', 'EXTIMM122', 'FILET DOUX', 'FR', 7.8, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (123, 'CFR123', 'MMSI123', 'IRCS123', 'EXTIMM123', 'DÉVOILÉ', 'FR', 7.9, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (123, 'CFR123', 'MMSI123', 'IRCS123', '{}', 'EXTIMM123', 'DÉVOILÉ', 'FR', 7.9, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (124, 'CFR124', 'MMSI124', 'IRCS124', 'EXTIMM124', 'MAT QUILLE', 'FR', 8.1, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (124, 'CFR124', 'MMSI124', 'IRCS124', '{}', 'EXTIMM124', 'MAT QUILLE', 'FR', 8.1, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (125, 'CFR125', 'MMSI125', 'IRCS125', 'EXTIMM125', 'BEAU SÉANT', 'FR', 8.2, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (125, 'CFR125', 'MMSI125', 'IRCS125', '{}', 'EXTIMM125', 'BEAU SÉANT', 'FR', 8.2, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (126, 'CFR126', 'MMSI126', 'IRCS126', 'EXTIMM126', 'THON BEAU', 'FR', 17.8, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (126, 'CFR126', 'MMSI126', 'IRCS126', '{}', 'EXTIMM126', 'THON BEAU', 'FR', 17.8, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (127, 'CFR127', 'MMSI127', 'IRCS127', 'EXTIMM127', 'MILLE SABORDS', 'FR', 16.9, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (127, 'CFR127', 'MMSI127', 'IRCS127', '{}', 'EXTIMM127', 'MILLE SABORDS', 'FR', 16.9, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (128, 'CFR128', 'MMSI128', 'IRCS128', 'EXTIMM128', 'THE FLOATING KANGAROO', 'AU', 31, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (128, 'CFR128', 'MMSI128', 'IRCS128', '{}', 'EXTIMM128', 'THE FLOATING KANGAROO', 'AU', 31, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (129, 'CFR129', 'MMSI129', 'IRCS129', 'EXTIMM129', 'BON VENT', 'FR', 34.5, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (129, 'CFR129', 'MMSI129', 'IRCS129', '{}', 'EXTIMM129', 'BON VENT', 'FR', 34.5, false); -INSERT INTO vessels (id, cfr, mmsi, ircs, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (130, 'CFR130', 'MMSI130', 'IRCS130', 'EXTIMM130', 'L''HIPPO CAMPE', 'FR', 19.2, false); +INSERT INTO vessels (id, cfr, mmsi, ircs, declared_fishing_gears, external_immatriculation, vessel_name, flag_state, length, under_charter) VALUES (130, 'CFR130', 'MMSI130', 'IRCS130', '{}', 'EXTIMM130', 'L''HIPPO CAMPE', 'FR', 19.2, false); diff --git a/backend/src/main/resources/db/testdata/V666.3__Insert_dummy_last_positions.sql b/backend/src/main/resources/db/testdata/V666.3__Insert_dummy_last_positions.sql index c27e178390..082175db00 100644 --- a/backend/src/main/resources/db/testdata/V666.3__Insert_dummy_last_positions.sql +++ b/backend/src/main/resources/db/testdata/V666.3__Insert_dummy_last_positions.sql @@ -1023,16 +1023,16 @@ set last_control_datetime_utc = last_control_datetime_utc + (now() - '2021-01-01 -- Add fake data used in automated testing COPY public.last_positions (id, cfr, external_immatriculation, mmsi, ircs, vessel_name, flag_state, trip_number, - latitude, longitude, speed, course, last_position_datetime_utc, emission_period, + latitude, longitude, speed, total_weight_onboard, course, last_position_datetime_utc, emission_period, last_logbook_message_datetime_utc, length, width, segments, gear_onboard, species_onboard, district, district_code, last_control_datetime_utc, last_control_infraction, post_control_comments, vessel_identifier, estimated_current_latitude, estimated_current_longitude, impact_risk_factor, probability_risk_factor, detectability_risk_factor, risk_factor, under_charter, is_at_port, alerts, beacon_malfunction_id, reportings) FROM stdin; -10000 FAK000999999 DONTSINK \N CALLME PHENOMENE GB \N 47.921999999999997 -8.0129999999999999 8.40000000000000036 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] CAEN CN 2020-12-22 08:59:00 true Pas de com INTERNAL_REFERENCE_NUMBER 47.7123 -8.8123 2.1 2 3 2.473 t f {THREE_MILES_TRAWLING_ALERT} 1 \N -10001 SOCR4T3 LePhiloFilou \N SCRT SOCRATE FR \N 48.921999999999997 -8.0129999999999999 8.40000000000000036 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] ATHENES AT 2020-12-22 08:59:00 true No comment INTERNAL_REFERENCE_NUMBER 49.003 -7.9523 2.1 2 3 2.473 f f \N \N \N -10002 U_W0NTFINDME ABC123456 \N TALK2ME MALOTRU FR \N 48.221999999999997 -8.5129999999999999 8.40000000000000036 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] ATHENES AT 2020-12-22 08:59:00 true No comment INTERNAL_REFERENCE_NUMBER 49.003 -7.9523 2.1 2 3 2.473 f f \N \N \N +10000 FAK000999999 DONTSINK \N CALLME PHENOMENE GB \N 47.921999999999997 -8.0129999999999999 8.40000000000000036 0.0 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] CAEN CN 2020-12-22 08:59:00 true Pas de com INTERNAL_REFERENCE_NUMBER 47.7123 -8.8123 2.1 2 3 2.473 t f {THREE_MILES_TRAWLING_ALERT} 1 \N +10001 SOCR4T3 LePhiloFilou \N SCRT SOCRATE FR \N 48.921999999999997 -8.0129999999999999 8.40000000000000036 0.0 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] ATHENES AT 2020-12-22 08:59:00 true No comment INTERNAL_REFERENCE_NUMBER 49.003 -7.9523 2.1 2 3 2.473 f f \N \N \N +10002 U_W0NTFINDME ABC123456 \N TALK2ME MALOTRU FR \N 48.221999999999997 -8.5129999999999999 8.40000000000000036 0.0 14 2021-01-15 07:32:00 00:40:00 2020-12-21 15:01:00 14.3 5.2 {"W10", "PEL03"} [{"gear": "OTB", "mesh": 70.0, "dimensions": 45.0}] [{ "gear": "OTB","faoZone": "27.8.b","species": "BLI","weight": 13.46 },{ "gear": "OTB","faoZone": "27.8.c","species": "HKE","weight": 235.6 }] ATHENES AT 2020-12-22 08:59:00 true No comment INTERNAL_REFERENCE_NUMBER 49.003 -7.9523 2.1 2 3 2.473 f f \N \N \N \. update last_positions diff --git a/backend/src/main/resources/db/testdata/json/V666.2__Insert_dummy_vessels.jsonc b/backend/src/main/resources/db/testdata/json/V666.2__Insert_dummy_vessels.jsonc index 84d5ed49c8..865b496c46 100644 --- a/backend/src/main/resources/db/testdata/json/V666.2__Insert_dummy_vessels.jsonc +++ b/backend/src/main/resources/db/testdata/json/V666.2__Insert_dummy_vessels.jsonc @@ -437,6 +437,7 @@ "id": -1, "cfr": "UNKNOWN", "ircs": "UNKNOWN", + "declared_fishing_gears": [], "external_immatriculation": "UNKNOWN", "vessel_name": "UNKNOWN" }, @@ -449,6 +450,7 @@ "cfr": "CFR101", "mmsi": "MMSI101", "ircs": "IRCS101", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM101", "vessel_name": "VIVA ESPANA", "flag_state": "ES", @@ -464,6 +466,7 @@ "cfr": "CFR102", "mmsi": "MMSI102", "ircs": "IRCS102", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM102", "vessel_name": "LEVE NEDERLAND", "flag_state": "NL", @@ -478,6 +481,7 @@ "cfr": "CFR103", "mmsi": "MMSI103", "ircs": "IRCS103", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM103", "vessel_name": "L'OM DU POISSON", "flag_state": "FR", @@ -492,6 +496,7 @@ "cfr": "CFR104", "mmsi": "MMSI104", "ircs": "IRCS104", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM104", "vessel_name": "DES BARS", "flag_state": null, @@ -505,6 +510,7 @@ "cfr": "CFR105", "mmsi": "MMSI105", "ircs": "IRCS105", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM105", "vessel_name": "CALAMARO", "flag_state": "FR", @@ -518,6 +524,7 @@ "cfr": "CFR106", "mmsi": "MMSI106", "ircs": "IRCS106", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM106", "vessel_name": "L'ANCRE SÈCHE", "flag_state": "FR", @@ -531,6 +538,7 @@ "cfr": "CFR107", "mmsi": "MMSI107", "ircs": "IRCS107", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM107", "vessel_name": "MERLU L'ENCHANTEUR", "flag_state": "FR", @@ -544,6 +552,7 @@ "cfr": "CFR108", "mmsi": "MMSI108", "ircs": "IRCS108", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM108", "vessel_name": "LE POISSON AMBULANT", "flag_state": "FR", @@ -558,6 +567,7 @@ "cfr": "CFR109", "mmsi": "MMSI109", "ircs": "IRCS109", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM109", "vessel_name": "LE POISSON D'AVRIL", "flag_state": "FR", @@ -570,6 +580,7 @@ // "cfr": "CFR109", // "mmsi": "MMSI109_DUPLICATE", // "ircs": "IRCS109_DUPLICATE", + // "declared_fishing_gears": [], // "external_immatriculation": "EXTIMM109_DUPLICATE", // "vessel_name": "LE POISSON D'AVRIL DUPLIQUÉ", // "flag_state": "FR", @@ -583,6 +594,7 @@ "cfr": "CFR110", "mmsi": "MMSI110", "ircs": "IRCS110", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM110", "vessel_name": "LA MER À BOIRE", "flag_state": "FR", @@ -596,6 +608,7 @@ "cfr": "CFR111", "mmsi": "MMSI111", "ircs": "IRCS111", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM111", "vessel_name": "LE MARIN D'EAU DOUCE", "flag_state": "FR", @@ -609,6 +622,7 @@ "cfr": "CFR112", "mmsi": "MMSI112", "ircs": "IRCS112", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM112", "vessel_name": "POISSON PAS NET", "flag_state": "FR", @@ -622,6 +636,7 @@ "cfr": "CFR113", "mmsi": "MMSI113", "ircs": "IRCS113", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM113", "vessel_name": "IN-ARÊTE-ABLE", "flag_state": "FR", @@ -637,6 +652,7 @@ "cfr": "CFR115", "mmsi": "MMSI115", "ircs": "IRCS115", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM115", "vessel_name": "DOS FIN", "flag_state": "BE", @@ -651,6 +667,7 @@ "cfr": "CFR116", "mmsi": "MMSI116", "ircs": "IRCS116", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM116", "vessel_name": "NAVIRE RENOMMÉ (NOUVEAU NOM)", "flag_state": "FR", @@ -664,6 +681,7 @@ "cfr": "CFR117", "mmsi": "MMSI117", "ircs": "IRCS117", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM117", "vessel_name": "QUEUE DE POISSON", "flag_state": "FR", @@ -677,6 +695,7 @@ "cfr": "CFR118", "mmsi": "MMSI118", "ircs": "IRCS118", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM118", "vessel_name": "GOUJON BOUGON", "flag_state": "FR", @@ -690,6 +709,7 @@ "cfr": "CFR119", "mmsi": "MMSI119", "ircs": "IRCS119", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM119", "vessel_name": "PAGEOT JO", "flag_state": "FR", @@ -704,6 +724,7 @@ "cfr": "CFR120", "mmsi": "MMSI120", "ircs": "IRCS120", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM120", "vessel_name": "VIVA L'ITALIA", "flag_state": "IT", @@ -717,6 +738,7 @@ "cfr": "CFR121", "mmsi": "MMSI121", "ircs": "IRCS121", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM121", "vessel_name": "MARE ET BASS", "flag_state": "FR", @@ -730,6 +752,7 @@ "cfr": "CFR122", "mmsi": "MMSI122", "ircs": "IRCS122", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM122", "vessel_name": "FILET DOUX", "flag_state": "FR", @@ -743,6 +766,7 @@ "cfr": "CFR123", "mmsi": "MMSI123", "ircs": "IRCS123", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM123", "vessel_name": "DÉVOILÉ", "flag_state": "FR", @@ -756,6 +780,7 @@ "cfr": "CFR124", "mmsi": "MMSI124", "ircs": "IRCS124", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM124", "vessel_name": "MAT QUILLE", "flag_state": "FR", @@ -769,6 +794,7 @@ "cfr": "CFR125", "mmsi": "MMSI125", "ircs": "IRCS125", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM125", "vessel_name": "BEAU SÉANT", "flag_state": "FR", @@ -782,6 +808,7 @@ "cfr": "CFR126", "mmsi": "MMSI126", "ircs": "IRCS126", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM126", "vessel_name": "THON BEAU", "flag_state": "FR", @@ -795,6 +822,7 @@ "cfr": "CFR127", "mmsi": "MMSI127", "ircs": "IRCS127", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM127", "vessel_name": "MILLE SABORDS", "flag_state": "FR", @@ -809,6 +837,7 @@ "cfr": "CFR128", "mmsi": "MMSI128", "ircs": "IRCS128", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM128", "vessel_name": "THE FLOATING KANGAROO", "flag_state": "AU", @@ -822,6 +851,7 @@ "cfr": "CFR129", "mmsi": "MMSI129", "ircs": "IRCS129", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM129", "vessel_name": "BON VENT", "flag_state": "FR", @@ -835,6 +865,7 @@ "cfr": "CFR130", "mmsi": "MMSI130", "ircs": "IRCS130", + "declared_fishing_gears": [], "external_immatriculation": "EXTIMM130", "vessel_name": "L'HIPPO CAMPE", "flag_state": "FR", diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt index f9f99d1a64..6a0555cabb 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/LogbookMessageFaker.kt @@ -48,7 +48,7 @@ class LogbookMessageFaker { rawMessage: String? = null, reportDateTime: ZonedDateTime? = null, software: String? = null, - transmissionFormat: LogbookTransmissionFormat? = null, + transmissionFormat: LogbookTransmissionFormat = LogbookTransmissionFormat.ERS, tripGears: List? = emptyList(), tripNumber: String? = null, tripSegments: List? = emptyList(), diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/VesselFaker.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/VesselFaker.kt index cc5ef9c0ee..1214310637 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/VesselFaker.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/fakers/VesselFaker.kt @@ -25,7 +25,7 @@ class VesselFaker { vesselType: String? = null, sailingCategory: String? = null, sailingType: String? = null, - declaredFishingGears: List? = null, + declaredFishingGears: List = listOf(), pinger: Boolean? = null, navigationLicenceExpirationDate: Date? = null, operatorName: String? = null, diff --git a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt index c4a89a95e9..74e261e763 100644 --- a/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt +++ b/backend/src/test/kotlin/fr/gouv/cnsp/monitorfish/infrastructure/database/repositories/JpaManualPriorNotificationRepositoryITests.kt @@ -1,10 +1,7 @@ package fr.gouv.cnsp.monitorfish.infrastructure.database.repositories import com.neovisionaries.i18n.CountryCode -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessage -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessageAndValue -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookMessagePurpose -import fr.gouv.cnsp.monitorfish.domain.entities.logbook.LogbookOperationType +import fr.gouv.cnsp.monitorfish.domain.entities.logbook.* import fr.gouv.cnsp.monitorfish.domain.entities.logbook.messages.PNO import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.PriorNotification import fr.gouv.cnsp.monitorfish.domain.entities.prior_notification.filters.PriorNotificationsFilter @@ -526,7 +523,7 @@ class JpaManualPriorNotificationRepositoryITests : AbstractDBTests() { operationType = LogbookOperationType.DAT, // Replaced by the generated `sentAt` during the save operation. reportDateTime = ZonedDateTime.now(), - transmissionFormat = null, + transmissionFormat = LogbookTransmissionFormat.FLUX, vesselName = "Vessel Name", vesselId = 123, ), diff --git a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts index b5ce681a2e..abd07682f5 100644 --- a/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts +++ b/frontend/cypress/e2e/main_window/vessel_sidebar/logbook.spec.ts @@ -171,7 +171,7 @@ context('Vessel sidebar logbook tab', () => { .its('response.url') .should( 'have.string', - '/bff/v1/vessels/positions?&afterDateTime=&beforeDateTime=' + + '/bff/v1/vessels/positions?afterDateTime=&beforeDateTime=' + '&externalReferenceNumber=DONTSINK&internalReferenceNumber=FAK000999999' + '&IRCS=CALLME&trackDepth=TWELVE_HOURS&vesselIdentifier=INTERNAL_REFERENCE_NUMBER' ) diff --git a/frontend/cypress/e2e/side_window/alert_list/alerts_list.spec.ts b/frontend/cypress/e2e/side_window/alert_list/alerts_list.spec.ts index 3e10aa4fb8..6cb6462326 100644 --- a/frontend/cypress/e2e/side_window/alert_list/alerts_list.spec.ts +++ b/frontend/cypress/e2e/side_window/alert_list/alerts_list.spec.ts @@ -62,8 +62,8 @@ context('Side Window > Alert List', () => { // Show vessel on map cy.intercept( 'GET', - 'bff/v1/vessels/find?vesselId=&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' + - '&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=' + 'bff/v1/vessels/find?afterDateTime=&beforeDateTime=&externalReferenceNumber=DONTSINK' + + '&internalReferenceNumber=FAK000999999&IRCS=CALLME&trackDepth=TWELVE_HOURS&vesselId=&vesselIdentifier=INTERNAL_REFERENCE_NUMBER' ).as('showVesselPositionsOnMap') cy.get('*[data-cy="side-window-alerts-show-vessel"]').first().forceClick() cy.wait('@showVesselPositionsOnMap').then(({ response }) => expect(response && response.statusCode).equal(200)) diff --git a/frontend/src/features/Vessel/Vessel.types.ts b/frontend/src/features/Vessel/Vessel.types.ts index 561f2dd739..c89123a9b6 100644 --- a/frontend/src/features/Vessel/Vessel.types.ts +++ b/frontend/src/features/Vessel/Vessel.types.ts @@ -18,7 +18,7 @@ export namespace Vessel { } export interface Vessel { - declaredFishingGears: string[] | undefined + declaredFishingGears: string[] district: string | undefined districtCode: string | undefined externalReferenceNumber: string | undefined diff --git a/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts b/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts index 2670bad7d1..53ae1cdc6e 100644 --- a/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts +++ b/frontend/src/features/Vessel/schemas/VesselLastPositionSchema.ts @@ -9,7 +9,6 @@ import DeclaredLogbookSpeciesSchema = Vessel.DeclaredLogbookSpeciesSchema import DeclaredLogbookGearSchema = Vessel.DeclaredLogbookGearSchema import VesselIdentifier = Vessel.VesselIdentifier -// TODO Check which of these schemas are nullable or not export const VesselLastPositionSchema = z.strictObject({ alerts: z.array(z.union([z.nativeEnum(PendingAlertValueType), z.literal('PNO_LAN_WEIGHT_TOLERANCE_ALERT')])), beaconMalfunctionId: numberOrUndefined, @@ -18,7 +17,7 @@ export const VesselLastPositionSchema = z.strictObject({ dateTime: z.string(), departureDateTime: stringOrUndefined, destination: stringOrUndefined, - detectabilityRiskFactor: numberOrUndefined, + detectabilityRiskFactor: z.number(), district: stringOrUndefined, districtCode: stringOrUndefined, emissionPeriod: numberOrUndefined, @@ -28,10 +27,10 @@ export const VesselLastPositionSchema = z.strictObject({ flagState: z.string(), from: stringOrUndefined, gearOnboard: z.array(DeclaredLogbookGearSchema), - impactRiskFactor: numberOrUndefined, + impactRiskFactor: z.number(), internalReferenceNumber: stringOrUndefined, ircs: stringOrUndefined, - isAtPort: booleanOrUndefined, + isAtPort: z.boolean(), lastControlDateTime: stringOrUndefined, lastControlInfraction: booleanOrUndefined, lastLogbookMessageDateTime: stringOrUndefined, @@ -41,15 +40,15 @@ export const VesselLastPositionSchema = z.strictObject({ mmsi: stringOrUndefined, positionType: z.string(), postControlComment: stringOrUndefined, - probabilityRiskFactor: numberOrUndefined, + probabilityRiskFactor: z.number(), registryPortLocode: stringOrUndefined, registryPortName: stringOrUndefined, reportings: z.array(z.nativeEnum(ReportingType)), - riskFactor: numberOrUndefined, + riskFactor: z.number(), segments: z.array(z.string()), speciesOnboard: z.array(DeclaredLogbookSpeciesSchema), speed: numberOrUndefined, - totalWeightOnboard: numberOrUndefined, + totalWeightOnboard: z.number(), tripNumber: stringOrUndefined, underCharter: booleanOrUndefined, vesselId: numberOrUndefined, From b4832cde4f37436b2e9a499726cb97809770ba14 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 4 Feb 2025 16:03:08 +0100 Subject: [PATCH 13/21] Rename migrations --- .../V0.290__Update_last_positions_table.sql | 18 ------------------ .../V0.291__Update_risk_factors_table.sql | 4 ++++ .../internal/V0.292__Update_vessels_table.sql | 2 ++ .../V0.293__Update_logbook_reports_table.sql | 1 + .../V0.294__Update_logbook_reports_table.sql | 6 ++++++ 5 files changed, 13 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql create mode 100644 backend/src/main/resources/db/migration/internal/V0.292__Update_vessels_table.sql create mode 100644 backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql create mode 100644 backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql diff --git a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql index 99c8cb6e95..a9657a0a7e 100644 --- a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql +++ b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql @@ -10,21 +10,3 @@ ALTER TABLE public.last_positions ALTER COLUMN probability_risk_factor SET NOT NULL; ALTER TABLE public.last_positions ALTER COLUMN risk_factor SET NOT NULL; - -ALTER TABLE public.risk_factors - ALTER COLUMN gear_onboard SET NOT NULL; -ALTER TABLE public.risk_factors - ALTER COLUMN species_onboard SET NOT NULL; - -ALTER TABLE public.vessels - ALTER COLUMN declared_fishing_gears SET NOT NULL; - -ALTER TABLE public.logbook_reports - ALTER COLUMN operation_type SET NOT NULL; -ALTER TABLE public.logbook_reports - ALTER COLUMN integration_datetime_utc SET NOT NULL; - -ALTER TYPE public.logbook_message_transmission_format ADD VALUE 'MANUAL' AFTER 'FLUX'; - -ALTER TABLE public.logbook_reports - ALTER COLUMN transmission_format SET NOT NULL; diff --git a/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql b/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql new file mode 100644 index 0000000000..d08b43897c --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql @@ -0,0 +1,4 @@ +ALTER TABLE public.risk_factors + ALTER COLUMN gear_onboard SET NOT NULL; +ALTER TABLE public.risk_factors + ALTER COLUMN species_onboard SET NOT NULL; diff --git a/backend/src/main/resources/db/migration/internal/V0.292__Update_vessels_table.sql b/backend/src/main/resources/db/migration/internal/V0.292__Update_vessels_table.sql new file mode 100644 index 0000000000..f9deb5d417 --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.292__Update_vessels_table.sql @@ -0,0 +1,2 @@ +ALTER TABLE public.vessels + ALTER COLUMN declared_fishing_gears SET NOT NULL; diff --git a/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql b/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql new file mode 100644 index 0000000000..a14e4d2dca --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql @@ -0,0 +1 @@ +ALTER TYPE public.logbook_message_transmission_format ADD VALUE 'MANUAL' AFTER 'FLUX'; diff --git a/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql b/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql new file mode 100644 index 0000000000..91c4d7abf0 --- /dev/null +++ b/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql @@ -0,0 +1,6 @@ +ALTER TABLE public.logbook_reports + ALTER COLUMN operation_type SET NOT NULL; +ALTER TABLE public.logbook_reports + ALTER COLUMN integration_datetime_utc SET NOT NULL; +ALTER TABLE public.logbook_reports + ALTER COLUMN transmission_format SET NOT NULL; From 20f723392fb90e9440194121c1219026e391f320 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Tue, 4 Feb 2025 16:31:11 +0100 Subject: [PATCH 14/21] Fix total weight not null --- .../tests/test_pipeline/test_flows/test_last_positions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datascience/tests/test_pipeline/test_flows/test_last_positions.py b/datascience/tests/test_pipeline/test_flows/test_last_positions.py index 610d8d1ca6..3797b3aae8 100644 --- a/datascience/tests/test_pipeline/test_flows/test_last_positions.py +++ b/datascience/tests/test_pipeline/test_flows/test_last_positions.py @@ -138,7 +138,7 @@ def test_load_last_positions(reset_test_data): ], None, ], - "total_weight_onboard": [30.0, None], + "total_weight_onboard": [30.0, 0.0], "last_control_datetime_utc": [datetime(2020, 1, 5, 9, 5, 32), None], "last_control_infraction": [False, None], "post_control_comments": ["RAS", None], @@ -496,7 +496,7 @@ def test_join(): "probability_risk_factor": [1.3, 1.5, 2.1, 2.2], "detectability_risk_factor": [2.1, 2.3, 2.3, 1.4], "risk_factor": [1.8, 3.0, 1.9, 3.3], - "total_weight_onboard": [121.2, None, None, 4.0], + "total_weight_onboard": [121.2, 0.0, 0.0, 4.0], } ) From 5226fa584e8634d789e8e96396663d384fdd8869 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:06:56 +0100 Subject: [PATCH 15/21] Fix pipeline test and remove migration --- .../internal/V0.293__Update_logbook_reports_table.sql | 7 ++++++- .../internal/V0.294__Update_logbook_reports_table.sql | 6 ------ .../test_pipeline/test_flows/test_missing_dep_alerts.py | 4 ++++ 3 files changed, 10 insertions(+), 7 deletions(-) delete mode 100644 backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql diff --git a/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql b/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql index a14e4d2dca..91c4d7abf0 100644 --- a/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql +++ b/backend/src/main/resources/db/migration/internal/V0.293__Update_logbook_reports_table.sql @@ -1 +1,6 @@ -ALTER TYPE public.logbook_message_transmission_format ADD VALUE 'MANUAL' AFTER 'FLUX'; +ALTER TABLE public.logbook_reports + ALTER COLUMN operation_type SET NOT NULL; +ALTER TABLE public.logbook_reports + ALTER COLUMN integration_datetime_utc SET NOT NULL; +ALTER TABLE public.logbook_reports + ALTER COLUMN transmission_format SET NOT NULL; diff --git a/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql b/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql deleted file mode 100644 index 91c4d7abf0..0000000000 --- a/backend/src/main/resources/db/migration/internal/V0.294__Update_logbook_reports_table.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE public.logbook_reports - ALTER COLUMN operation_type SET NOT NULL; -ALTER TABLE public.logbook_reports - ALTER COLUMN integration_datetime_utc SET NOT NULL; -ALTER TABLE public.logbook_reports - ALTER COLUMN transmission_format SET NOT NULL; diff --git a/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py b/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py index 7f8795a750..8566cb505b 100644 --- a/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py @@ -59,6 +59,8 @@ def reset_test_data_missing_dep_alerts(reset_test_data): vessel_id, vessel_identifier, vessel_name, + latitude, + longitude, impact_risk_factor, probability_risk_factor, detectability_risk_factor, @@ -73,6 +75,8 @@ def reset_test_data_missing_dep_alerts(reset_test_data): 1, 'INTERNAL_REFERENCE_NUMBER', 'ÉTABLIR IMPRESSION LORSQUE', + 8.322, + 1.256, 1.5, 2.5, 3.1, From 00f0e76a81f818a887398ae0ddf4ac35a108e8e4 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:11:55 +0100 Subject: [PATCH 16/21] Add missing not nullable column --- .../internal/V0.290__Update_last_positions_table.sql | 2 ++ .../migration/internal/V0.291__Update_risk_factors_table.sql | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) delete mode 100644 backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql diff --git a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql index a9657a0a7e..193e0a96a3 100644 --- a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql +++ b/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql @@ -8,5 +8,7 @@ ALTER TABLE public.last_positions ALTER COLUMN impact_risk_factor SET NOT NULL; ALTER TABLE public.last_positions ALTER COLUMN probability_risk_factor SET NOT NULL; +ALTER TABLE public.last_positions + ALTER COLUMN detectability_risk_factor SET NOT NULL; ALTER TABLE public.last_positions ALTER COLUMN risk_factor SET NOT NULL; diff --git a/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql b/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql deleted file mode 100644 index d08b43897c..0000000000 --- a/backend/src/main/resources/db/migration/internal/V0.291__Update_risk_factors_table.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE public.risk_factors - ALTER COLUMN gear_onboard SET NOT NULL; -ALTER TABLE public.risk_factors - ALTER COLUMN species_onboard SET NOT NULL; From 8605ad9a9cb557db7edfe32fe0859b71f9ca8874 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:13:37 +0100 Subject: [PATCH 17/21] Fix test in datascience --- .../tests/test_pipeline/test_flows/test_missing_dep_alerts.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py b/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py index 8566cb505b..dddc9449df 100644 --- a/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_missing_dep_alerts.py @@ -65,6 +65,7 @@ def reset_test_data_missing_dep_alerts(reset_test_data): probability_risk_factor, detectability_risk_factor, risk_factor, + total_weight_onboard, is_at_port ) VALUES ( @@ -81,6 +82,7 @@ def reset_test_data_missing_dep_alerts(reset_test_data): 2.5, 3.1, 2.58, + 0.0, false ) """ From 1e613d000577518752484750dfb81f0bcdff9266 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:21:54 +0100 Subject: [PATCH 18/21] Fix pipeline test --- .../test_suspicions_of_under_declaration_alerts.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py b/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py index a364b61655..7462c03701 100644 --- a/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py @@ -61,7 +61,8 @@ def reset_test_data_suspicions_of_under_declaration_alerts(reset_test_data): risk_factor, is_at_port, latitude, - longitude + longitude, + total_weight_onboard, ) VALUES ( 13639642, @@ -77,7 +78,8 @@ def reset_test_data_suspicions_of_under_declaration_alerts(reset_test_data): 2.58, false, 49.606, - -0.736 + -0.736, + 0.0 ) """ ) From 2727d0c7f922efa4741e7560f833fe41fc3b55af Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:27:38 +0100 Subject: [PATCH 19/21] Remove comma --- .../test_flows/test_suspicions_of_under_declaration_alerts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py b/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py index 7462c03701..a15a3906a7 100644 --- a/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py +++ b/datascience/tests/test_pipeline/test_flows/test_suspicions_of_under_declaration_alerts.py @@ -62,7 +62,7 @@ def reset_test_data_suspicions_of_under_declaration_alerts(reset_test_data): is_at_port, latitude, longitude, - total_weight_onboard, + total_weight_onboard ) VALUES ( 13639642, From 7432a2e31a839e58d1aacf11e5cc8ca9f4caee92 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:31:10 +0100 Subject: [PATCH 20/21] Fix container upload and download --- .github/workflows/cicd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 6d02e534c1..fa216cd959 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -307,7 +307,7 @@ jobs: fetch-depth: 100 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: image: monitorfish-app:${{ env.VERSION }} @@ -353,7 +353,7 @@ jobs: uses: actions/checkout@v4 - name: Download image - uses: ishworkh/docker-image-artifact-download@v1 + uses: ishworkh/container-image-artifact-download@v2.0.0 with: image: monitorfish-app:${{ env.VERSION }} From 0418079c0b61083351105857f8dad2ab7a6bf9f2 Mon Sep 17 00:00:00 2001 From: Loup Theron Date: Wed, 5 Feb 2025 09:47:37 +0100 Subject: [PATCH 21/21] Rename miugration --- ...ositions_table.sql => V0.291__Update_last_positions_table.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename backend/src/main/resources/db/migration/internal/{V0.290__Update_last_positions_table.sql => V0.291__Update_last_positions_table.sql} (100%) diff --git a/backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql b/backend/src/main/resources/db/migration/internal/V0.291__Update_last_positions_table.sql similarity index 100% rename from backend/src/main/resources/db/migration/internal/V0.290__Update_last_positions_table.sql rename to backend/src/main/resources/db/migration/internal/V0.291__Update_last_positions_table.sql