From a7574b378b0f19d3bce9a754d90cb7e28af4f21d Mon Sep 17 00:00:00 2001 From: Ivan Gabriele Date: Tue, 23 Apr 2024 22:13:16 +0200 Subject: [PATCH] Create LeftMenu & migrate global slice into MainWindow feature [WIP/PROPOSAL] --- frontend/src/api/APIWorker.tsx | 2 +- frontend/src/api/BackofficeMode.tsx | 2 +- frontend/src/domain/entities/vessel/vessel.ts | 6 +- frontend/src/domain/shared_slices/Vessel.ts | 11 +- .../use_cases/alert/addSilencedAlert.ts | 2 +- .../use_cases/alert/getOperationalAlerts.ts | 2 +- .../use_cases/alert/getSilencedAlerts.ts | 2 +- .../alert/reactivateSilencedAlert.ts | 2 +- .../domain/use_cases/alert/silenceAlert.ts | 2 +- .../domain/use_cases/alert/validateAlert.ts | 2 +- .../archiveBeaconMalfunctions.ts | 2 +- .../getAllBeaconMalfunctions.js | 2 +- .../getVesselBeaconMalfunctions.ts | 2 +- .../openBeaconMalfunction.js | 2 +- .../openBeaconMalfunctionInKanban.ts | 2 +- .../saveBeaconMalfunctionCommentFromKanban.js | 4 +- .../beaconMalfunction/sendNotification.ts | 2 +- .../updateBeaconMalfunctionFromKanban.js | 2 +- .../use_cases/error/displayOrLogError.ts | 2 +- .../domain/use_cases/faoAreas/getFAOAreas.js | 2 +- .../use_cases/gearCode/getAllGearCodes.js | 2 +- .../use_cases/infraction/getInfractions.ts | 2 +- .../domain/use_cases/map/clickOnMapFeature.ts | 2 +- .../use_cases/measurement/saveMeasurement.js | 2 +- .../use_cases/mission/getVesselControls.ts | 2 +- .../domain/use_cases/species/getAllSpecies.js | 2 +- .../use_cases/vessel/getVesselReportings.ts | 2 +- .../domain/use_cases/vessel/searchVessels.ts | 2 +- .../src/domain/use_cases/vessel/showVessel.ts | 4 +- .../use_cases/vessel/showVesselTrack.ts | 2 +- .../domain/use_cases/vessel/unselectVessel.ts | 2 +- .../updateSelectedVesselTrackRequest.ts | 2 +- .../features/Account/components/Account.tsx | 8 +- .../layers/AdministrativeLayers.tsx | 2 +- .../useCases/getAdministrativeZoneGeometry.js | 2 +- .../useCases/getAdministrativeZones.ts | 4 +- .../useCases/getZonesAndSubZonesPromises.js | 2 +- .../edit_regulation/EditRegulation.tsx | 2 +- .../components/ControlUnitDialog/index.tsx | 2 +- .../ControlUnitListDialogButton.tsx | 6 +- .../useCases/addFleetSegmentYear.ts | 2 +- .../useCases/createFleetSegment.ts | 2 +- .../useCases/deleteFleetSegment.ts | 2 +- .../useCases/getFleetSegmentsYearEntries.ts | 2 +- .../useCases/updateFleetSegment.ts | 2 +- .../components/HealthcheckHeadband.tsx | 6 +- .../LayersSidebar/components/index.tsx | 8 +- .../Logbook/useCases/getVesselLogbook.ts | 2 +- .../MainWindow/components/LeftMenu.tsx | 159 ++++++++++++ .../components/MapButtons/AlertsMapButton.tsx | 66 ----- .../BeaconMalfunctionsMapButton.tsx | 2 +- .../MapButtons/FavoriteVessels/index.tsx | 6 +- .../InterestPoints/EditInterestPoint.tsx | 2 +- .../MapButtons/InterestPoints/index.tsx | 6 +- .../components/MapButtons/MapButton.tsx | 7 +- .../Measurements/CustomCircleRange.tsx | 2 +- .../MapButtons/Measurements/index.tsx | 10 +- .../components/MapButtons/Missions/index.tsx | 149 ----------- .../PriorNotificationListButton.tsx | 2 +- .../MapButtons/VesselFilters/Filters.tsx | 2 +- .../MapButtons/VesselFilters/index.tsx | 10 +- .../VesselLabels/EditVesselLabels.tsx | 4 +- .../MapButtons/VesselLabels/index.tsx | 8 +- .../VesselVisibility/EditVesselVisibility.tsx | 4 +- .../MapButtons/VesselVisibility/index.tsx | 8 +- .../components/MapButtons/index.tsx | 5 - .../MapButtons/shared/MapToolButton.tsx | 4 +- .../shared/RightMenuOnHoverArea.tsx | 4 +- frontend/src/features/MainWindow/index.tsx | 28 +- .../MainWindow/slice.ts} | 65 +++-- .../Mission/components/MissionMenuDialog.tsx | 129 ++++++++++ .../useCases/getAllRegulatoryLayers.js | 4 +- .../getAllRegulatoryLayersByRegTerritory.ts | 4 +- .../getGeometryWithoutRegulationReference.ts | 4 +- .../useCases/searchRegulatoryLayers.ts | 2 +- .../useCases/showRegulationToEdit.js | 4 +- .../Regulation/useCases/showRegulatoryZone.js | 2 +- .../useCases/showRegulatoryZoneMetadata.ts | 4 +- .../Regulation/useCases/updateRegulation.js | 2 +- .../useCases/updateTopicForAllZones.js | 2 +- .../Reporting/useCases/addReporting.ts | 13 +- .../Reporting/useCases/archiveReporting.js | 2 +- .../Reporting/useCases/archiveReportings.js | 2 +- .../Reporting/useCases/deleteReporting.ts | 14 +- .../Reporting/useCases/deleteReportings.js | 2 +- .../useCases/getAllCurrentReportings.ts | 2 +- .../Reporting/useCases/updateReporting.ts | 2 +- .../SilenceAlertMenu.tsx | 2 +- .../BeaconMalfunctionDetailsFollowUp.tsx | 6 +- .../BeaconMalfunctionBoard/index.tsx | 2 +- .../Vessel/components/VesselLoader.tsx | 4 +- .../Vessel/useCases/applyFilterToVessels.ts | 2 +- .../useCases/showVesselsLastPosition.ts | 2 +- frontend/src/features/VesselList/index.tsx | 6 +- .../VesselSearch/VesselSearchResult.tsx | 2 +- frontend/src/features/VesselSearch/index.tsx | 5 +- frontend/src/features/VesselSidebar/Body.tsx | 2 +- .../VesselSidebarHeader/VesselName.tsx | 12 +- .../VesselSidebarHeader/index.tsx | 10 +- .../actions/TrackRequest/index.tsx | 2 +- .../actions/animate_to_track/index.tsx | 2 +- .../hide_non_selected_vessels/index.tsx | 2 +- .../actions/show_fishing_activities/index.tsx | 2 +- frontend/src/features/VesselSidebar/index.tsx | 2 +- .../ErrorToastNotification.tsx | 2 +- .../features/commonStyles/MapComponent.tsx | 7 - frontend/src/features/map/BaseMap.tsx | 25 +- .../map/layers/InterestPointLayer.jsx | 243 ++++++++++-------- .../VesselAlertAndBeaconMalfunctionLayer.jsx | 72 +++--- .../map/layers/Vessel/VesselAlertLayer.jsx | 76 +++--- .../Vessel/VesselBeaconMalfunctionLayer.jsx | 72 +++--- .../Vessel/VesselEstimatedPositionLayer.jsx | 80 +++--- .../Vessel/VesselInfractionSuspicionLayer.jsx | 76 +++--- .../map/layers/Vessel/VesselsLabelsLayer.tsx | 2 +- .../map/layers/Vessel/VesselsLayer/index.tsx | 4 +- .../PreviewFilteredVessels.jsx | 11 +- .../useClickOutsideWhenOpenedWithinRef.jsx | 34 --- .../useClickOutsideWhenOpenedWithinRef.tsx | 48 ++++ frontend/src/store/reducers.ts | 4 +- 119 files changed, 946 insertions(+), 757 deletions(-) create mode 100644 frontend/src/features/MainWindow/components/LeftMenu.tsx delete mode 100644 frontend/src/features/MainWindow/components/MapButtons/AlertsMapButton.tsx delete mode 100644 frontend/src/features/MainWindow/components/MapButtons/Missions/index.tsx rename frontend/src/{domain/shared_slices/Global.ts => features/MainWindow/slice.ts} (78%) create mode 100644 frontend/src/features/Mission/components/MissionMenuDialog.tsx delete mode 100644 frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.jsx create mode 100644 frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.tsx diff --git a/frontend/src/api/APIWorker.tsx b/frontend/src/api/APIWorker.tsx index 713bb8453b..e5f03c44f4 100644 --- a/frontend/src/api/APIWorker.tsx +++ b/frontend/src/api/APIWorker.tsx @@ -3,7 +3,6 @@ import { useEffect, useRef, useState } from 'react' import { SideWindowStatus } from '../domain/entities/sideWindow/constants' 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' @@ -15,6 +14,7 @@ import { getVesselControls } from '../domain/use_cases/mission/getVesselControls import getAllSpecies from '../domain/use_cases/species/getAllSpecies' import { getVesselReportings } from '../domain/use_cases/vessel/getVesselReportings' import { updateVesselTracks } from '../domain/use_cases/vessel/updateVesselTracks' +import { setIsUpdatingVessels } from '../features/MainWindow/slice' import getAllRegulatoryLayers from '../features/Regulation/useCases/getAllRegulatoryLayers' import { getAllCurrentReportings } from '../features/Reporting/useCases/getAllCurrentReportings' import { useIsSuperUser } from '../hooks/authorization/useIsSuperUser' diff --git a/frontend/src/api/BackofficeMode.tsx b/frontend/src/api/BackofficeMode.tsx index f7494f194b..e119965d18 100644 --- a/frontend/src/api/BackofficeMode.tsx +++ b/frontend/src/api/BackofficeMode.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react' -import { setIsBackoffice } from '../domain/shared_slices/Global' +import { setIsBackoffice } from '../features/MainWindow/slice' import { useMainAppDispatch } from '../hooks/useMainAppDispatch' export type BackofficeModeProps = { diff --git a/frontend/src/domain/entities/vessel/vessel.ts b/frontend/src/domain/entities/vessel/vessel.ts index ae30ff1a72..0345f01eca 100644 --- a/frontend/src/domain/entities/vessel/vessel.ts +++ b/frontend/src/domain/entities/vessel/vessel.ts @@ -26,7 +26,7 @@ export const VESSEL_SELECTOR_STYLE = 200 export class Vessel { static vesselIsMovingSpeed = 0.1 - static getVesselFeatureId(vessel) { + static getVesselFeatureId(vessel: VesselIdentity) { return `${MonitorFishLayer.VESSELS}:${getVesselCompositeIdentifier(vessel)}` } @@ -143,7 +143,7 @@ export class Vessel { } export const getOnlyVesselIdentityProperties = ( - vessel: VesselEnhancedObject | SelectedVessel | VesselTypes.Vessel | Reporting + vessel: VesselEnhancedObject | SelectedVessel | VesselTypes.Vessel | Reporting | VesselIdentity ): VesselIdentity => ({ beaconNumber: 'beaconNumber' in vessel && !!vessel.beaconNumber ? vessel.beaconNumber : null, districtCode: 'districtCode' in vessel && !!vessel.districtCode ? vessel.districtCode : null, @@ -157,7 +157,7 @@ export const getOnlyVesselIdentityProperties = ( vesselName: vessel.vesselName ?? null }) -export const getVesselCompositeIdentifier: (vessel) => VesselCompositeIdentifier = vessel => +export const getVesselCompositeIdentifier: (vessel: VesselIdentity) => VesselCompositeIdentifier = vessel => `${vessel.internalReferenceNumber ?? 'UNKNOWN'}/${vessel.ircs ?? 'UNKNOWN'}/${ vessel.externalReferenceNumber ?? 'UNKNOWN' }` diff --git a/frontend/src/domain/shared_slices/Vessel.ts b/frontend/src/domain/shared_slices/Vessel.ts index 9cca184842..492eea6887 100644 --- a/frontend/src/domain/shared_slices/Vessel.ts +++ b/frontend/src/domain/shared_slices/Vessel.ts @@ -195,7 +195,13 @@ const vesselSlice = createSlice({ state.highlightedVesselTrackPosition = action.payload }, - loadingVessel(state, action) { + loadingVessel( + state, + action: PayloadAction<{ + calledFromCron: boolean + vesselIdentity: VesselIdentity + }> + ) { state.selectedVesselIdentity = action.payload.vesselIdentity state.vesselSidebarIsOpen = true if (!action.payload.calledFromCron) { @@ -303,7 +309,8 @@ const vesselSlice = createSlice({ }) if ( - state.selectedVessel && + !!state.selectedVessel && + !!state.selectedVesselIdentity && Vessel.getVesselFeatureId(state.selectedVesselIdentity) === action.payload.vesselFeatureId ) { const vesselReportingWithoutFirstFoundReportingType = state.selectedVessel.reportings?.reduce( diff --git a/frontend/src/domain/use_cases/alert/addSilencedAlert.ts b/frontend/src/domain/use_cases/alert/addSilencedAlert.ts index bfdfca034b..467f954e8c 100644 --- a/frontend/src/domain/use_cases/alert/addSilencedAlert.ts +++ b/frontend/src/domain/use_cases/alert/addSilencedAlert.ts @@ -1,6 +1,6 @@ import { alertApi } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { setSilencedAlerts } from '../../../features/SideWindow/Alert/slice' -import { setError } from '../../shared_slices/Global' import type { MainAppThunk } from '../../../store' import type { SilencedAlertData } from '../../entities/alerts/types' diff --git a/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts b/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts index 762440ce8a..6faca42709 100644 --- a/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts +++ b/frontend/src/domain/use_cases/alert/getOperationalAlerts.ts @@ -1,6 +1,6 @@ import { getOperationalAlertsFromAPI } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { setPendingAlerts } from '../../../features/SideWindow/Alert/slice' -import { setError } from '../../shared_slices/Global' import type { MainAppThunk } from '../../../store' diff --git a/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts b/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts index b14c4c5175..515e970729 100644 --- a/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts +++ b/frontend/src/domain/use_cases/alert/getSilencedAlerts.ts @@ -1,6 +1,6 @@ import { getSilencedAlertsFromAPI } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { setSilencedAlerts } from '../../../features/SideWindow/Alert/slice' -import { setError } from '../../shared_slices/Global' export const getSilencedAlerts = () => dispatch => { getSilencedAlertsFromAPI() diff --git a/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts b/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts index ea2596bf90..6944916e2c 100644 --- a/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts +++ b/frontend/src/domain/use_cases/alert/reactivateSilencedAlert.ts @@ -1,8 +1,8 @@ import { deleteSilencedAlertFromAPI } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { setSilencedAlerts } from '../../../features/SideWindow/Alert/slice' 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 '../../entities/alerts/types' diff --git a/frontend/src/domain/use_cases/alert/silenceAlert.ts b/frontend/src/domain/use_cases/alert/silenceAlert.ts index 38fc38ebbe..1786779589 100644 --- a/frontend/src/domain/use_cases/alert/silenceAlert.ts +++ b/frontend/src/domain/use_cases/alert/silenceAlert.ts @@ -1,4 +1,5 @@ import { silenceAlertFromAPI } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { addToPendingAlertsBeingSilenced, removeFromSilencedAlertsQueue, @@ -7,7 +8,6 @@ import { } from '../../../features/SideWindow/Alert/slice' import { deleteListItems } from '../../../utils/deleteListItems' import { Vessel } from '../../entities/vessel/vessel' -import { setError } from '../../shared_slices/Global' import { removeVesselAlertAndUpdateReporting } from '../../shared_slices/Vessel' import type { MainAppThunk } from '../../../store' diff --git a/frontend/src/domain/use_cases/alert/validateAlert.ts b/frontend/src/domain/use_cases/alert/validateAlert.ts index 112d1305b7..76f2fb4370 100644 --- a/frontend/src/domain/use_cases/alert/validateAlert.ts +++ b/frontend/src/domain/use_cases/alert/validateAlert.ts @@ -1,9 +1,9 @@ import { validateAlertFromAPI } from '../../../api/alert' +import { setError } from '../../../features/MainWindow/slice' import { setPendingAlerts } from '../../../features/SideWindow/Alert/slice' import { deleteListItems } from '../../../utils/deleteListItems' import { updateListItemsProp } from '../../../utils/updateListItemsProp' import { Vessel } from '../../entities/vessel/vessel' -import { setError } from '../../shared_slices/Global' import { removeVesselAlertAndUpdateReporting } from '../../shared_slices/Vessel' import { getVesselReportings } from '../vessel/getVesselReportings' diff --git a/frontend/src/domain/use_cases/beaconMalfunction/archiveBeaconMalfunctions.ts b/frontend/src/domain/use_cases/beaconMalfunction/archiveBeaconMalfunctions.ts index 766fea0ab7..7b68404b96 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/archiveBeaconMalfunctions.ts +++ b/frontend/src/domain/use_cases/beaconMalfunction/archiveBeaconMalfunctions.ts @@ -1,11 +1,11 @@ import { archiveBeaconMalfunctionsFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setBeaconMalfunctions, setOpenedBeaconMalfunction, updateLocalBeaconMalfunctions, updateVesselBeaconMalfunctionsResumeAndHistory } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' /** * Archive multiple beacon malfunctions diff --git a/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.js b/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.js index fa5786319d..c21234b6fe 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.js +++ b/frontend/src/domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions.js @@ -1,6 +1,6 @@ import { getAllBeaconMalfunctionsFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setBeaconMalfunctions } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' const getAllBeaconMalfunctions = () => dispatch => { getAllBeaconMalfunctionsFromAPI() diff --git a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts b/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts index 0570f80790..fd18e89a15 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts +++ b/frontend/src/domain/use_cases/beaconMalfunction/getVesselBeaconMalfunctions.ts @@ -2,6 +2,7 @@ import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import openBeaconMalfunction from './openBeaconMalfunction' import { getVesselBeaconsMalfunctionsFromAPI } from '../../../api/beaconMalfunction' +import { removeError } from '../../../features/MainWindow/slice' import { getOnlyVesselIdentityProperties } from '../../entities/vessel/vessel' import { loadVesselBeaconMalfunctions, @@ -9,7 +10,6 @@ import { setVesselBeaconMalfunctionsResumeAndHistory } from '../../shared_slices/BeaconMalfunction' import { displayedErrorActions } from '../../shared_slices/DisplayedError' -import { removeError } from '../../shared_slices/Global' import { displayOrLogError } from '../error/displayOrLogError' export const getVesselBeaconMalfunctions = (isFromUserAction: boolean) => async (dispatch, getState) => { diff --git a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js index 67ceee6c4a..7306989eba 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js +++ b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunction.js @@ -1,6 +1,6 @@ import { getBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setOpenedBeaconMalfunction } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' /** * Open a single beacon malfunction diff --git a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts index 634b9eedac..5592a76476 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts +++ b/frontend/src/domain/use_cases/beaconMalfunction/openBeaconMalfunctionInKanban.ts @@ -1,6 +1,6 @@ import { getBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setOpenedBeaconMalfunctionsInKanban } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' /** * Open a single beacon malfunction diff --git a/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js b/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js index 4c1197d4da..5767b59331 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js +++ b/frontend/src/domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban.js @@ -1,10 +1,10 @@ import { saveBeaconMalfunctionCommentFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setOpenedBeaconMalfunction, setOpenedBeaconMalfunctionsInKanban, updateVesselBeaconMalfunctionsResumeAndHistory } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' /** * Save a new comment to a beacon malfunction @@ -13,7 +13,7 @@ import { setError } from '../../shared_slices/Global' * @param {string} comment */ const saveBeaconMalfunctionCommentFromKanban = (beaconMalfunctionId, comment) => (dispatch, getState) => { - const { userType } = getState().global + const { userType } = getState().mainWindow const newCommentInput = { comment, userType diff --git a/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts b/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts index b521989d87..03d335a3b5 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts +++ b/frontend/src/domain/use_cases/beaconMalfunction/sendNotification.ts @@ -1,6 +1,6 @@ import { sendNotificationFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { NOTIFICATION_TYPE } from '../../entities/beaconMalfunction/constants' -import { setError } from '../../shared_slices/Global' /** * Send a notification message diff --git a/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js b/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js index 09b31deb8a..5d071e2632 100644 --- a/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js +++ b/frontend/src/domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban.js @@ -1,4 +1,5 @@ import { updateBeaconMalfunctionFromAPI } from '../../../api/beaconMalfunction' +import { setError } from '../../../features/MainWindow/slice' import { setBeaconMalfunctions, updateLocalBeaconMalfunction, @@ -6,7 +7,6 @@ import { setOpenedBeaconMalfunction, updateVesselBeaconMalfunctionsResumeAndHistory } from '../../shared_slices/BeaconMalfunction' -import { setError } from '../../shared_slices/Global' /** * Update a beacon malfunction diff --git a/frontend/src/domain/use_cases/error/displayOrLogError.ts b/frontend/src/domain/use_cases/error/displayOrLogError.ts index 940ab25744..071b7abdde 100644 --- a/frontend/src/domain/use_cases/error/displayOrLogError.ts +++ b/frontend/src/domain/use_cases/error/displayOrLogError.ts @@ -1,6 +1,6 @@ +import { setError } from '../../../features/MainWindow/slice' import { DisplayedError } from '../../../libs/DisplayedError' import { INITIAL_STATE, type DisplayedErrorState, displayedErrorActions } from '../../shared_slices/DisplayedError' -import { setError } from '../../shared_slices/Global' import type { MainAppUseCase } from '@store' diff --git a/frontend/src/domain/use_cases/faoAreas/getFAOAreas.js b/frontend/src/domain/use_cases/faoAreas/getFAOAreas.js index a92a454210..d6688b52d4 100644 --- a/frontend/src/domain/use_cases/faoAreas/getFAOAreas.js +++ b/frontend/src/domain/use_cases/faoAreas/getFAOAreas.js @@ -1,5 +1,5 @@ import { getFAOAreasFromAPI } from '../../../api/faoAreas' -import { setError } from '../../shared_slices/Global' +import { setError } from '../../../features/MainWindow/slice' const getFAOAreas = () => dispatch => getFAOAreasFromAPI().catch(error => { diff --git a/frontend/src/domain/use_cases/gearCode/getAllGearCodes.js b/frontend/src/domain/use_cases/gearCode/getAllGearCodes.js index 0ef08800c2..f8190e0e95 100644 --- a/frontend/src/domain/use_cases/gearCode/getAllGearCodes.js +++ b/frontend/src/domain/use_cases/gearCode/getAllGearCodes.js @@ -1,10 +1,10 @@ import { batch } from 'react-redux' import { getAllGearsFromAPI } from '../../../api/gearCode' +import { setError } from '../../../features/MainWindow/slice' import { setIsReadyToShowRegulatoryZones } from '../../../features/Regulation/slice' import { REGULATED_GEARS_KEYS } from '../../entities/backoffice' import { setCategoriesToGears, setGears, setGroupsToCategories, setGearsByCode } from '../../shared_slices/Gear' -import { setError } from '../../shared_slices/Global' /** * * Get gear group name, see SQL init of table fishing_gear_groups: diff --git a/frontend/src/domain/use_cases/infraction/getInfractions.ts b/frontend/src/domain/use_cases/infraction/getInfractions.ts index 6a5574ccc2..fe6e9c3141 100644 --- a/frontend/src/domain/use_cases/infraction/getInfractions.ts +++ b/frontend/src/domain/use_cases/infraction/getInfractions.ts @@ -1,5 +1,5 @@ import { getInfractionsFromAPI } from '../../../api/infraction' -import { setError } from '../../shared_slices/Global' +import { setError } from '../../../features/MainWindow/slice' import { setInfractions } from '../../shared_slices/Infraction' export const getInfractions = () => dispatch => { diff --git a/frontend/src/domain/use_cases/map/clickOnMapFeature.ts b/frontend/src/domain/use_cases/map/clickOnMapFeature.ts index 31f757f828..2173c4a114 100644 --- a/frontend/src/domain/use_cases/map/clickOnMapFeature.ts +++ b/frontend/src/domain/use_cases/map/clickOnMapFeature.ts @@ -68,7 +68,7 @@ export const clickOnMapFeature = return } - if (getState().global.previewFilteredVesselsMode) { + if (getState().mainWindow.previewFilteredVesselsMode) { return } diff --git a/frontend/src/domain/use_cases/measurement/saveMeasurement.js b/frontend/src/domain/use_cases/measurement/saveMeasurement.js index 2a9c7432a2..d667d7ae86 100644 --- a/frontend/src/domain/use_cases/measurement/saveMeasurement.js +++ b/frontend/src/domain/use_cases/measurement/saveMeasurement.js @@ -3,8 +3,8 @@ import Circle from 'ol/geom/Circle' import { fromCircle } from 'ol/geom/Polygon' import { batch } from 'react-redux' +import { setRightMapBoxOpened } from '../../../features/MainWindow/slice' import { OPENLAYERS_PROJECTION } from '../../entities/map/constants' -import { setRightMapBoxOpened } from '../../shared_slices/Global' import { addMeasurementDrawed, resetCircleMeasurementInDrawing } from '../../shared_slices/Measurement' const saveMeasurement = (feature, measurement) => dispatch => { diff --git a/frontend/src/domain/use_cases/mission/getVesselControls.ts b/frontend/src/domain/use_cases/mission/getVesselControls.ts index fdea1aad76..3f43cd58d3 100644 --- a/frontend/src/domain/use_cases/mission/getVesselControls.ts +++ b/frontend/src/domain/use_cases/mission/getVesselControls.ts @@ -2,6 +2,7 @@ import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { getVesselControlsFromAPI } from '../../../api/missionAction' import NoControlsFoundError from '../../../errors/NoControlsFoundError' +import { removeError, setError } from '../../../features/MainWindow/slice' import { loadControls, resetLoadControls, @@ -10,7 +11,6 @@ import { unsetControlSummary } from '../../shared_slices/Control' import { displayedErrorActions } from '../../shared_slices/DisplayedError' -import { removeError, setError } from '../../shared_slices/Global' import { displayOrLogError } from '../error/displayOrLogError' export const getVesselControls = (isFromUserAction: boolean) => async (dispatch, getState) => { diff --git a/frontend/src/domain/use_cases/species/getAllSpecies.js b/frontend/src/domain/use_cases/species/getAllSpecies.js index bbffaaa89e..acdd5dd999 100644 --- a/frontend/src/domain/use_cases/species/getAllSpecies.js +++ b/frontend/src/domain/use_cases/species/getAllSpecies.js @@ -1,5 +1,5 @@ import { getAllSpeciesFromAPI } from '../../../api/species' -import { setError } from '../../shared_slices/Global' +import { setError } from '../../../features/MainWindow/slice' import { setSpeciesAndSpeciesGroups } from '../../shared_slices/Species' const getAllSpecies = () => async (dispatch, getState) => { diff --git a/frontend/src/domain/use_cases/vessel/getVesselReportings.ts b/frontend/src/domain/use_cases/vessel/getVesselReportings.ts index b746080ea4..3b1ef71b51 100644 --- a/frontend/src/domain/use_cases/vessel/getVesselReportings.ts +++ b/frontend/src/domain/use_cases/vessel/getVesselReportings.ts @@ -1,13 +1,13 @@ import { DisplayedErrorKey } from '@libs/DisplayedError/constants' import { getVesselReportingsFromAPI } from '../../../api/vessel' +import { removeError } from '../../../features/MainWindow/slice' import { loadReporting, resetCurrentAndArchivedReportingsOfSelectedVessel, setCurrentAndArchivedReportingsOfSelectedVessel } from '../../../features/Reporting/slice' import { displayedErrorActions } from '../../shared_slices/DisplayedError' -import { removeError } from '../../shared_slices/Global' import { displayOrLogError } from '../error/displayOrLogError' export const getVesselReportings = (isFromUserAction: boolean) => async (dispatch, getState) => { diff --git a/frontend/src/domain/use_cases/vessel/searchVessels.ts b/frontend/src/domain/use_cases/vessel/searchVessels.ts index 837bfd5ddc..5c54e37636 100644 --- a/frontend/src/domain/use_cases/vessel/searchVessels.ts +++ b/frontend/src/domain/use_cases/vessel/searchVessels.ts @@ -1,5 +1,5 @@ import { searchVesselsFromAPI } from '../../../api/vessel' -import { setError } from '../../shared_slices/Global' +import { setError } from '../../../features/MainWindow/slice' export const searchVessels = searched => dispatch => searchVesselsFromAPI(searched).catch(error => { diff --git a/frontend/src/domain/use_cases/vessel/showVessel.ts b/frontend/src/domain/use_cases/vessel/showVessel.ts index 89f54f0ccc..3bede6f45e 100644 --- a/frontend/src/domain/use_cases/vessel/showVessel.ts +++ b/frontend/src/domain/use_cases/vessel/showVessel.ts @@ -3,11 +3,11 @@ import { captureMessage } from '@sentry/react' import { getVesselFromAPI } from '../../../api/vessel' import { logbookActions } from '../../../features/Logbook/slice' +import { addSearchedVessel, removeError, setError } from '../../../features/MainWindow/slice' import { Vessel } 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 { doNotAnimate } from '../../shared_slices/Map' import { loadingVessel, resetLoadingVessel, setSelectedVessel } from '../../shared_slices/Vessel' import { displayOrLogError } from '../error/displayOrLogError' @@ -96,7 +96,7 @@ export const showVessel = } } -function dispatchLoadingVessel(dispatch, isFromUserAction: boolean, vesselIdentity) { +function dispatchLoadingVessel(dispatch, isFromUserAction: boolean, vesselIdentity: VesselIdentity) { dispatch(doNotAnimate(!isFromUserAction)) dispatch(removeError()) dispatch( diff --git a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts index ad9a289c58..8f53e07b56 100644 --- a/frontend/src/domain/use_cases/vessel/showVesselTrack.ts +++ b/frontend/src/domain/use_cases/vessel/showVesselTrack.ts @@ -1,10 +1,10 @@ import { transform } from 'ol/proj' import { getVesselPositionsFromAPI } from '../../../api/vessel' +import { removeError, setError } from '../../../features/MainWindow/slice' import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../entities/map/constants' import { getVesselCompositeIdentifier } from '../../entities/vessel/vessel' import { getCustomOrDefaultTrackRequest, throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' -import { removeError, setError } from '../../shared_slices/Global' import { doNotAnimate } from '../../shared_slices/Map' import { addVesselTrackShowed, resetLoadingVessel } from '../../shared_slices/Vessel' diff --git a/frontend/src/domain/use_cases/vessel/unselectVessel.ts b/frontend/src/domain/use_cases/vessel/unselectVessel.ts index df651528e2..0a173605b6 100644 --- a/frontend/src/domain/use_cases/vessel/unselectVessel.ts +++ b/frontend/src/domain/use_cases/vessel/unselectVessel.ts @@ -1,7 +1,7 @@ import { logbookActions } from '../../../features/Logbook/slice' +import { expandRightMenu } from '../../../features/MainWindow/slice' import { resetCurrentAndArchivedReportingsOfSelectedVessel } from '../../../features/Reporting/slice' import { resetVesselBeaconMalfunctionsResumeAndHistory } from '../../shared_slices/BeaconMalfunction' -import { expandRightMenu } from '../../shared_slices/Global' import { closeVesselSidebar, resetSelectedVessel } from '../../shared_slices/Vessel' export const unselectVessel = () => dispatch => { diff --git a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts index 45400e1150..2fba421f82 100644 --- a/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts +++ b/frontend/src/domain/use_cases/vessel/updateSelectedVesselTrackRequest.ts @@ -1,7 +1,7 @@ import { getVesselPositionsFromAPI } from '../../../api/vessel' import { logbookActions } from '../../../features/Logbook/slice' +import { removeError, setError } from '../../../features/MainWindow/slice' import { throwCustomErrorFromAPIFeedback } from '../../entities/vesselTrackDepth' -import { removeError, setError } from '../../shared_slices/Global' import { animateToExtent, doNotAnimate } from '../../shared_slices/Map' import { resetLoadingVessel, diff --git a/frontend/src/features/Account/components/Account.tsx b/frontend/src/features/Account/components/Account.tsx index 8bc2081b39..8391fd7e6f 100644 --- a/frontend/src/features/Account/components/Account.tsx +++ b/frontend/src/features/Account/components/Account.tsx @@ -8,15 +8,15 @@ import styled from 'styled-components' import { UserAccountContext } from '../../../context/UserAccountContext' import { MapBox } from '../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../domain/shared_slices/Global' +import { setRightMapBoxOpened } from '../../MainWindow/slice' -const MARGIN_TOP = 388 +const MARGIN_TOP = 390 export function Account() { const dispatch = useMainAppDispatch() const userAccount = useContext(UserAccountContext) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const openOrClose = () => { dispatch(setRightMapBoxOpened(rightMapBoxOpened === MapBox.ACCOUNT ? undefined : MapBox.ACCOUNT)) diff --git a/frontend/src/features/AdministrativeZone/layers/AdministrativeLayers.tsx b/frontend/src/features/AdministrativeZone/layers/AdministrativeLayers.tsx index 9fc2b08cf5..fd1eaa1303 100644 --- a/frontend/src/features/AdministrativeZone/layers/AdministrativeLayers.tsx +++ b/frontend/src/features/AdministrativeZone/layers/AdministrativeLayers.tsx @@ -15,7 +15,7 @@ import { getVectorOLLayer } from '../useCases/showAdministrativeZone' function UnmemoizedAdministrativeLayers() { const { showedLayers } = useMainAppSelector(state => state.layer) - const isBackoffice = useMainAppSelector(state => state.global.isBackoffice) + const isBackoffice = useMainAppSelector(state => state.mainWindow.isBackoffice) useEffect(() => { if (!showedLayers) { diff --git a/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZoneGeometry.js b/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZoneGeometry.js index 0cddd4e254..ca74ed1228 100644 --- a/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZoneGeometry.js +++ b/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZoneGeometry.js @@ -11,7 +11,7 @@ const getAdministrativeZoneGeometry = dispatchZoneSelected(foundCache.value) } - getAdministrativeZoneFromAPI(administrativeZoneCode, null, subZoneCode, getState().global.isBackoffice) + getAdministrativeZoneFromAPI(administrativeZoneCode, null, subZoneCode, getState().mainWindow.isBackoffice) .then(administrativeZoneFeature => { if (administrativeZoneFeature.numberReturned === 1) { dispatchZoneSelected(administrativeZoneFeature) diff --git a/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZones.ts b/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZones.ts index 7cfa0ad4d4..557ec7417f 100644 --- a/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZones.ts +++ b/frontend/src/features/AdministrativeZone/useCases/getAdministrativeZones.ts @@ -36,14 +36,14 @@ export const getAdministrativeZones = .filter(zone => zone.type === LayerType.ADMINISTRATIVE) .filter(zone => zone.hasFetchableZones) .map(zone => - getAdministrativeSubZonesFromAPI(zone.code, getState().global.isBackoffice).then( + getAdministrativeSubZonesFromAPI(zone.code, getState().mainWindow.isBackoffice).then( (fetchedZones: GeoJSON.FeatureCollection) => { const nextZones: ShowableLayer[] = fetchedZones.features.map(feature => ({ code: feature.id!.toString(), group: zone.group, hasFetchableZones: zone.hasFetchableZones!, name: - (zone.zoneNamePropertyKey && feature.properties?.[zone.zoneNamePropertyKey]?.toString()) || 'Aucun nom', + (zone.zoneNamePropertyKey && feature.properties?.[zone.zoneNamePropertyKey]?.toString()) ?? 'Aucun nom', type: LayerType.ADMINISTRATIVE })) diff --git a/frontend/src/features/AdministrativeZone/useCases/getZonesAndSubZonesPromises.js b/frontend/src/features/AdministrativeZone/useCases/getZonesAndSubZonesPromises.js index 94df1e0f1c..28fc53db63 100644 --- a/frontend/src/features/AdministrativeZone/useCases/getZonesAndSubZonesPromises.js +++ b/frontend/src/features/AdministrativeZone/useCases/getZonesAndSubZonesPromises.js @@ -8,7 +8,7 @@ export const getZonesAndSubZonesPromises = () => (dispatch, getState) => .filter(layer => layer.isIntersectable) .map(zone => { if (zone.hasSearchableZones) { - return getAdministrativeSubZonesFromAPI(zone.code, getState().global.isBackoffice) + return getAdministrativeSubZonesFromAPI(zone.code, getState().mainWindow.isBackoffice) .then(subZonesFeatures => subZonesFeatures.features.map(subZone => ({ code: subZone.id, diff --git a/frontend/src/features/Backoffice/edit_regulation/EditRegulation.tsx b/frontend/src/features/Backoffice/edit_regulation/EditRegulation.tsx index 84f2b17282..8dbc5c8dcb 100644 --- a/frontend/src/features/Backoffice/edit_regulation/EditRegulation.tsx +++ b/frontend/src/features/Backoffice/edit_regulation/EditRegulation.tsx @@ -19,7 +19,6 @@ import ConfirmRegulationModal from './ConfirmRegulationModal' import SpeciesRegulation from './species_regulation/SpeciesRegulation' import { COLORS } from '../../../constants/constants' import { LayerProperties } from '../../../domain/entities/layers/constants' -import { setError } from '../../../domain/shared_slices/Global' import getAllSpecies from '../../../domain/use_cases/species/getAllSpecies' import { useBackofficeAppDispatch } from '../../../hooks/useBackofficeAppDispatch' import { useBackofficeAppSelector } from '../../../hooks/useBackofficeAppSelector' @@ -28,6 +27,7 @@ import { Footer, FooterButton, OtherRemark, Section, Title } from '../../commonS import { CancelButton, ValidateButton } from '../../commonStyles/Buttons.style' import { CustomInput, Label } from '../../commonStyles/Input.style' import ChevronIconSVG from '../../icons/Chevron_simple_gris.svg?react' +import { setError } from '../../MainWindow/slice' import { BaseMap } from '../../map/BaseMap' import { RegulatoryPreviewLayer } from '../../Regulation/layers/RegulatoryPreviewLayer' import { diff --git a/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx index 9344790023..55af3b746a 100644 --- a/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitDialog/index.tsx @@ -25,7 +25,7 @@ export function ControlUnitDialog() { if (!controlUnitId) { throw new FrontendError('`store.controlUnitDialog.controlUnitId` is undefined.') } - const healthcheckTextWarning = useMainAppSelector(store => store.global.healthcheckTextWarning) + const healthcheckTextWarning = useMainAppSelector(store => store.mainWindow.healthcheckTextWarning) const { data: controlUnit, error: getControlControlUnitError } = useGetControlUnitQuery( controlUnitId, diff --git a/frontend/src/features/ControlUnit/components/ControlUnitListDialogButton.tsx b/frontend/src/features/ControlUnit/components/ControlUnitListDialogButton.tsx index 5e104d6ce1..7d4b7b691a 100644 --- a/frontend/src/features/ControlUnit/components/ControlUnitListDialogButton.tsx +++ b/frontend/src/features/ControlUnit/components/ControlUnitListDialogButton.tsx @@ -11,8 +11,8 @@ export function ControlUnitListDialogButton() { const wrapperRef = useRef(null) const dispatch = useMainAppDispatch() - // const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + // const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isControlUnitDialogDisplayed = useMainAppSelector( state => state.displayedComponent.isControlUnitDialogDisplayed ) @@ -41,7 +41,7 @@ export function ControlUnitListDialogButton() { diff --git a/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts b/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts index c308087887..2fcdb1e2c6 100644 --- a/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts +++ b/frontend/src/features/FleetSegment/useCases/addFleetSegmentYear.ts @@ -1,7 +1,7 @@ import { addFleetSegmentYearFromAPI } from '@features/FleetSegment/apis' import { getFleetSegmentsYearEntries } from './getFleetSegmentsYearEntries' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' /** * Add a new fleet segment year diff --git a/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts b/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts index 483be2ac24..68529bde6e 100644 --- a/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts +++ b/frontend/src/features/FleetSegment/useCases/createFleetSegment.ts @@ -1,6 +1,6 @@ import { createFleetSegmentFromAPI } from '@features/FleetSegment/apis' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' import type { FleetSegment, UpdateFleetSegment } from '../types' diff --git a/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts b/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts index 549e036b0f..9b5cb6925a 100644 --- a/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts +++ b/frontend/src/features/FleetSegment/useCases/deleteFleetSegment.ts @@ -1,6 +1,6 @@ import { deleteFleetSegmentFromAPI } from '@features/FleetSegment/apis' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' import type { FleetSegment } from '../types' diff --git a/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts b/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts index fd33380c8d..004d90521a 100644 --- a/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts +++ b/frontend/src/features/FleetSegment/useCases/getFleetSegmentsYearEntries.ts @@ -1,6 +1,6 @@ import { getFleetSegmentYearEntriesFromAPI } from '@features/FleetSegment/apis' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' export const getFleetSegmentsYearEntries = () => dispatch => getFleetSegmentYearEntriesFromAPI().catch(error => { diff --git a/frontend/src/features/FleetSegment/useCases/updateFleetSegment.ts b/frontend/src/features/FleetSegment/useCases/updateFleetSegment.ts index 34657b26e7..c873765e1a 100644 --- a/frontend/src/features/FleetSegment/useCases/updateFleetSegment.ts +++ b/frontend/src/features/FleetSegment/useCases/updateFleetSegment.ts @@ -1,6 +1,6 @@ import { updateFleetSegmentFromAPI } from '@features/FleetSegment/apis' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' import type { FleetSegment, UpdateFleetSegment } from '../types' diff --git a/frontend/src/features/Healthcheck/components/HealthcheckHeadband.tsx b/frontend/src/features/Healthcheck/components/HealthcheckHeadband.tsx index b52ee4676e..7a226a7498 100644 --- a/frontend/src/features/Healthcheck/components/HealthcheckHeadband.tsx +++ b/frontend/src/features/Healthcheck/components/HealthcheckHeadband.tsx @@ -2,19 +2,19 @@ import { useEffect, useState } from 'react' import styled from 'styled-components' import { FIVE_MINUTES } from '../../../api/APIWorker' -import { setError, setHealthcheckTextWarning } from '../../../domain/shared_slices/Global' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { ChevronIcon } from '../../commonStyles/icons/ChevronIcon.style' import WarningSVG from '../../icons/Picto_alerte.svg?react' +import { setError, setHealthcheckTextWarning } from '../../MainWindow/slice' import { useGetHealthcheckQuery } from '../apis' import { useIsOnline } from '../hooks/useIsOnline' import { getHealthcheckWarnings } from '../utils' export function HealthcheckHeadband() { const dispatch = useMainAppDispatch() - const healthcheckTextWarning = useMainAppSelector(state => state.global.healthcheckTextWarning) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const healthcheckTextWarning = useMainAppSelector(state => state.mainWindow.healthcheckTextWarning) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const isOnline = useIsOnline() const { data: healthcheck, diff --git a/frontend/src/features/LayersSidebar/components/index.tsx b/frontend/src/features/LayersSidebar/components/index.tsx index 6144c2198a..1c41d3f826 100644 --- a/frontend/src/features/LayersSidebar/components/index.tsx +++ b/frontend/src/features/LayersSidebar/components/index.tsx @@ -4,7 +4,6 @@ import styled from 'styled-components' import { COLORS } from '../../../constants/constants' import { NamespaceContext } from '../../../context/NamespaceContext' import { MapBox } from '../../../domain/entities/map/constants' -import { setLeftMapBoxOpened } from '../../../domain/shared_slices/Global' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { AdministrativeZones } from '../../AdministrativeZone/components/AdministrativeZones' @@ -13,6 +12,7 @@ import { MapComponent } from '../../commonStyles/MapComponent' import { CustomZones } from '../../CustomZone/components/CustomZones' import LayersSVG from '../../icons/Couches.svg?react' import { MapButton } from '../../MainWindow/components/MapButtons/MapButton' +import { setLeftMapBoxOpened } from '../../MainWindow/slice' import { RegulationSearch } from '../../Regulation/components/RegulationSearch' import { RegulatoryZoneMetadata } from '../../Regulation/components/RegulatoryZoneMetadata' import { RegulatoryZones } from '../../Regulation/components/RegulatoryZones' @@ -23,9 +23,9 @@ export function LayersSidebar() { const regulatoryZoneMetadataPanelIsOpen = useMainAppSelector( state => state.regulatory.regulatoryZoneMetadataPanelIsOpen ) - const healthcheckTextWarning = useMainAppSelector(state => state.global.healthcheckTextWarning) - const leftMapBoxOpened = useMainAppSelector(state => state.global.leftMapBoxOpened) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const healthcheckTextWarning = useMainAppSelector(state => state.mainWindow.healthcheckTextWarning) + const leftMapBoxOpened = useMainAppSelector(state => state.mainWindow.leftMapBoxOpened) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const [numberOfRegulatoryLayersSaved, setNumberOfRegulatoryLayersSaved] = useState(0) diff --git a/frontend/src/features/Logbook/useCases/getVesselLogbook.ts b/frontend/src/features/Logbook/useCases/getVesselLogbook.ts index df14b9444d..3733a9914b 100644 --- a/frontend/src/features/Logbook/useCases/getVesselLogbook.ts +++ b/frontend/src/features/Logbook/useCases/getVesselLogbook.ts @@ -4,10 +4,10 @@ import { customDayjs } from '@mtes-mct/monitor-ui' import { vesselsAreEquals } from '../../../domain/entities/vessel/vessel' import { getTrackRequestFromDates } from '../../../domain/entities/vesselTrackDepth' import { displayedErrorActions } from '../../../domain/shared_slices/DisplayedError' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { displayOrLogError } from '../../../domain/use_cases/error/displayOrLogError' import { updateSelectedVesselTrackRequest } from '../../../domain/use_cases/vessel/updateSelectedVesselTrackRequest' import NoLogbookMessagesFoundError from '../../../errors/NoLogbookMessagesFoundError' +import { removeError, setError } from '../../MainWindow/slice' import { getVesselLogbookFromAPI } from '../api' import { NavigateTo } from '../constants' import { logbookActions } from '../slice' diff --git a/frontend/src/features/MainWindow/components/LeftMenu.tsx b/frontend/src/features/MainWindow/components/LeftMenu.tsx new file mode 100644 index 0000000000..750a5853ca --- /dev/null +++ b/frontend/src/features/MainWindow/components/LeftMenu.tsx @@ -0,0 +1,159 @@ +import { openSideWindowPath } from '@features/SideWindow/useCases/openSideWindowPath' +import { useIsSuperUser } from '@hooks/authorization/useIsSuperUser' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { Icon, IconButton, Size } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' +import { MapBox } from 'domain/entities/map/constants' +import { SideWindowMenuKey, SideWindowStatus } from 'domain/entities/sideWindow/constants' +import { sideWindowActions } from 'domain/shared_slices/SideWindow' +import { useRef } from 'react' +import styled from 'styled-components' + +import { mainWindowActions } from '../slice' + +export function LeftMenu() { + const missionButtonRef = useRef(null) + + const dispatch = useMainAppDispatch() + const { favorites } = useMainAppSelector(state => state.favoriteVessel) + const healthcheckTextWarning = useMainAppSelector(state => state.mainWindow.healthcheckTextWarning) + const leftDialog = useMainAppSelector(state => state.mainWindow.leftDialog) + const isAlertsMapButtonDisplayed = useMainAppSelector(state => state.displayedComponent.isAlertsMapButtonDisplayed) + const isBeaconMalfunctionsMapButtonDisplayed = useMainAppSelector( + state => state.displayedComponent.isBeaconMalfunctionsMapButtonDisplayed + ) + const isFavoriteVesselsMapButtonDisplayed = useMainAppSelector( + state => state.displayedComponent.isFavoriteVesselsMapButtonDisplayed + ) + const isMissionsMapButtonDisplayed = useMainAppSelector( + state => state.displayedComponent.isMissionsMapButtonDisplayed + ) + const isSuperUser = useIsSuperUser() + // const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) + const sideWindow = useMainAppSelector(state => state.sideWindow) + + const toggleMissionMenuDialog = () => { + assertNotNullish(missionButtonRef.current) + + dispatch( + mainWindowActions.toggleLeftDialog({ + key: MapBox.MISSIONS, + // TODO There is something wrong here, we shouldn't have to offset when the bar is opened since it's viewport-related. + // Maybe because of `box-sizing`? + topPosition: missionButtonRef.current.getBoundingClientRect().top - (healthcheckTextWarning.length > 0 ? 50 : 0) + }) + ) + } + + const toggleSideWindowAlertList = () => { + const isActive = + sideWindow.status !== SideWindowStatus.CLOSED && + sideWindow.selectedPath.menu === SideWindowMenuKey.ALERT_LIST_AND_REPORTING_LIST + if (isActive) { + dispatch(sideWindowActions.close()) + + return + } + + dispatch(openSideWindowPath({ menu: SideWindowMenuKey.ALERT_LIST_AND_REPORTING_LIST })) + } + + return ( + + + + + + {isFavoriteVesselsMapButtonDisplayed && ( + + + + {favorites?.length || 0} + + + + + )} + + + {isSuperUser && isMissionsMapButtonDisplayed && ( + + + + )} + + {isSuperUser && isAlertsMapButtonDisplayed && ( + + )} + + {import.meta.env.FRONTEND_PRIOR_NOTIFICATION_LIST_ENABLED === 'true' && ( + + )} + + {isSuperUser && isBeaconMalfunctionsMapButtonDisplayed && } + + + ) +} + +const Wrapper = styled.div` + box-sizing: border-box; + display: flex; + flex-direction: column; + position: absolute; + left: 112px; + top: 12px; + + * { + box-sizing: border-box; + } + + > div:not(:first-child) { + margin-top: 16px; + } +` + +const Block = styled.div` + display: flex; + flex-direction: column; + + > *:not(:first-child) { + margin-top: 4px; + } +` + +const IconButtonWrapper = styled.div` + position: relative; +` +const IconButtonBadge = styled.div<{ + $isActive: boolean +}>` + background-color: ${p => (p.$isActive ? p.theme.color.charcoal : p.theme.color.gainsboro)}; + border-radius: 50%; + color: ${p => (p.$isActive ? p.theme.color.white : p.theme.color.gunMetal)}; + height: 16px; + left: 40px; + line-height: 16px; + position: absolute; + top: -6px; + width: 16px; + z-index: 100; +` diff --git a/frontend/src/features/MainWindow/components/MapButtons/AlertsMapButton.tsx b/frontend/src/features/MainWindow/components/MapButtons/AlertsMapButton.tsx deleted file mode 100644 index 20279fddb5..0000000000 --- a/frontend/src/features/MainWindow/components/MapButtons/AlertsMapButton.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { openSideWindowPath } from '@features/SideWindow/useCases/openSideWindowPath' -import { useMainAppDispatch } from '@hooks/useMainAppDispatch' -import { useMainAppSelector } from '@hooks/useMainAppSelector' -import { useCallback } from 'react' -import styled from 'styled-components' - -import { MapButton } from './MapButton' -import { SideWindowMenuKey, SideWindowStatus } from '../../../../domain/entities/sideWindow/constants' -import { sideWindowActions } from '../../../../domain/shared_slices/SideWindow' -import AlertsSVG from '../../../icons/Icone_alertes.svg?react' - -export function AlertsMapButton() { - const dispatch = useMainAppDispatch() - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) - const sideWindow = useMainAppSelector(state => state.sideWindow) - - const isActive = - sideWindow.status !== SideWindowStatus.CLOSED && - sideWindow.selectedPath.menu === SideWindowMenuKey.ALERT_LIST_AND_REPORTING_LIST - - const toggleSideWindow = useCallback(() => { - if (isActive) { - dispatch(sideWindowActions.close()) - - return - } - - dispatch(openSideWindowPath({ menu: SideWindowMenuKey.ALERT_LIST_AND_REPORTING_LIST })) - }, [dispatch, isActive]) - - return ( - - - - ) -} - -const AlertsButton = styled(MapButton)<{ - $isActive: boolean -}>` - position: absolute; - display: inline-block; - background: ${p => (p.$isActive ? p.theme.color.blueGray : p.theme.color.charcoal)}; - padding: 2px 2px 2px 2px; - top: 162px; - left: 10px; - border-radius: 2px; - height: 40px; - width: 40px; - - :hover, - :focus { - background: ${p => (p.$isActive ? p.theme.color.blueGray : p.theme.color.charcoal)}; - } -` - -const AlertsIcon = styled(AlertsSVG)` - margin-top: 5px; - width: 20px; -` diff --git a/frontend/src/features/MainWindow/components/MapButtons/BeaconMalfunctionsMapButton.tsx b/frontend/src/features/MainWindow/components/MapButtons/BeaconMalfunctionsMapButton.tsx index 193c4dfc7d..e6f78fbff8 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/BeaconMalfunctionsMapButton.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/BeaconMalfunctionsMapButton.tsx @@ -11,7 +11,7 @@ import BeaconMalfunctionsSVG from '../../../icons/Icone_VMS.svg?react' export function BeaconMalfunctionsMapButton() { const dispatch = useMainAppDispatch() - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const sideWindow = useMainAppSelector(state => state.sideWindow) const isActive = diff --git a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx index 6ed85e2692..5bca518528 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/FavoriteVessels/index.tsx @@ -2,7 +2,6 @@ import { COLORS } from '@constants/constants' import { useMainAppDispatch } from '@hooks/useMainAppDispatch' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { MapBox } from 'domain/entities/map/constants' -import { setLeftMapBoxOpened } from 'domain/shared_slices/Global' import { useRef } from 'react' import styled from 'styled-components' @@ -14,6 +13,7 @@ import { MapComponent } from '../../../../commonStyles/MapComponent' import HidingOtherTracksSVG from '../../../../icons/Bouton_masquer_pistes_actif.svg?react' import ShowingOtherTracksSVG from '../../../../icons/Bouton_masquer_pistes_inactif.svg?react' import FavoriteSVG from '../../../../icons/favorite.svg?react' +import { setLeftMapBoxOpened } from '../../../slice' import { MapButton } from '../MapButton' export function FavoriteVessels() { @@ -22,8 +22,8 @@ export function FavoriteVessels() { const { hideNonSelectedVessels, selectedVesselIdentity, vesselsTracksShowed } = useMainAppSelector( state => state.vessel ) - const leftMapBoxOpened = useMainAppSelector(state => state.global.leftMapBoxOpened) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const leftMapBoxOpened = useMainAppSelector(state => state.mainWindow.leftMapBoxOpened) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const wrapperRef = useRef(null) diff --git a/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/EditInterestPoint.tsx b/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/EditInterestPoint.tsx index ca1437702e..f2879729b9 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/EditInterestPoint.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/EditInterestPoint.tsx @@ -258,6 +258,6 @@ const Body = styled.div` ` const Wrapper = styled(MapToolBox)` - top: 333px; + top: 335px; width: 306px; ` diff --git a/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/index.tsx index 5f804c613d..5f57b69adb 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/InterestPoints/index.tsx @@ -6,18 +6,18 @@ import styled from 'styled-components' import { EditInterestPoint } from './EditInterestPoint' import { MapBox } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' import { deleteInterestPointBeingDrawed, drawInterestPoint, endInterestPointDraw } from '../../../../../domain/shared_slices/InterestPoint' import InterestPointSVG from '../../../../icons/standardized/Landmark.svg?react' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolButton } from '../shared/MapToolButton' export function InterestPointMapButton() { const dispatch = useMainAppDispatch() - const { rightMapBoxOpened, rightMenuIsOpen } = useMainAppSelector(state => state.global) + const { rightMapBoxOpened, rightMenuIsOpen } = useMainAppSelector(state => state.mainWindow) const isRightMenuShrinked = !rightMenuIsOpen const isOpen = useMemo(() => rightMapBoxOpened === MapBox.INTEREST_POINT, [rightMapBoxOpened]) const wrapperRef = useRef(null) @@ -50,7 +50,7 @@ export function InterestPointMapButton() { data-cy="interest-point" isActive={isOpen} onClick={openOrCloseInterestPoint} - style={{ top: 333 }} + style={{ top: 335 }} title={"Créer un point d'intérêt"} > diff --git a/frontend/src/features/MainWindow/components/MapButtons/MapButton.tsx b/frontend/src/features/MainWindow/components/MapButtons/MapButton.tsx index 554e03e67d..66d0907806 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/MapButton.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/MapButton.tsx @@ -1,4 +1,3 @@ -import { useMainAppSelector } from '@hooks/useMainAppSelector' import styled from 'styled-components' import type { ReactNode, HTMLProps } from 'react' @@ -8,8 +7,6 @@ type MapButtonType = { isHidden?: boolean | undefined } & HTMLProps export function MapButton({ children, isHidden, ...props }: MapButtonType) { - const healthcheckTextWarning = useMainAppSelector(state => state.global.healthcheckTextWarning) - return ( /** * TODO We have this error without the `ts-ignore` : @@ -18,7 +15,7 @@ export function MapButton({ children, isHidden, ...props }: MapButtonType) { */ /* eslint-disable react/jsx-props-no-spreading */ // @ts-ignore - + {children} /* eslint-enable react/jsx-props-no-spreading */ @@ -26,9 +23,7 @@ export function MapButton({ children, isHidden, ...props }: MapButtonType) { } const Wrapper = styled.button<{ - hasHealthcheckTextWarning?: boolean | undefined isHidden?: boolean | undefined }>` - margin-top: ${p => (p.hasHealthcheckTextWarning ? 50 : 0)}px; visibility: ${p => (p.isHidden ? 'hidden' : 'visible')}; ` diff --git a/frontend/src/features/MainWindow/components/MapButtons/Measurements/CustomCircleRange.tsx b/frontend/src/features/MainWindow/components/MapButtons/Measurements/CustomCircleRange.tsx index b45150eb86..4deb195372 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/Measurements/CustomCircleRange.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/Measurements/CustomCircleRange.tsx @@ -12,7 +12,6 @@ import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' import { resetCircleMeasurementInDrawing, setCircleMeasurementInDrawing, @@ -20,6 +19,7 @@ import { setMeasurementTypeToAdd } from '../../../../../domain/shared_slices/Measurement' import { SetCoordinates } from '../../../../coordinates/SetCoordinates' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolBox } from '../shared/MapToolBox' export function CustomCircleRange() { diff --git a/frontend/src/features/MainWindow/components/MapButtons/Measurements/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/Measurements/index.tsx index 73bdb37f02..61e459d920 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/Measurements/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/Measurements/index.tsx @@ -7,19 +7,19 @@ import styled from 'styled-components' import { CustomCircleRange } from './CustomCircleRange' import { MapBox, MeasurementType } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' import { setMeasurementTypeToAdd } from '../../../../../domain/shared_slices/Measurement' import { MapComponent } from '../../../../commonStyles/MapComponent' import MultiLineSVG from '../../../../icons/standardized/Measure_broken_line.svg?react' import CircleRangeSVG from '../../../../icons/standardized/Measure_circle.svg?react' import MeasurementSVG from '../../../../icons/standardized/Measure_line.svg?react' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolButton } from '../shared/MapToolButton' export function MeasurementMapButton() { const dispatch = useMainAppDispatch() const measurementTypeToAdd = useMainAppSelector(state => state.measurement.measurementTypeToAdd) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isRightMenuShrinked = !rightMenuIsOpen const isOpen = useMemo(() => rightMapBoxOpened === MapBox.MEASUREMENT_MENU, [rightMapBoxOpened]) @@ -71,7 +71,7 @@ export function MeasurementMapButton() { data-cy="measurement" isActive={isOpen || !!measurementTypeToAdd} onClick={openOrCloseMeasurementMenu} - style={{ top: 291 }} + style={{ top: 293 }} title="Mesurer une distance" > {measurementIcon} @@ -149,7 +149,7 @@ const MeasurementOptions = styled(MapComponent)<{ opacity: ${p => (p.isOpen ? '1' : '0')}; position: absolute; right: 10px; - top: 291px; + top: 293px; transition: all 0.5s; width: 135px; z-index: 1000; diff --git a/frontend/src/features/MainWindow/components/MapButtons/Missions/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/Missions/index.tsx deleted file mode 100644 index 15113226b2..0000000000 --- a/frontend/src/features/MainWindow/components/MapButtons/Missions/index.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { COLORS } from '@constants/constants' -import { addMission } from '@features/Mission/useCases/addMission' -import { openSideWindowPath } from '@features/SideWindow/useCases/openSideWindowPath' -import { useMainAppDispatch } from '@hooks/useMainAppDispatch' -import { useMainAppSelector } from '@hooks/useMainAppSelector' -import { Accent, Button, Icon, IconButton, Size, THEME } from '@mtes-mct/monitor-ui' -import { useCallback } from 'react' -import styled from 'styled-components' - -import { MapBox } from '../../../../../domain/entities/map/constants' -import { SideWindowMenuKey, SideWindowStatus } from '../../../../../domain/entities/sideWindow/constants' -import { setDisplayedComponents } from '../../../../../domain/shared_slices/DisplayedComponent' -import { setLeftMapBoxOpened } from '../../../../../domain/shared_slices/Global' -import { sideWindowActions } from '../../../../../domain/shared_slices/SideWindow' -import { MapToolBox } from '../shared/MapToolBox' -import { MapToolButton } from '../shared/MapToolButton' - -export function MissionsMenu() { - const dispatch = useMainAppDispatch() - const sideWindow = useMainAppSelector(state => state.sideWindow) - const leftMapBoxOpened = useMainAppSelector(state => state.global.leftMapBoxOpened) - const isMissionsLayerDisplayed = useMainAppSelector(state => state.displayedComponent.isMissionsLayerDisplayed) - - const isActive = - sideWindow.status !== SideWindowStatus.CLOSED && sideWindow.selectedPath.menu === SideWindowMenuKey.MISSION_LIST - - const toggleMissionsWindow = useCallback(() => { - if (isActive) { - dispatch(sideWindowActions.close()) - - return - } - - dispatch(openSideWindowPath({ menu: SideWindowMenuKey.MISSION_LIST })) - }, [dispatch, isActive]) - - const openNewMission = () => { - dispatch(addMission()) - } - - const toggleMissionsMenu = () => { - dispatch(setLeftMapBoxOpened(leftMapBoxOpened === MapBox.MISSIONS ? undefined : MapBox.MISSIONS)) - } - - const toggleMissionsLayer = () => { - dispatch(setDisplayedComponents({ isMissionsLayerDisplayed: !isMissionsLayerDisplayed })) - } - - return ( - - - - - - Missions et contrôles - - - -
- - Ouvrir une nouvelle mission - -
-
- - Voir la vue détaillée des missions - -
-
-
-
- - - -
- ) -} - -const Wrapper = styled.div` - transition: all 0.2s; - z-index: 98; - left: 10px; -` - -const MissionMenuBox = styled(MapToolBox)` - top: 120px; -` - -const MissionMenuButton = styled(MapToolButton)`` - -const MissionsMenuWrapper = styled.div` - width: 320px; - background-color: ${COLORS.white}; -` - -const MissionsMenuHeader = styled.div` - height: 40px; - background-color: ${COLORS.charcoal}; - display: flex; - justify-content: space-between; - padding-right: 5px; - padding-left: 5px; - align-items: center; -` - -const Title = styled.span` - font-size: 16px; - line-height: 22px; - color: ${COLORS.white}; -` - -const MissionsMenuBody = styled.div`` -const Section = styled.div` - padding: 12px; - &:not(:last-child) { - border-bottom: 1px solid ${COLORS.gainsboro}; - } -` - -const ToggleMissionsButton = styled(IconButton)` - background: ${COLORS.white}; - - :hover { - background: ${COLORS.white}; - } - :focus { - background: ${COLORS.white}; - } -` -const ToggleMissionMenuButton = styled(IconButton)` - color: white; -` -const BlockIconButton = styled(Button)` - width: 100%; -` diff --git a/frontend/src/features/MainWindow/components/MapButtons/PriorNotificationListButton.tsx b/frontend/src/features/MainWindow/components/MapButtons/PriorNotificationListButton.tsx index a130b5b171..ca3efe293e 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/PriorNotificationListButton.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/PriorNotificationListButton.tsx @@ -10,7 +10,7 @@ import { sideWindowActions } from '../../../../domain/shared_slices/SideWindow' export function PriorNotificationListButton() { const dispatch = useMainAppDispatch() - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const sideWindow = useMainAppSelector(state => state.sideWindow) const isActive = diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx index 41b13c9e61..cc8946ae16 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/Filters.tsx @@ -19,7 +19,7 @@ import { MapToolBox } from '../shared/MapToolBox' export function Filters() { const dispatch = useMainAppDispatch() const { filters, nonFilteredVesselsAreHidden } = useMainAppSelector(state => state.filter) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) const isOpen = useMemo(() => rightMapBoxOpened === MapBox.FILTERS, [rightMapBoxOpened]) diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx index da1d60724b..292135af48 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselFilters/index.tsx @@ -7,16 +7,16 @@ import styled from 'styled-components' import { Filters } from './Filters' import { MapBox } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' import FilterSVG from '../../../../icons/standardized/Filter.svg?react' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolButton } from '../shared/MapToolButton' export function VesselFiltersMapButton() { const dispatch = useMainAppDispatch() const filters = useMainAppSelector(state => state.filter.filters) const previousFilters = usePrevious(filters) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isRightMenuShrinked = !rightMenuIsOpen const isOpen = useMemo(() => rightMapBoxOpened === MapBox.FILTERS, [rightMapBoxOpened]) @@ -43,7 +43,7 @@ export function VesselFiltersMapButton() { data-cy="vessel-filters" isActive={isOpen} onClick={openOrCloseVesselFilters} - style={{ top: 110 }} + style={{ top: 112 }} title="Mes filtres" > @@ -69,7 +69,7 @@ const NewFilterAdded = styled.div<{ padding: 11px 12px; position: absolute; right: -150px; - top: 110px; + top: 112px; width: 86px; z-index: 9999; diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/EditVesselLabels.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/EditVesselLabels.tsx index 4b88f49d90..aae06432d7 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/EditVesselLabels.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/EditVesselLabels.tsx @@ -24,7 +24,7 @@ export function EditVesselLabels() { const vesselLabel = useMainAppSelector(state => state.map.vesselLabel) const riskFactorShowedOnMap = useMainAppSelector(state => state.map.riskFactorShowedOnMap) const vesselLabelsShowedOnMap = useMainAppSelector(state => state.map.vesselLabelsShowedOnMap) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) const isOpen = useMemo(() => rightMapBoxOpened === MapBox.VESSEL_LABELS, [rightMapBoxOpened]) @@ -62,5 +62,5 @@ export function EditVesselLabels() { const Wrapper = styled(MapToolBox)` width: 406px; - top: 194px; + top: 196px; ` diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/index.tsx index 5c853b1000..219e08208e 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselLabels/index.tsx @@ -7,13 +7,13 @@ import styled from 'styled-components' import { EditVesselLabels } from './EditVesselLabels' import { MapBox } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolButton } from '../shared/MapToolButton' export function VesselLabelsMapButton() { const dispatch = useMainAppDispatch() - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isRightMenuShrinked = !rightMenuIsOpen const isOpen = useMemo(() => rightMapBoxOpened === MapBox.VESSEL_LABELS, [rightMapBoxOpened]) @@ -37,7 +37,7 @@ export function VesselLabelsMapButton() { data-cy="vessel-labels" isActive={isOpen} onClick={openOrCloseVesselLabels} - style={{ top: 194 }} + style={{ top: 196 }} title="Affichage des dernières positions" > diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/EditVesselVisibility.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/EditVesselVisibility.tsx index 57631c3c0b..0bbcb0d1fd 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/EditVesselVisibility.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/EditVesselVisibility.tsx @@ -25,7 +25,7 @@ import { Header, Content } from '../shared/styles' export function EditVesselVisibility() { const dispatch = useMainAppDispatch() const hideNonSelectedVessels = useMainAppSelector(state => state.vessel.hideNonSelectedVessels) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) const hideVesselsAtPort = useMainAppSelector(state => state.map.hideVesselsAtPort) const defaultVesselTrackDepth = useMainAppSelector(state => state.map.defaultVesselTrackDepth) const showingVesselsEstimatedPositions = useMainAppSelector(state => state.map.showingVesselsEstimatedPositions) @@ -144,6 +144,6 @@ const Wrapper = styled(MapToolBox)<{ isHidden?: boolean isOpen: boolean }>` - top: 152px; + top: 154px; width: 406px; ` diff --git a/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/index.tsx index 6ae8a56b0c..3503637ada 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/VesselVisibility/index.tsx @@ -7,13 +7,13 @@ import styled from 'styled-components' import { EditVesselVisibility } from './EditVesselVisibility' import { MapBox } from '../../../../../domain/entities/map/constants' -import { setRightMapBoxOpened } from '../../../../../domain/shared_slices/Global' +import { setRightMapBoxOpened } from '../../../slice' import { MapToolButton } from '../shared/MapToolButton' export function VesselVisibilityMapButton() { const dispatch = useMainAppDispatch() - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isRightMenuShrinked = !rightMenuIsOpen const isOpen = useMemo(() => rightMapBoxOpened === MapBox.VESSEL_VISIBILITY, [rightMapBoxOpened]) @@ -37,7 +37,7 @@ export function VesselVisibilityMapButton() { data-cy="vessel-visibility" isActive={isOpen} onClick={openOrCloseVesselVisibility} - style={{ top: 152 }} + style={{ top: 154 }} title="Affichage des dernières positions" > diff --git a/frontend/src/features/MainWindow/components/MapButtons/index.tsx b/frontend/src/features/MainWindow/components/MapButtons/index.tsx index ce470484bd..205a71a306 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/index.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/index.tsx @@ -4,12 +4,10 @@ import { useIsSuperUser } from '@hooks/authorization/useIsSuperUser' import { useMainAppSelector } from '@hooks/useMainAppSelector' import { LegacyRsuiteComponentsWrapper } from 'ui/LegacyRsuiteComponentsWrapper' -import { AlertsMapButton } from './AlertsMapButton' import { BeaconMalfunctionsMapButton } from './BeaconMalfunctionsMapButton' import { FavoriteVessels } from './FavoriteVessels' import { InterestPointMapButton } from './InterestPoints' import { MeasurementMapButton } from './Measurements' -import { MissionsMenu } from './Missions' import { PriorNotificationListButton } from './PriorNotificationListButton' import { VesselFiltersMapButton } from './VesselFilters' import { VesselLabelsMapButton } from './VesselLabels' @@ -17,7 +15,6 @@ import { VesselVisibilityMapButton } from './VesselVisibility' export function MapButtons() { const isSuperUser = useIsSuperUser() - const isAlertsMapButtonDisplayed = useMainAppSelector(state => state.displayedComponent.isAlertsMapButtonDisplayed) const isBeaconMalfunctionsMapButtonDisplayed = useMainAppSelector( state => state.displayedComponent.isBeaconMalfunctionsMapButtonDisplayed ) @@ -44,8 +41,6 @@ export function MapButtons() { <> {isFavoriteVesselsMapButtonDisplayed && } - {isSuperUser && isFavoriteVesselsMapButtonDisplayed && } - {isSuperUser && isAlertsMapButtonDisplayed && } {import.meta.env.FRONTEND_PRIOR_NOTIFICATION_LIST_ENABLED === 'true' && } {isSuperUser && isBeaconMalfunctionsMapButtonDisplayed && } diff --git a/frontend/src/features/MainWindow/components/MapButtons/shared/MapToolButton.tsx b/frontend/src/features/MainWindow/components/MapButtons/shared/MapToolButton.tsx index 92c8a828eb..16bc820ba1 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/shared/MapToolButton.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/shared/MapToolButton.tsx @@ -11,8 +11,8 @@ type MapToolButtonProps = { isLeftButton?: boolean } & HTMLProps export function MapToolButton({ children, isActive, isLeftButton = false, ...props }: MapToolButtonProps) { - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isRightMenuShrinked = !rightMenuIsOpen && !isLeftButton return ( diff --git a/frontend/src/features/MainWindow/components/MapButtons/shared/RightMenuOnHoverArea.tsx b/frontend/src/features/MainWindow/components/MapButtons/shared/RightMenuOnHoverArea.tsx index 632f3ee476..a41f7db02a 100644 --- a/frontend/src/features/MainWindow/components/MapButtons/shared/RightMenuOnHoverArea.tsx +++ b/frontend/src/features/MainWindow/components/MapButtons/shared/RightMenuOnHoverArea.tsx @@ -4,12 +4,12 @@ import { useMainAppSelector } from '@hooks/useMainAppSelector' import { useEffect, useRef } from 'react' import styled from 'styled-components' -import { contractRightMenu, expandRightMenu } from '../../../../../domain/shared_slices/Global' +import { contractRightMenu, expandRightMenu } from '../../../slice' export function RightMenuOnHoverArea() { const dispatch = useMainAppDispatch() const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) - const rightMapBoxOpened = useMainAppSelector(state => state.global.rightMapBoxOpened) + const rightMapBoxOpened = useMainAppSelector(state => state.mainWindow.rightMapBoxOpened) const areaRef = useRef(null) const clickedOutsideComponent = useClickOutsideWhenOpened(areaRef, !!selectedVessel) diff --git a/frontend/src/features/MainWindow/index.tsx b/frontend/src/features/MainWindow/index.tsx index 1902c6fb77..b3303f59c6 100644 --- a/frontend/src/features/MainWindow/index.tsx +++ b/frontend/src/features/MainWindow/index.tsx @@ -1,8 +1,11 @@ +import { MissionMenuDialog } from '@features/Mission/components/MissionMenuDialog' +import { MapBox } from 'domain/entities/map/constants' import { useCallback } from 'react' import { useBeforeUnload } from 'react-router-dom' import styled from 'styled-components' import { LegacyRsuiteComponentsWrapper } from 'ui/LegacyRsuiteComponentsWrapper' +import { LeftMenu } from './components/LeftMenu' import { MapButtons } from './components/MapButtons' import { RightMenuOnHoverArea } from './components/MapButtons/shared/RightMenuOnHoverArea' import { APIWorker } from '../../api/APIWorker' @@ -35,6 +38,7 @@ export function MainWindow() { const isVesselSearchDisplayed = useMainAppSelector(state => state.displayedComponent.isVesselSearchDisplayed) const isVesselSidebarOpen = useMainAppSelector(state => state.vessel.vesselSidebarIsOpen) const isDraftDirty = useMainAppSelector(state => state.missionForm.isDraftDirty) + const leftDialog = useMainAppSelector(state => state.mainWindow.leftDialog) const status = useMainAppSelector(state => state.sideWindow.status) const warnOnUnload = useCallback( @@ -53,14 +57,17 @@ export function MainWindow() { useBeforeUnload(warnOnUnload) return ( - <> + - + + - + + {leftDialog?.key === MapBox.MISSIONS && } + {isVesselSearchDisplayed && } @@ -80,13 +87,22 @@ export function MainWindow() { {status !== SideWindowStatus.CLOSED && } {isDrawLayerModalDisplayed && } - - + + ) } const Wrapper = styled.div` + display: flex; + flex-direction: column; + height: 100vh; + width: 100vw; +` + +const MapWrapper = styled.div` + box-sizing: border-box; + flex-grow: 1; font-size: 13px; overflow: hidden; - width: 100vw; + position: relative; ` diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/features/MainWindow/slice.ts similarity index 78% rename from frontend/src/domain/shared_slices/Global.ts rename to frontend/src/features/MainWindow/slice.ts index 727b616303..61480a19ec 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/features/MainWindow/slice.ts @@ -1,17 +1,17 @@ import { createSlice } from '@reduxjs/toolkit' +import { UserType } from '../../domain/entities/beaconMalfunction/constants' +import { getOnlyVesselIdentityProperties, vesselsAreEquals } from '../../domain/entities/vessel/vessel' import { getLocalStorageState } from '../../utils' -import { UserType } from '../entities/beaconMalfunction/constants' -import { getOnlyVesselIdentityProperties, vesselsAreEquals } from '../entities/vessel/vessel' -import type { MapBox } from '../entities/map/constants' +import type { MapBox } from '../../domain/entities/map/constants' import type { PayloadAction } from '@reduxjs/toolkit' +import type { VesselIdentity } from 'domain/entities/vessel/types' const userTypeLocalStorageKey = 'userType' const lastSearchedVesselsLocalStorageKey = 'lastSearchedVessels' -// TODO Properly type this redux state. -export type GlobalState = { +export type MainWindowState = { blockVesselsUpdate: boolean error: any // TODO Rename this prop. @@ -19,6 +19,12 @@ export type GlobalState = { isBackoffice: boolean isUpdatingVessels: boolean lastSearchedVessels: any[] + leftDialog: + | { + key: MapBox + topPosition: number + } + | undefined leftMapBoxOpened: MapBox | undefined // TODO Rename this prop. // TODO Investigate that. Should be a defined boolean. @@ -28,13 +34,14 @@ export type GlobalState = { userType: string vesselListModalIsOpen: boolean } -const INITIAL_STATE: GlobalState = { +const INITIAL_STATE: MainWindowState = { blockVesselsUpdate: false, error: null, healthcheckTextWarning: [], isBackoffice: false, isUpdatingVessels: false, lastSearchedVessels: getLocalStorageState([], lastSearchedVesselsLocalStorageKey), + leftDialog: undefined, leftMapBoxOpened: undefined, previewFilteredVesselsMode: undefined, rightMapBoxOpened: undefined, @@ -43,20 +50,16 @@ const INITIAL_STATE: GlobalState = { vesselListModalIsOpen: false } -// TODO Properly type this redux reducers. -export const globalSlice = createSlice({ +export const mainWindowSlice = createSlice({ initialState: INITIAL_STATE, name: 'global', reducers: { /** * Adds a vessel to the last searched vessels list showed below * the vessel search input on click - * @function addLastSearchedVessel - * @memberOf GlobalReducer - * @param {Object=} state - * @param {{payload: VesselIdentity}} action - The last searched vessel */ - addSearchedVessel(state, action) { + addSearchedVessel(state, action: PayloadAction) { + // TODO Is it useful since it seems we already receive a `VesselIdentity` (if typings are right)? const vesselIdentityToAdd = getOnlyVesselIdentityProperties(action.payload) // Remove vessel if already in the list @@ -75,6 +78,13 @@ export const globalSlice = createSlice({ window.localStorage.setItem(lastSearchedVesselsLocalStorageKey, JSON.stringify(state.lastSearchedVessels)) }, + /** + * Close the left dialog. + */ + closeLeftDialog(state) { + state.leftDialog = undefined + }, + closeVesselListModal(state) { state.vesselListModalIsOpen = false }, @@ -103,10 +113,8 @@ export const globalSlice = createSlice({ /** * Block or not the vessel update cron - The vessel update is blocked when the * vessel list table is opened or when vessels filters are previewed - * @param {Object=} state - * @param {{payload: boolean}} action - blocked when true */ - setBlockVesselsUpdate(state, action) { + setBlockVesselsUpdate(state, action: PayloadAction) { state.blockVesselsUpdate = action.payload }, @@ -161,20 +169,29 @@ export const globalSlice = createSlice({ /** * Set the user type as OPS or SIP - * @function setUserType - * @memberOf GlobalReducer - * @param {Object=} state - * @param {{payload: string}} action - The user type */ - setUserType(state, action) { + setUserType(state, action: PayloadAction) { state.userType = action.payload window.localStorage.setItem(userTypeLocalStorageKey, JSON.stringify(state.userType)) + }, + + /** + * Toggle the left dialog. + */ + toggleLeftDialog( + state, + action: PayloadAction<{ + key: MapBox + topPosition: number + }> + ) { + state.leftDialog = action.payload.key === state.leftDialog?.key ? undefined : action.payload } } }) -export const globalActions = globalSlice.actions -export const globalSliceReducer = globalSlice.reducer +export const mainWindowActions = mainWindowSlice.actions +export const mainWindowSliceReducer = mainWindowSlice.reducer export const { addSearchedVessel, @@ -193,4 +210,4 @@ export const { setPreviewFilteredVesselsMode, setRightMapBoxOpened, setUserType -} = globalSlice.actions +} = mainWindowSlice.actions diff --git a/frontend/src/features/Mission/components/MissionMenuDialog.tsx b/frontend/src/features/Mission/components/MissionMenuDialog.tsx new file mode 100644 index 0000000000..fd563b083c --- /dev/null +++ b/frontend/src/features/Mission/components/MissionMenuDialog.tsx @@ -0,0 +1,129 @@ +import { addMission } from '@features/Mission/useCases/addMission' +import { openSideWindowPath } from '@features/SideWindow/useCases/openSideWindowPath' +import { useMainAppDispatch } from '@hooks/useMainAppDispatch' +import { useMainAppSelector } from '@hooks/useMainAppSelector' +import { Accent, Button, Icon, IconButton, Size } from '@mtes-mct/monitor-ui' +import { assertNotNullish } from '@utils/assertNotNullish' +import { useCallback } from 'react' +import styled from 'styled-components' + +import { SideWindowMenuKey, SideWindowStatus } from '../../../domain/entities/sideWindow/constants' +import { setDisplayedComponents } from '../../../domain/shared_slices/DisplayedComponent' +import { sideWindowActions } from '../../../domain/shared_slices/SideWindow' +import { mainWindowActions } from '../../MainWindow/slice' + +export function MissionMenuDialog() { + const dispatch = useMainAppDispatch() + const sideWindow = useMainAppSelector(state => state.sideWindow) + const isMissionsLayerDisplayed = useMainAppSelector(state => state.displayedComponent.isMissionsLayerDisplayed) + const leftDialog = useMainAppSelector(state => state.mainWindow.leftDialog) + assertNotNullish(leftDialog) + + const isActive = + sideWindow.status !== SideWindowStatus.CLOSED && sideWindow.selectedPath.menu === SideWindowMenuKey.MISSION_LIST + + const toggleMissionsWindow = useCallback(() => { + if (isActive) { + dispatch(sideWindowActions.close()) + + return + } + + dispatch(openSideWindowPath({ menu: SideWindowMenuKey.MISSION_LIST })) + }, [dispatch, isActive]) + + const openNewMission = () => { + dispatch(addMission()) + } + + const close = () => { + dispatch(mainWindowActions.closeLeftDialog()) + } + + const toggleMissionsLayer = () => { + dispatch(setDisplayedComponents({ isMissionsLayerDisplayed: !isMissionsLayerDisplayed })) + } + + return ( + + + + Missions et contrôles + + + +
+ + Ouvrir une nouvelle mission + +
+
+ + Voir la vue détaillée des missions + +
+
+
+ ) +} + +const Wrapper = styled.div` + box-sizing: border-box; + background-color: ${p => p.theme.color.white}; + left: 160px; + position: absolute; + transition: all 0.2s; + width: 320px; + + * { + box-sizing: border-box; + } +` + +const MissionsMenuHeader = styled.div` + height: 40px; + background-color: ${p => p.theme.color.charcoal}; + display: flex; + justify-content: space-between; + padding-right: 5px; + padding-left: 5px; + align-items: center; +` + +const Title = styled.span` + font-size: 16px; + line-height: 22px; + color: ${p => p.theme.color.white}; +` + +const MissionsMenuBody = styled.div`` +const Section = styled.div` + padding: 12px; + &:not(:last-child) { + border-bottom: 1px solid ${p => p.theme.color.gainsboro}; + } +` + +const ToggleMissionsButton = styled(IconButton)` + background: ${p => p.theme.color.white}; + + :hover { + background: ${p => p.theme.color.white}; + } + :focus { + background: ${p => p.theme.color.white}; + } +` +const ToggleMissionMenuButton = styled(IconButton)` + color: white; +` +const BlockIconButton = styled(Button)` + width: 100%; +` diff --git a/frontend/src/features/Regulation/useCases/getAllRegulatoryLayers.js b/frontend/src/features/Regulation/useCases/getAllRegulatoryLayers.js index a917be9bac..6d07023a3e 100644 --- a/frontend/src/features/Regulation/useCases/getAllRegulatoryLayers.js +++ b/frontend/src/features/Regulation/useCases/getAllRegulatoryLayers.js @@ -1,9 +1,9 @@ import { batch } from 'react-redux' import { getAllRegulatoryLayersFromAPI } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' import layer from '../../../domain/shared_slices/Layer' import { MonitorFishWorker } from '../../../workers/MonitorFishWorker' +import { setError } from '../../MainWindow/slice' import { setLayersTopicsByRegTerritory, setRegulatoryLayerLawTypes, @@ -16,7 +16,7 @@ const getAllRegulatoryLayers = () => async (dispatch, getState) => { const { setShowedLayersWithLocalStorageValues } = layer.homepage.actions const { speciesByCode } = getState().species - return getAllRegulatoryLayersFromAPI(getState().global.isBackoffice) + return getAllRegulatoryLayersFromAPI(getState().mainWindow.isBackoffice) .then(features => { monitorFishWorker.mapGeoserverToRegulatoryZones(features, speciesByCode).then(regulatoryZones => { dispatch(setRegulatoryZones(regulatoryZones)) diff --git a/frontend/src/features/Regulation/useCases/getAllRegulatoryLayersByRegTerritory.ts b/frontend/src/features/Regulation/useCases/getAllRegulatoryLayersByRegTerritory.ts index 0c5229774b..b55bd9f95c 100644 --- a/frontend/src/features/Regulation/useCases/getAllRegulatoryLayersByRegTerritory.ts +++ b/frontend/src/features/Regulation/useCases/getAllRegulatoryLayersByRegTerritory.ts @@ -1,6 +1,6 @@ import { getAllRegulatoryLayersFromAPI } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' import { MonitorFishWorker } from '../../../workers/MonitorFishWorker' +import { setError } from '../../MainWindow/slice' import { setLayersTopicsByRegTerritory, setRegulatoryLayerLawTypes } from '../slice' export const getAllRegulatoryLayersByRegTerritory = () => async (dispatch, getState) => { @@ -13,7 +13,7 @@ export const getAllRegulatoryLayersByRegTerritory = () => async (dispatch, getSt } try { - const features = await getAllRegulatoryLayersFromAPI(getState().global.isBackoffice) + const features = await getAllRegulatoryLayersFromAPI(getState().mainWindow.isBackoffice) const { layersTopicsByRegulatoryTerritory } = await monitorFishWorker.convertGeoJSONFeaturesToStructuredRegulatoryObject(features, speciesByCode) diff --git a/frontend/src/features/Regulation/useCases/getGeometryWithoutRegulationReference.ts b/frontend/src/features/Regulation/useCases/getGeometryWithoutRegulationReference.ts index 1f3b2ba2c2..42d2e49a23 100644 --- a/frontend/src/features/Regulation/useCases/getGeometryWithoutRegulationReference.ts +++ b/frontend/src/features/Regulation/useCases/getGeometryWithoutRegulationReference.ts @@ -1,6 +1,6 @@ import { getAllGeometryWithoutProperty } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' import { MonitorFishWorker } from '../../../workers/MonitorFishWorker' +import { setError } from '../../MainWindow/slice' import type { GeoJSON } from '../../../domain/types/GeoJSON' @@ -10,7 +10,7 @@ export const getGeometryWithoutRegulationReference = const monitorFishWorker = await MonitorFishWorker try { - const features = await getAllGeometryWithoutProperty(getState().global.isBackoffice) + const features = await getAllGeometryWithoutProperty(getState().mainWindow.isBackoffice) return await monitorFishWorker.getIdToGeometryObject(features) } catch (e) { diff --git a/frontend/src/features/Regulation/useCases/searchRegulatoryLayers.ts b/frontend/src/features/Regulation/useCases/searchRegulatoryLayers.ts index 5d8d3073a4..9aa0e480e7 100644 --- a/frontend/src/features/Regulation/useCases/searchRegulatoryLayers.ts +++ b/frontend/src/features/Regulation/useCases/searchRegulatoryLayers.ts @@ -23,7 +23,7 @@ export const searchRegulatoryLayers = searchQuery => async (_, getState) => { const extent = getExtentFromGeoJSON(zoneSelected.feature) if (extent?.length === 4) { - return getRegulatoryZonesInExtentFromAPI(extent, state.global.isBackoffice) + return getRegulatoryZonesInExtentFromAPI(extent, state.mainWindow.isBackoffice) .then(features => monitorFishWorker.mapGeoserverToRegulatoryZones(features, speciesByCode)) .then(filteredRegulatoryZones => { if (searchQuery?.length < MINIMUM_SEARCH_CHARACTERS_NUMBER) { diff --git a/frontend/src/features/Regulation/useCases/showRegulationToEdit.js b/frontend/src/features/Regulation/useCases/showRegulationToEdit.js index 6c47a3f177..12f97f2acb 100644 --- a/frontend/src/features/Regulation/useCases/showRegulationToEdit.js +++ b/frontend/src/features/Regulation/useCases/showRegulationToEdit.js @@ -1,15 +1,15 @@ import { getRegulatoryZoneFromAPI, REGULATORY_ZONE_METADATA_ERROR_MESSAGE } from '../../../api/geoserver' import { LayerProperties } from '../../../domain/entities/layers/constants' -import { setError } from '../../../domain/shared_slices/Global' import { STATUS } from '../../Backoffice/constants' import { setProcessingRegulation, setSelectedRegulatoryZoneId, setStatus } from '../../Backoffice/slice' +import { setError } from '../../MainWindow/slice' import { mapToRegulatoryZone, DEFAULT_REGULATORY_TEXT } from '../utils' const showRegulationToEdit = regulatoryZone => async (dispatch, getState) => { const { speciesByCode } = getState().species dispatch(setStatus(STATUS.LOADING)) - return getRegulatoryZoneFromAPI(LayerProperties.REGULATORY.code, regulatoryZone, getState().global.isBackoffice) + return getRegulatoryZoneFromAPI(LayerProperties.REGULATORY.code, regulatoryZone, getState().mainWindow.isBackoffice) .then(feature => { const regulatoryZoneMetadata = mapToRegulatoryZone(feature, speciesByCode) diff --git a/frontend/src/features/Regulation/useCases/showRegulatoryZone.js b/frontend/src/features/Regulation/useCases/showRegulatoryZone.js index 151034faac..269a46c519 100644 --- a/frontend/src/features/Regulation/useCases/showRegulatoryZone.js +++ b/frontend/src/features/Regulation/useCases/showRegulatoryZone.js @@ -67,7 +67,7 @@ const getRegulatoryVectorSource = (dispatch, getState) => regulatoryZoneProperti getRegulatoryZoneFromAPI( LayerProperties.REGULATORY.code, regulatoryZoneProperties, - getState().global.isBackoffice + getState().mainWindow.isBackoffice ) .then(regulatoryZone => { if (!regulatoryZone.geometry) { diff --git a/frontend/src/features/Regulation/useCases/showRegulatoryZoneMetadata.ts b/frontend/src/features/Regulation/useCases/showRegulatoryZoneMetadata.ts index f2987bd44d..0383fa64f1 100644 --- a/frontend/src/features/Regulation/useCases/showRegulatoryZoneMetadata.ts +++ b/frontend/src/features/Regulation/useCases/showRegulatoryZoneMetadata.ts @@ -1,5 +1,5 @@ import { getRegulatoryFeatureMetadataFromAPI } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' import { closeRegulatoryZoneMetadataPanel, resetLoadingRegulatoryZoneMetadata, @@ -19,7 +19,7 @@ export const showRegulatoryZoneMetadata = dispatch(setLoadingRegulatoryZoneMetadata()) const { speciesByCode } = getState().species - getRegulatoryFeatureMetadataFromAPI(partialRegulatoryZone, getState().global.isBackoffice) + getRegulatoryFeatureMetadataFromAPI(partialRegulatoryZone, getState().mainWindow.isBackoffice) .then(feature => { const parsedRegulatoryZone = mapToRegulatoryZone(feature, speciesByCode) dispatch(setRegulatoryZoneMetadata(parsedRegulatoryZone)) diff --git a/frontend/src/features/Regulation/useCases/updateRegulation.js b/frontend/src/features/Regulation/useCases/updateRegulation.js index e6012e8f35..808be84d6b 100644 --- a/frontend/src/features/Regulation/useCases/updateRegulation.js +++ b/frontend/src/features/Regulation/useCases/updateRegulation.js @@ -1,6 +1,6 @@ import { sendRegulationTransaction } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' import { setProcessingRegulationSaved, setProcessingRegulationDeleted } from '../../Backoffice/slice' +import { setError } from '../../MainWindow/slice' import { REGULATION_ACTION_TYPE } from '../utils' const updateRegulation = (feature, type) => dispatch => diff --git a/frontend/src/features/Regulation/useCases/updateTopicForAllZones.js b/frontend/src/features/Regulation/useCases/updateTopicForAllZones.js index 9f68a703fd..0ba65471bc 100644 --- a/frontend/src/features/Regulation/useCases/updateTopicForAllZones.js +++ b/frontend/src/features/Regulation/useCases/updateTopicForAllZones.js @@ -1,7 +1,7 @@ import { Feature } from 'ol' import { sendRegulationTransaction } from '../../../api/geoserver' -import { setError } from '../../../domain/shared_slices/Global' +import { setError } from '../../MainWindow/slice' import { setLayersTopicsByRegTerritory, setRegulatoryLayerLawTypes } from '../slice' import { getRegulatoryFeatureId, mapToRegulatoryFeatureObject, REGULATION_ACTION_TYPE } from '../utils' diff --git a/frontend/src/features/Reporting/useCases/addReporting.ts b/frontend/src/features/Reporting/useCases/addReporting.ts index d14a22db36..2d156c4ad3 100644 --- a/frontend/src/features/Reporting/useCases/addReporting.ts +++ b/frontend/src/features/Reporting/useCases/addReporting.ts @@ -1,7 +1,9 @@ +import { logSoftError } from '@mtes-mct/monitor-ui' + import { addReportingFromAPI } from '../../../api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { addVesselReporting } from '../../../domain/shared_slices/Vessel' +import { removeError, setError } from '../../MainWindow/slice' import { addReportingToCurrentReportings, setCurrentAndArchivedReportingsOfSelectedVessel } from '../slice' import type { ReportingCreation } from '../../../domain/types/reporting' @@ -11,6 +13,15 @@ export const addReporting = (newReporting: ReportingCreation): MainAppThunk> => (dispatch, getState) => { const { selectedVesselIdentity } = getState().vessel + // TODO Can this case happen? Is it the right way to handle it? + if (!selectedVesselIdentity) { + logSoftError({ + message: '`selectedVesselIdentity` is null.', + userMessage: 'Aucun navire sélectionné pour ajouter un signalement.' + }) + + return Promise.resolve() + } const { currentAndArchivedReportingsOfSelectedVessel } = getState().reporting return addReportingFromAPI(newReporting) diff --git a/frontend/src/features/Reporting/useCases/archiveReporting.js b/frontend/src/features/Reporting/useCases/archiveReporting.js index dd7a20273a..5fa5c5d0e1 100644 --- a/frontend/src/features/Reporting/useCases/archiveReporting.js +++ b/frontend/src/features/Reporting/useCases/archiveReporting.js @@ -2,8 +2,8 @@ import { batch } from 'react-redux' import { archiveReportingFromAPI } from '../../../api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { removeVesselReporting } from '../../../domain/shared_slices/Vessel' +import { removeError, setError } from '../../MainWindow/slice' import { removeReportingsIdsFromCurrentReportings, setCurrentAndArchivedReportingsOfSelectedVessel } from '../slice' const archiveReporting = id => (dispatch, getState) => { diff --git a/frontend/src/features/Reporting/useCases/archiveReportings.js b/frontend/src/features/Reporting/useCases/archiveReportings.js index 8ce2507578..95eda7b09d 100644 --- a/frontend/src/features/Reporting/useCases/archiveReportings.js +++ b/frontend/src/features/Reporting/useCases/archiveReportings.js @@ -1,7 +1,7 @@ import { archiveReportingsFromAPI } from '../../../api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { removeVesselReportings } from '../../../domain/shared_slices/Vessel' +import { removeError, setError } from '../../MainWindow/slice' import { removeReportingsIdsFromCurrentReportings, setCurrentAndArchivedReportingsOfSelectedVessel } from '../slice' /** diff --git a/frontend/src/features/Reporting/useCases/deleteReporting.ts b/frontend/src/features/Reporting/useCases/deleteReporting.ts index 7e645337b7..5489f99d7d 100644 --- a/frontend/src/features/Reporting/useCases/deleteReporting.ts +++ b/frontend/src/features/Reporting/useCases/deleteReporting.ts @@ -1,17 +1,27 @@ +import { logSoftError } from '@mtes-mct/monitor-ui' import { batch } from 'react-redux' import { deleteReportingFromAPI } from '../../../api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { removeVesselReporting } from '../../../domain/shared_slices/Vessel' +import { removeError, setError } from '../../MainWindow/slice' import { removeReportingsIdsFromCurrentReportings, setCurrentAndArchivedReportingsOfSelectedVessel } from '../slice' import type { MainAppThunk } from '../../../store' export const deleteReporting = - (id: number): MainAppThunk => + (id: number): MainAppThunk => (dispatch, getState) => { const { selectedVesselIdentity } = getState().vessel + // TODO Can this case happen? Is it the right way to handle it? + if (!selectedVesselIdentity) { + logSoftError({ + message: '`selectedVesselIdentity` is null.', + userMessage: 'Aucun navire sélectionné pour supprimer un signalement.' + }) + + return + } const { currentAndArchivedReportingsOfSelectedVessel } = getState().reporting const deletedReporting = currentAndArchivedReportingsOfSelectedVessel?.current.find( diff --git a/frontend/src/features/Reporting/useCases/deleteReportings.js b/frontend/src/features/Reporting/useCases/deleteReportings.js index 2da3d06453..f8d997c943 100644 --- a/frontend/src/features/Reporting/useCases/deleteReportings.js +++ b/frontend/src/features/Reporting/useCases/deleteReportings.js @@ -1,7 +1,7 @@ import { deleteReportingsFromAPI } from '../../../api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { removeVesselReportings } from '../../../domain/shared_slices/Vessel' +import { removeError, setError } from '../../MainWindow/slice' import { removeReportingsIdsFromCurrentReportings, setCurrentAndArchivedReportingsOfSelectedVessel } from '../slice' /** diff --git a/frontend/src/features/Reporting/useCases/getAllCurrentReportings.ts b/frontend/src/features/Reporting/useCases/getAllCurrentReportings.ts index 8e458e9147..e6a713d977 100644 --- a/frontend/src/features/Reporting/useCases/getAllCurrentReportings.ts +++ b/frontend/src/features/Reporting/useCases/getAllCurrentReportings.ts @@ -1,5 +1,5 @@ import { getAllCurrentReportingsFromAPI } from '../../../api/reporting' -import { removeError, setError } from '../../../domain/shared_slices/Global' +import { removeError, setError } from '../../MainWindow/slice' import { setCurrentReportings } from '../slice' export const getAllCurrentReportings = () => dispatch => diff --git a/frontend/src/features/Reporting/useCases/updateReporting.ts b/frontend/src/features/Reporting/useCases/updateReporting.ts index 6d90028ff7..3a47179ff5 100644 --- a/frontend/src/features/Reporting/useCases/updateReporting.ts +++ b/frontend/src/features/Reporting/useCases/updateReporting.ts @@ -1,9 +1,9 @@ import { updateReportingFromAPI } from '@api/reporting' import { Vessel } from '../../../domain/entities/vessel/vessel' -import { removeError, setError } from '../../../domain/shared_slices/Global' import { addVesselReporting, removeVesselReporting } from '../../../domain/shared_slices/Vessel' import { ReportingType } from '../../../domain/types/reporting' +import { removeError, setError } from '../../MainWindow/slice' import { removeCurrentReporting, setCurrentAndArchivedReportingsOfSelectedVessel, diff --git a/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/SilenceAlertMenu.tsx b/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/SilenceAlertMenu.tsx index 7fa34d6581..6f98557065 100644 --- a/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/SilenceAlertMenu.tsx +++ b/frontend/src/features/SideWindow/Alert/AlertListAndReportingList/SilenceAlertMenu.tsx @@ -34,7 +34,7 @@ export function SilenceAlertMenu({ silenceAlert }: SilenceAlertMenuProps) { const silencedAlertRef = useRef() as MutableRefObject - const clickedOutside = useClickOutsideWhenOpenedWithinRef(silencedAlertRef, showSilencedAlertForIndex, baseRef) + const clickedOutside = useClickOutsideWhenOpenedWithinRef(silencedAlertRef, showSilencedAlertForIndex > 0, baseRef) const { forceUpdate } = useForceUpdate() useEffect(() => { diff --git a/frontend/src/features/SideWindow/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx b/frontend/src/features/SideWindow/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx index d418ed1204..ff185f7299 100644 --- a/frontend/src/features/SideWindow/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx +++ b/frontend/src/features/SideWindow/BeaconMalfunctionBoard/BeaconMalfunctionDetailsFollowUp.tsx @@ -8,7 +8,6 @@ import { BeaconMalfunctionDetailsFollowUpRow } from './BeaconMalfunctionDetailsF import { BeaconMalfunctionDetailsType, getContent } from './beaconMalfunctions' import { COLORS } from '../../../constants/constants' import { UserType, VESSEL_STATUS } from '../../../domain/entities/beaconMalfunction/constants' -import { setUserType } from '../../../domain/shared_slices/Global' import saveBeaconMalfunctionCommentFromKanban from '../../../domain/use_cases/beaconMalfunction/saveBeaconMalfunctionCommentFromKanban' import { useListenForScroll } from '../../../hooks/useListenForScroll' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' @@ -16,6 +15,7 @@ import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { getDate, mergeObjects } from '../../../utils' import { pushToObjectAtIndex } from '../../../utils/pushToObjectAtIndex' import CommentsSVG from '../../icons/Commentaires.svg?react' +import { setUserType } from '../../MainWindow/slice' import type { BeaconMalfunctionFollowUpItem, @@ -26,7 +26,7 @@ import type { CSSProperties } from 'react' export function BeaconMalfunctionDetailsFollowUp({ beaconMalfunctionWithDetails, firstStatus, smallSize }) { const { actions, beaconMalfunction, comments, notifications } = beaconMalfunctionWithDetails const dispatch = useMainAppDispatch() - const userType = useMainAppSelector(state => state.global.userType) + const userType = useMainAppSelector(state => state.mainWindow.userType) const firstVesselStatus = VESSEL_STATUS.find(status => status.value === firstStatus) as BeaconMalfunctionStatusValue const [today, setToday] = useState('') const [yesterday, setYesterday] = useState('') @@ -146,7 +146,7 @@ export function BeaconMalfunctionDetailsFollowUp({ beaconMalfunctionWithDetails, } else if (textareaRef.current) { textareaRef.current.style.height = '50px' } - setTextareaHeight(parseInt(textareaRef.current?.style?.height.replace('px', '') || '', 10)) + setTextareaHeight(parseInt(textareaRef.current?.style?.height.replace('px', '') ?? '', 10)) }, [comment]) const saveComment = () => { diff --git a/frontend/src/features/SideWindow/BeaconMalfunctionBoard/index.tsx b/frontend/src/features/SideWindow/BeaconMalfunctionBoard/index.tsx index 0c193721b6..400213ebd6 100644 --- a/frontend/src/features/SideWindow/BeaconMalfunctionBoard/index.tsx +++ b/frontend/src/features/SideWindow/BeaconMalfunctionBoard/index.tsx @@ -21,12 +21,12 @@ import { StageColumn } from './StageColumn' import { VesselStatusSelect } from './VesselStatusSelect' import { COLORS } from '../../../constants/constants' import { STAGE_RECORD, VESSEL_STATUS } from '../../../domain/entities/beaconMalfunction/constants' -import { setError } from '../../../domain/shared_slices/Global' import getAllBeaconMalfunctions from '../../../domain/use_cases/beaconMalfunction/getAllBeaconMalfunctions' import updateBeaconMalfunctionFromKanban from '../../../domain/use_cases/beaconMalfunction/updateBeaconMalfunctionFromKanban' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import SearchIconSVG from '../../icons/Loupe_dark.svg?react' +import { setError } from '../../MainWindow/slice' import type { BeaconMalfunction, diff --git a/frontend/src/features/Vessel/components/VesselLoader.tsx b/frontend/src/features/Vessel/components/VesselLoader.tsx index 6d303e2cf9..941896f5d2 100644 --- a/frontend/src/features/Vessel/components/VesselLoader.tsx +++ b/frontend/src/features/Vessel/components/VesselLoader.tsx @@ -5,12 +5,12 @@ import styled from 'styled-components' import { FIVE_MINUTES, TWENTY_MINUTES } from '../../../api/APIWorker' import { COLORS } from '../../../constants/constants' -import { setError } from '../../../domain/shared_slices/Global' import { useIsInLightMode } from '../../../hooks/authorization/useIsInLightMode' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { MapComponent } from '../../commonStyles/MapComponent' import VesselSVG from '../../icons/Icone_navire.svg?react' +import { setError } from '../../MainWindow/slice' import { useGetVesselsLastPositionsApi } from '../hooks/useGetVesselsLastPositionsApi' import { showVesselsLastPosition } from '../useCases/showVesselsLastPosition' @@ -19,7 +19,7 @@ export function VesselLoader() { const isInLightMode = useIsInLightMode() const dispatch = useMainAppDispatch() - const blockVesselsUpdate = useMainAppSelector(state => state.global.blockVesselsUpdate) + const blockVesselsUpdate = useMainAppSelector(state => state.mainWindow.blockVesselsUpdate) const loadingPositions = useMainAppSelector(state => state.vessel.loadingPositions) const vesselSidebarIsOpen = useMainAppSelector(state => state.vessel.vesselSidebarIsOpen) diff --git a/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts b/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts index e3508314c8..245118b8c0 100644 --- a/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts +++ b/frontend/src/features/Vessel/useCases/applyFilterToVessels.ts @@ -1,7 +1,7 @@ -import { setError } from '../../../domain/shared_slices/Global' import { setAllVesselsAsUnfiltered, setFilteredVesselsFeatures } from '../../../domain/shared_slices/Vessel' import { getFilteredVessels } from '../../../domain/use_cases/vessel/getFilteredVessels' import NoVesselsInFilterError from '../../../errors/NoVesselsInFilterError' +import { setError } from '../../MainWindow/slice' export const applyFilterToVessels = () => (dispatch, getState) => { const showedFilter = getState().filter?.filters?.find(filter => filter.showed) diff --git a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts index 179725f35a..429c5bafd5 100644 --- a/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts +++ b/frontend/src/features/Vessel/useCases/showVesselsLastPosition.ts @@ -1,7 +1,7 @@ import { applyFilterToVessels } from './applyFilterToVessels' -import { resetIsUpdatingVessels } from '../../../domain/shared_slices/Global' import { setVesselsFromAPI, setVesselsSpeciesAndDistricts } from '../../../domain/shared_slices/Vessel' import getUniqueSpeciesAndDistricts from '../../../domain/use_cases/species/getUniqueSpeciesAndDistricts' +import { resetIsUpdatingVessels } from '../../MainWindow/slice' import type { VesselLastPosition } from '../../../domain/entities/vessel/types' diff --git a/frontend/src/features/VesselList/index.tsx b/frontend/src/features/VesselList/index.tsx index d519d68648..18069a65e3 100644 --- a/frontend/src/features/VesselList/index.tsx +++ b/frontend/src/features/VesselList/index.tsx @@ -17,7 +17,6 @@ import { InteractionListener, InteractionType } from '../../domain/entities/map/ import { VesselLocation } from '../../domain/entities/vessel/vessel' import { setDisplayedComponents } from '../../domain/shared_slices/DisplayedComponent' import { addFilter } from '../../domain/shared_slices/Filter' -import { setBlockVesselsUpdate, setPreviewFilteredVesselsMode } from '../../domain/shared_slices/Global' import { animateToExtent } from '../../domain/shared_slices/Map' import { setPreviewFilteredVesselsFeatures } from '../../domain/shared_slices/Vessel' import { addVesselListFilterZone } from '../../domain/use_cases/vessel/addVesselListFilterZone' @@ -36,6 +35,7 @@ import { resetInteraction } from '../Draw/slice' import { useGetFleetSegmentsQuery } from '../FleetSegment/apis' import VesselListSVG from '../icons/Icone_liste_navires.svg?react' import PreviewSVG from '../icons/Oeil_apercu_carte.svg?react' +import { setBlockVesselsUpdate, setPreviewFilteredVesselsMode } from '../MainWindow/slice' import { setProcessingRegulationSearchedZoneExtent } from '../Regulation/slice' import type { VesselEnhancedLastPositionWebGLObject } from '../../domain/entities/vessel/types' @@ -64,7 +64,7 @@ type ZoneGroupAndChildren = { export function VesselList({ namespace }) { const dispatch = useMainAppDispatch() - const { previewFilteredVesselsMode, rightMenuIsOpen } = useMainAppSelector(state => state.global) + const { previewFilteredVesselsMode, rightMenuIsOpen } = useMainAppSelector(state => state.mainWindow) const isVesselListModalDisplayed = useMainAppSelector(state => state.displayedComponent.isVesselListModalDisplayed) const { drawedGeometry } = useListenForDrawedGeometry(InteractionListener.VESSELS_LIST) const { @@ -398,7 +398,7 @@ export function VesselList({ namespace }) { setDistrictsFiltered }} fleetSegments={{ - fleetSegments: getFleetSegmentsQuery.data || [], + fleetSegments: getFleetSegmentsQuery.data ?? [], fleetSegmentsFiltered, setFleetSegmentsFiltered }} diff --git a/frontend/src/features/VesselSearch/VesselSearchResult.tsx b/frontend/src/features/VesselSearch/VesselSearchResult.tsx index 21c2fbdbb7..24ec3e3269 100644 --- a/frontend/src/features/VesselSearch/VesselSearchResult.tsx +++ b/frontend/src/features/VesselSearch/VesselSearchResult.tsx @@ -7,7 +7,7 @@ import { getVesselCompositeIdentifier } from '../../domain/entities/vessel/vesse import { useMainAppSelector } from '../../hooks/useMainAppSelector' export function VesselSearchResult({ foundVessels, searchQuery, selectVessel, showLastSearchedVessels }) { - const lastSearchedVessels = useMainAppSelector(state => state.global.lastSearchedVessels) + const lastSearchedVessels = useMainAppSelector(state => state.mainWindow.lastSearchedVessels) const baseUrl = useMemo(() => window.location.origin, []) return ( diff --git a/frontend/src/features/VesselSearch/index.tsx b/frontend/src/features/VesselSearch/index.tsx index 3ec8850349..3e86c4e765 100644 --- a/frontend/src/features/VesselSearch/index.tsx +++ b/frontend/src/features/VesselSearch/index.tsx @@ -20,6 +20,7 @@ import type { ChangeEvent, InputHTMLAttributes, MutableRefObject } from 'react' import type { Promisable } from 'type-fest' type VesselSearchProps = Omit, 'defaultValue' | 'onChange'> & { + // TODO Should be `MutableRefObject | undefined`. baseRef?: MutableRefObject | undefined defaultValue?: VesselIdentity | undefined extendedWidth: number @@ -47,7 +48,7 @@ export function VesselSearch({ ...inputNativeProps }: VesselSearchProps) { const searchQueryRef = useRef('') - const wrapperRef = useRef(null) + const wrapperRef = useRef(null) const [selectedVessel, setSelectedVessel] = useState(undefined) const [foundVessels, setFoundVessels] = useState([]) @@ -159,7 +160,7 @@ export function VesselSearch({ // TODO Replace with existing hooks. useEffect(() => { - if (clickedOutsideComponent || escapeFromKeyboard) { + if (!!clickedOutsideComponent || !!escapeFromKeyboard) { setShowLastSearchedVessels(false) if (onClickOutsideOrEscape) { diff --git a/frontend/src/features/VesselSidebar/Body.tsx b/frontend/src/features/VesselSidebar/Body.tsx index 3918eef50a..c4ce99ae27 100644 --- a/frontend/src/features/VesselSidebar/Body.tsx +++ b/frontend/src/features/VesselSidebar/Body.tsx @@ -19,7 +19,7 @@ import { VesselReportings } from '../Reporting/components/VesselReportings' export function Body() { const isSuperUser = useIsSuperUser() const dispatch = useMainAppDispatch() - const healthcheckTextWarning = useMainAppSelector(state => state.global.healthcheckTextWarning) + const healthcheckTextWarning = useMainAppSelector(state => state.mainWindow.healthcheckTextWarning) const vesselSidebarError = useMainAppSelector(state => state.displayedError.vesselSidebarError) const selectedVessel = useMainAppSelector(state => state.vessel.selectedVessel) const vesselSidebarTab = useMainAppSelector(state => state.vessel.vesselSidebarTab) diff --git a/frontend/src/features/VesselSidebar/VesselSidebarHeader/VesselName.tsx b/frontend/src/features/VesselSidebar/VesselSidebarHeader/VesselName.tsx index 9baee91266..fde10687d6 100644 --- a/frontend/src/features/VesselSidebar/VesselSidebarHeader/VesselName.tsx +++ b/frontend/src/features/VesselSidebar/VesselSidebarHeader/VesselName.tsx @@ -1,3 +1,4 @@ +import { logSoftError } from '@mtes-mct/monitor-ui' import countries from 'i18n-iso-countries' import { useCallback, useMemo } from 'react' import styled from 'styled-components' @@ -29,6 +30,15 @@ export function VesselName({ focusOnVesselSearchInput }) { const addOrRemoveToFavorites = useCallback( e => { e.stopPropagation() + // TODO Can this case happen? Is it the right way to handle it? + if (!selectedVesselIdentity) { + logSoftError({ + message: '`selectedVesselIdentity` is null.', + userMessage: 'Aucun navire sélectionné à ajouter ou supprimer des favoris.' + }) + + return + } if (isFavorite) { dispatch(removeVesselFromFavorites(getVesselCompositeIdentifier(selectedVesselIdentity))) @@ -66,7 +76,7 @@ export function VesselName({ focusOnVesselSearchInput }) { data-cy="sidebar-add-vessel-to-favorites" onClick={addOrRemoveToFavorites} /> - {getVesselName(selectedVesselIdentity)} + {getVesselName(selectedVesselIdentity)}
) diff --git a/frontend/src/features/VesselSidebar/VesselSidebarHeader/index.tsx b/frontend/src/features/VesselSidebar/VesselSidebarHeader/index.tsx index 35cf40dce8..8ff140bb0a 100644 --- a/frontend/src/features/VesselSidebar/VesselSidebarHeader/index.tsx +++ b/frontend/src/features/VesselSidebar/VesselSidebarHeader/index.tsx @@ -3,7 +3,6 @@ import styled from 'styled-components' import { VesselName } from './VesselName' import { vesselsAreEquals } from '../../../domain/entities/vessel/vessel' -import { expandRightMenu } from '../../../domain/shared_slices/Global' import { setIsFocusedOnVesselSearch } from '../../../domain/shared_slices/Vessel' import { showVessel } from '../../../domain/use_cases/vessel/showVessel' import { useMainAppDispatch } from '../../../hooks/useMainAppDispatch' @@ -11,6 +10,7 @@ import { useMainAppSelector } from '../../../hooks/useMainAppSelector' import { MapComponent } from '../../commonStyles/MapComponent' import SearchIconSVG from '../../icons/Loupe.svg?react' import { MapButton } from '../../MainWindow/components/MapButtons/MapButton' +import { expandRightMenu } from '../../MainWindow/slice' import { VesselSearch } from '../../VesselSearch' import type { VesselIdentity } from '../../../domain/entities/vessel/types' @@ -22,8 +22,8 @@ export function VesselSidebarHeader() { state => state.vessel ) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isVesselNameShown = !isFocusedOnVesselSearch && selectedVesselIdentity const isRightMenuShrinked = vesselSidebarIsOpen && !rightMenuIsOpen @@ -86,7 +86,7 @@ const VesselNameOrInput = styled(MapComponent)<{ }>` position: absolute; display: inline-block; - top: 10px; + top: 12px; right: ${p => (p.isRightMenuShrinked ? 10 : 55)}px; z-index: 1000; color: ${p => p.theme.color.gainsboro}; @@ -113,7 +113,7 @@ const SearchButton = styled(MapButton)<{ width: 40px; height: 40px; right: 10px; - top: 10px; + top: 12px; z-index: 99; cursor: pointer; border-radius: 2px; diff --git a/frontend/src/features/VesselSidebar/actions/TrackRequest/index.tsx b/frontend/src/features/VesselSidebar/actions/TrackRequest/index.tsx index 390a58b121..fc4b7ae1a5 100644 --- a/frontend/src/features/VesselSidebar/actions/TrackRequest/index.tsx +++ b/frontend/src/features/VesselSidebar/actions/TrackRequest/index.tsx @@ -22,7 +22,7 @@ type TrackRequestProps = { } export function TrackRequest({ isSidebarOpen }: TrackRequestProps) { const dispatch = useMainAppDispatch() - const { rightMenuIsOpen } = useMainAppSelector(state => state.global) + const { rightMenuIsOpen } = useMainAppSelector(state => state.mainWindow) const { defaultVesselTrackDepth } = useMainAppSelector(state => state.map) const { selectedVesselTrackRequest } = useMainAppSelector(state => state.vessel) const { selectedVesselIdentity } = useMainAppSelector(state => state.vessel) diff --git a/frontend/src/features/VesselSidebar/actions/animate_to_track/index.tsx b/frontend/src/features/VesselSidebar/actions/animate_to_track/index.tsx index f6e65fee36..72dead9843 100644 --- a/frontend/src/features/VesselSidebar/actions/animate_to_track/index.tsx +++ b/frontend/src/features/VesselSidebar/actions/animate_to_track/index.tsx @@ -7,7 +7,7 @@ import ShowTrackSVG from '../../../icons/Bouton_afficher_toute_la_piste.svg?reac import { VesselSidebarActionButton } from '../VesselSidebarActionButton' export function AnimateToTrack({ isSidebarOpen }) { - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const selectedVesselPositions = useMainAppSelector(state => state.vessel.selectedVesselPositions) const dispatch = useMainAppDispatch() diff --git a/frontend/src/features/VesselSidebar/actions/hide_non_selected_vessels/index.tsx b/frontend/src/features/VesselSidebar/actions/hide_non_selected_vessels/index.tsx index 3497614d5f..84e5721b7a 100644 --- a/frontend/src/features/VesselSidebar/actions/hide_non_selected_vessels/index.tsx +++ b/frontend/src/features/VesselSidebar/actions/hide_non_selected_vessels/index.tsx @@ -10,7 +10,7 @@ import { VesselSidebarActionButton } from '../VesselSidebarActionButton' export function HideNonSelectedVessels({ isSidebarOpen }) { const dispatch = useMainAppDispatch() - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const hideNonSelectedVessels = useMainAppSelector(state => state.vessel.hideNonSelectedVessels) const selectedVesselPositions = useMainAppSelector(state => state.vessel.selectedVesselPositions) diff --git a/frontend/src/features/VesselSidebar/actions/show_fishing_activities/index.tsx b/frontend/src/features/VesselSidebar/actions/show_fishing_activities/index.tsx index 0107d1dd66..41c4155079 100644 --- a/frontend/src/features/VesselSidebar/actions/show_fishing_activities/index.tsx +++ b/frontend/src/features/VesselSidebar/actions/show_fishing_activities/index.tsx @@ -11,7 +11,7 @@ import { VesselSidebarActionButton } from '../VesselSidebarActionButton' export function ShowFishingActivitiesOnMap({ isSidebarOpen }) { const dispatch = useMainAppDispatch() - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const { selectedVesselIdentity, selectedVesselPositions } = useMainAppSelector(state => state.vessel) const { areFishingActivitiesShowedOnMap, fishingActivities, fishingActivitiesShowedOnMap } = useMainAppSelector( state => state.fishingActivities diff --git a/frontend/src/features/VesselSidebar/index.tsx b/frontend/src/features/VesselSidebar/index.tsx index 550a0dc25d..55f20aba86 100644 --- a/frontend/src/features/VesselSidebar/index.tsx +++ b/frontend/src/features/VesselSidebar/index.tsx @@ -11,7 +11,7 @@ import { useMainAppSelector } from '../../hooks/useMainAppSelector' import { MapComponent } from '../commonStyles/MapComponent' export function VesselSidebar() { - const rightMenuIsOpen = useMainAppSelector(state => state.global.rightMenuIsOpen) + const rightMenuIsOpen = useMainAppSelector(state => state.mainWindow.rightMenuIsOpen) const isFocusedOnVesselSearch = useMainAppSelector(state => state.vessel.isFocusedOnVesselSearch) const [isFirstLoad, setIsFirstLoad] = useState(false) diff --git a/frontend/src/features/commonComponents/ErrorToastNotification.tsx b/frontend/src/features/commonComponents/ErrorToastNotification.tsx index 492fb1422e..714ea7087d 100644 --- a/frontend/src/features/commonComponents/ErrorToastNotification.tsx +++ b/frontend/src/features/commonComponents/ErrorToastNotification.tsx @@ -14,7 +14,7 @@ export function ErrorToastNotification() { | (Error & { type: ErrorType }) - | null = useMainAppSelector(state => state.global.error) + | null = useMainAppSelector(state => state.mainWindow.error) useEffect(() => { if (!error || (error.type && error.type === ErrorType.INFO_AND_HIDDEN)) { diff --git a/frontend/src/features/commonStyles/MapComponent.tsx b/frontend/src/features/commonStyles/MapComponent.tsx index 97fab34272..69c44f992f 100644 --- a/frontend/src/features/commonStyles/MapComponent.tsx +++ b/frontend/src/features/commonStyles/MapComponent.tsx @@ -1,7 +1,5 @@ import styled from 'styled-components' -import { useMainAppSelector } from '../../hooks/useMainAppSelector' - import type { ReactNode } from 'react' type MapComponentStyleType = { @@ -10,12 +8,9 @@ type MapComponentStyleType = { isHidden?: boolean | undefined } export function MapComponent({ children, className, isHidden, ...props }: MapComponentStyleType) { - const healthcheckTextWarning = useMainAppSelector(state => state.global.healthcheckTextWarning) - return ( ` - margin-top: ${p => (p.hasHealthcheckTextWarning ? 50 : 0)}px; visibility: ${p => (p.isHidden ? 'hidden' : 'visible')}; ` diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index a923ac1ee1..056d587e73 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -43,17 +43,16 @@ export function BaseMap({ showAttributions, showCoordinates }: BaseMapProps) { - const { animateToRegulatoryLayer } = useMainAppSelector(state => state.map) + const isAnimating = useRef(false) + const isInitRenderDone = useRef(false) + const mapElement = useRef(null) - const { healthcheckTextWarning, previewFilteredVesselsMode } = useMainAppSelector(state => state.global) const dispatch = useMainAppDispatch() + const animateToRegulatoryLayer = useMainAppSelector(state => state.map.animateToRegulatoryLayer) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) - const isAnimating = useRef(false) - const isInitRenderDone = useRef(false) const [cursorCoordinates, setCursorCoordinates] = useState(undefined) - const mapElement = useRef() - const handleMapClick = useCallback( (event, _map: OpenLayerMap) => { if (!event || !_map) { @@ -151,7 +150,7 @@ export function BaseMap({ ) useEffect(() => { - monitorfishMap.setTarget(mapElement.current) + monitorfishMap.setTarget(mapElement.current ?? undefined) monitorfishMap.on('click', event => handleMapClick(event, monitorfishMap)) monitorfishMap.on('pointermove', event => throttleAndHandlePointerMove(event, monitorfishMap)) @@ -223,12 +222,7 @@ export function BaseMap({ return ( - + {showCoordinates && } {showAttributions && } {Children.map(children, child => child)} @@ -238,16 +232,15 @@ export function BaseMap({ const MapWrapper = styled.div` display: flex; - flex: 1; + height: 100%; ` const MapContainer = styled.div< { - hasHealthcheckTextWarning: boolean isPreviewFilteredVesselsMode: boolean } & HTMLProps >` - height: ${p => (p.hasHealthcheckTextWarning || p.isPreviewFilteredVesselsMode ? 'calc(100vh - 50px)' : '100vh')}; + height: ${p => (p.isPreviewFilteredVesselsMode ? 'calc(100vh - 50px)' : '100%')}; width: 100%; overflow-y: hidden; overflow-x: hidden; diff --git a/frontend/src/features/map/layers/InterestPointLayer.jsx b/frontend/src/features/map/layers/InterestPointLayer.jsx index ffa0c052e9..43e3ed0ea6 100644 --- a/frontend/src/features/map/layers/InterestPointLayer.jsx +++ b/frontend/src/features/map/layers/InterestPointLayer.jsx @@ -1,14 +1,23 @@ import { usePrevious } from '@mtes-mct/monitor-ui' -import { useEffect, useRef, useState } from 'react' - -import { useDispatch, useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' -import { MapBox, OPENLAYERS_PROJECTION } from '../../../domain/entities/map/constants' +import GeoJSON from 'ol/format/GeoJSON' +import LineString from 'ol/geom/LineString' import Draw from 'ol/interaction/Draw' import VectorLayer from 'ol/layer/Vector' -import { getInterestPointStyle, POIStyle } from './styles/interestPoint.style' +import VectorSource from 'ol/source/Vector' +import { getLength } from 'ol/sphere' +import { useEffect, useRef, useState } from 'react' +import { useDispatch, useSelector } from 'react-redux' import { v4 as uuidv4 } from 'uuid' -import { InterestPointOverlay } from '../overlays/InterestPointOverlay' + +import { getInterestPointStyle, POIStyle } from './styles/interestPoint.style' +import { InterestPointLine } from '../../../domain/entities/interestPointLine' +import { + coordinatesAreModified, + coordinatesOrTypeAreModified, + InterestPointType +} from '../../../domain/entities/interestPoints' +import { LayerProperties } from '../../../domain/entities/layers/constants' +import { MapBox, OPENLAYERS_PROJECTION } from '../../../domain/entities/map/constants' import { deleteInterestPointBeingDrawed, editInterestPoint, @@ -18,19 +27,10 @@ import { updateInterestPointBeingDrawed, updateInterestPointKeyBeingDrawed } from '../../../domain/shared_slices/InterestPoint' -import { - coordinatesAreModified, - coordinatesOrTypeAreModified, - InterestPointType -} from '../../../domain/entities/interestPoints' import saveInterestPointFeature from '../../../domain/use_cases/interestPoint/saveInterestPointFeature' -import GeoJSON from 'ol/format/GeoJSON' -import LineString from 'ol/geom/LineString' -import { InterestPointLine } from '../../../domain/entities/interestPointLine' -import { getLength } from 'ol/sphere' -import { LayerProperties } from '../../../domain/entities/layers/constants' -import { setRightMapBoxOpened } from '../../../domain/shared_slices/Global' +import { setRightMapBoxOpened } from '../../MainWindow/slice' import { monitorfishMap } from '../monitorfishMap' +import { InterestPointOverlay } from '../overlays/InterestPointOverlay' const DRAW_START_EVENT = 'drawstart' const DRAW_ABORT_EVENT = 'drawabort' @@ -38,44 +38,46 @@ const DRAW_END_EVENT = 'drawend' export const MIN_ZOOM = 7 -const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { +function InterestPointLayer({ mapMovingAndZoomEvent }) { const dispatch = useDispatch() const { - isDrawing, - isEditing, - /** @type {InterestPoint | null} interestPointBeingDrawed */ interestPointBeingDrawed, - /** @type {InterestPoint[]} interestPoints */ interestPoints, + /** @type {InterestPoint | null} interestPointBeingDrawed */ + isDrawing, + /** @type {InterestPoint[]} interestPoints */ + isEditing, triggerInterestPointFeatureDeletion } = useSelector(state => state.interestPoint) const [drawObject, setDrawObject] = useState(null) const vectorSourceRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ - wrapX: false, - projection: OPENLAYERS_PROJECTION + projection: OPENLAYERS_PROJECTION, + wrapX: false }) } + return vectorSourceRef.current } const layerRef = useRef(null) - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new VectorLayer({ - source: getVectorSource(), renderBuffer: 7, + source: getVectorSource(), + style: (feature, resolution) => getInterestPointStyle(feature, resolution), updateWhileAnimating: true, updateWhileInteracting: true, - style: (feature, resolution) => getInterestPointStyle(feature, resolution), zIndex: LayerProperties.INTEREST_POINT.zIndex }) } + return layerRef.current } @@ -84,7 +86,7 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { const previousInterestPointBeingDrawed = usePrevious(interestPointBeingDrawed) useEffect(() => { - function addLayerToMap () { + function addLayerToMap() { monitorfishMap.getLayers().push(getLayer()) return () => { @@ -96,22 +98,24 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, []) useEffect(() => { - function drawExistingFeaturesOnMap () { + function drawExistingFeaturesOnMap() { if (interestPoints) { - const features = interestPoints.map(interestPoint => { - if (interestPoint.feature) { - const nextFeature = new GeoJSON({ - featureProjection: OPENLAYERS_PROJECTION - }).readFeature(interestPoint.feature) + const features = interestPoints + .map(interestPoint => { + if (interestPoint.feature) { + const nextFeature = new GeoJSON({ + featureProjection: OPENLAYERS_PROJECTION + }).readFeature(interestPoint.feature) - const { feature, ...interestPointWithoutFeature } = interestPoint - nextFeature.setProperties(interestPointWithoutFeature) + const { feature, ...interestPointWithoutFeature } = interestPoint + nextFeature.setProperties(interestPointWithoutFeature) - return nextFeature - } + return nextFeature + } - return null - }).filter(feature => feature) + return null + }) + .filter(feature => feature) getVectorSource().addFeatures(features) } @@ -122,21 +126,23 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { useEffect(() => { if (isDrawing) { - function addEmptyNextInterestPoint () { - dispatch(updateInterestPointBeingDrawed({ - uuid: uuidv4(), - name: null, - type: InterestPointType.FISHING_VESSEL, - coordinates: null, - observations: null - })) + function addEmptyNextInterestPoint() { + dispatch( + updateInterestPointBeingDrawed({ + coordinates: null, + name: null, + observations: null, + type: InterestPointType.FISHING_VESSEL, + uuid: uuidv4() + }) + ) } - function drawNewFeatureOnMap () { + function drawNewFeatureOnMap() { const draw = new Draw({ source: getVectorSource(), - type: 'Point', - style: POIStyle + style: POIStyle, + type: 'Point' }) monitorfishMap.addInteraction(draw) @@ -149,11 +155,11 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, [isDrawing]) useEffect(() => { - function removeInteraction () { + function removeInteraction() { if (!isDrawing && drawObject) { setDrawObject(null) - function waitForUnwantedZoomAndQuitInteraction () { + function waitForUnwantedZoomAndQuitInteraction() { setTimeout(() => { monitorfishMap.removeInteraction(drawObject) }, 300) @@ -167,17 +173,19 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, [isDrawing]) useEffect(() => { - function handleDrawEvents () { + function handleDrawEvents() { if (drawObject) { drawObject.once(DRAW_START_EVENT, event => { - function startDrawing (event, type) { - dispatch(updateInterestPointBeingDrawed({ - uuid: interestPointBeingDrawed.uuid, - name: null, - type: type, - coordinates: event.feature.getGeometry().getLastCoordinate(), - observations: null - })) + function startDrawing(event, type) { + dispatch( + updateInterestPointBeingDrawed({ + coordinates: event.feature.getGeometry().getLastCoordinate(), + name: null, + observations: null, + type, + uuid: interestPointBeingDrawed.uuid + }) + ) } if (interestPointBeingDrawed) { @@ -202,7 +210,7 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, [drawObject, interestPointBeingDrawed]) useEffect(() => { - function showOrHideInterestPointsOverlays () { + function showOrHideInterestPointsOverlays() { const currentZoom = monitorfishMap.getView().getZoom().toFixed(2) if (currentZoom !== previousMapZoom.current) { previousMapZoom.current = currentZoom @@ -229,7 +237,7 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, [triggerInterestPointFeatureDeletion]) useEffect(() => { - function modifyFeatureWhenCoordinatesOrTypeModified () { + function modifyFeatureWhenCoordinatesOrTypeModified() { if (interestPointBeingDrawed?.coordinates?.length && interestPointBeingDrawed?.uuid) { const drawingFeatureToUpdate = getVectorSource().getFeatureById(interestPointBeingDrawed.uuid) @@ -238,13 +246,16 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { drawingFeatureToUpdate.getGeometry().setCoordinates(interestPointWithoutFeature.coordinates) drawingFeatureToUpdate.setProperties(interestPointWithoutFeature) - const nextFeature = new GeoJSON() - .writeFeatureObject(drawingFeatureToUpdate, { featureProjection: OPENLAYERS_PROJECTION }) + const nextFeature = new GeoJSON().writeFeatureObject(drawingFeatureToUpdate, { + featureProjection: OPENLAYERS_PROJECTION + }) - dispatch(updateInterestPointKeyBeingDrawed({ - key: 'feature', - value: nextFeature - })) + dispatch( + updateInterestPointKeyBeingDrawed({ + key: 'feature', + value: nextFeature + }) + ) } } } @@ -253,9 +264,16 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { }, [interestPointBeingDrawed?.coordinates, interestPointBeingDrawed?.type]) useEffect(() => { - function initLineWhenInterestPointCoordinatesModified () { - if (interestPointBeingDrawed && previousInterestPointBeingDrawed && coordinatesAreModified(interestPointBeingDrawed, previousInterestPointBeingDrawed)) { - const line = new LineString([interestPointBeingDrawed.coordinates, previousInterestPointBeingDrawed.coordinates]) + function initLineWhenInterestPointCoordinatesModified() { + if ( + interestPointBeingDrawed && + previousInterestPointBeingDrawed && + coordinatesAreModified(interestPointBeingDrawed, previousInterestPointBeingDrawed) + ) { + const line = new LineString([ + interestPointBeingDrawed.coordinates, + previousInterestPointBeingDrawed.coordinates + ]) const distance = getLength(line, { projection: OPENLAYERS_PROJECTION }) if (distance > 10) { @@ -264,7 +282,9 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { interestPointToCoordinates.delete(featureId) const feature = getVectorSource().getFeatureById(featureId) if (feature) { - feature.setGeometry(new LineString([interestPointBeingDrawed.coordinates, interestPointBeingDrawed.coordinates])) + feature.setGeometry( + new LineString([interestPointBeingDrawed.coordinates, interestPointBeingDrawed.coordinates]) + ) } } } @@ -274,7 +294,7 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { initLineWhenInterestPointCoordinatesModified() }, [interestPointBeingDrawed]) - function deleteInterestPoint (uuid) { + function deleteInterestPoint(uuid) { const feature = getVectorSource().getFeatureById(uuid) if (feature) { getVectorSource().removeFeature(feature) @@ -290,7 +310,7 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { dispatch(removeInterestPoint(uuid)) } - function moveInterestPointLine (uuid, coordinates, nextCoordinates, offset) { + function moveInterestPointLine(uuid, coordinates, nextCoordinates, offset) { const featureId = InterestPointLine.getFeatureId(uuid) if (interestPointToCoordinates.has(featureId)) { @@ -299,14 +319,13 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { if (existingLabelLineFeature) { if (interestPointFeature) { - existingLabelLineFeature.setGeometry(new LineString([interestPointFeature.getGeometry().getCoordinates(), nextCoordinates])) + existingLabelLineFeature.setGeometry( + new LineString([interestPointFeature.getGeometry().getCoordinates(), nextCoordinates]) + ) } } } else { - const interestPointLineFeature = InterestPointLine.getFeature( - coordinates, - nextCoordinates, - featureId) + const interestPointLineFeature = InterestPointLine.getFeature(coordinates, nextCoordinates, featureId) getVectorSource().addFeature(interestPointLineFeature) } @@ -316,12 +335,12 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { setInterestPointToCoordinates(nextVesselToCoordinates) } - function modifyInterestPoint (uuid) { + function modifyInterestPoint(uuid) { dispatch(editInterestPoint(uuid)) dispatch(setRightMapBoxOpened(MapBox.INTEREST_POINT)) } - function deleteInterestPointBeingDrawedAndCloseTool () { + function deleteInterestPointBeingDrawedAndCloseTool() { dispatch(endInterestPointDraw()) dispatch(setRightMapBoxOpened(undefined)) dispatch(deleteInterestPointBeingDrawed()) @@ -330,41 +349,37 @@ const InterestPointLayer = ({ mapMovingAndZoomEvent }) => { return ( <>
- { - interestPoints && Array.isArray(interestPoints) - ? interestPoints.map(interestPoint => { - return ( + - }) - : null - } - { - interestPointBeingDrawed && !isEditing - ? {}} - zoomHasChanged={previousMapZoom.current} - moveLine={moveInterestPointLine} - /> - : null - } + )) + : null} + {interestPointBeingDrawed && !isEditing ? ( + {}} + moveLine={moveInterestPointLine} + name={interestPointBeingDrawed.name} + observations={interestPointBeingDrawed.observations} + uuid={interestPointBeingDrawed.uuid} + zoomHasChanged={previousMapZoom.current} + /> + ) : null}
) diff --git a/frontend/src/features/map/layers/Vessel/VesselAlertAndBeaconMalfunctionLayer.jsx b/frontend/src/features/map/layers/Vessel/VesselAlertAndBeaconMalfunctionLayer.jsx index e2eb12fe1b..8453f0ed99 100644 --- a/frontend/src/features/map/layers/Vessel/VesselAlertAndBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/map/layers/Vessel/VesselAlertAndBeaconMalfunctionLayer.jsx @@ -1,59 +1,52 @@ -import React, { useEffect, useRef } from 'react' -import { useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../../domain/entities/layers/constants' +import VectorSource from 'ol/source/Vector' +import React, { useEffect, useRef } from 'react' +import { useSelector } from 'react-redux' import { getVesselAlertAndBeaconMalfunctionStyle } from './style' +import { LayerProperties } from '../../../../domain/entities/layers/constants' import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../../domain/entities/vessel/vessel' import { useIsSuperUser } from '../../../../hooks/authorization/useIsSuperUser' import { monitorfishMap } from '../../monitorfishMap' -const VesselAlertAndBeaconMalfunctionLayer = () => { +function VesselAlertAndBeaconMalfunctionLayer() { const isSuperUser = useIsSuperUser() - const { - vessels, - hideNonSelectedVessels, - selectedVesselIdentity, - vesselsTracksShowed - } = useSelector(state => state.vessel) + const { hideNonSelectedVessels, selectedVesselIdentity, vessels, vesselsTracksShowed } = useSelector( + state => state.vessel + ) - const { - nonFilteredVesselsAreHidden - } = useSelector(state => state.filter) + const { nonFilteredVesselsAreHidden } = useSelector(state => state.filter) - const { - previewFilteredVesselsMode - } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) - const { - hideVesselsAtPort - } = useSelector(state => state.map) + const { hideVesselsAtPort } = useSelector(state => state.map) const vectorSourceRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ features: [], wrapX: false }) } + return vectorSourceRef.current } const layerRef = useRef(null) - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new Vector({ source: getVectorSource(), - zIndex: LayerProperties.VESSEL_BEACON_MALFUNCTION.zIndex, + style: (_, resolution) => getVesselAlertAndBeaconMalfunctionStyle(resolution), updateWhileAnimating: true, updateWhileInteracting: true, - style: (_, resolution) => getVesselAlertAndBeaconMalfunctionStyle(resolution) + zIndex: LayerProperties.VESSEL_BEACON_MALFUNCTION.zIndex }) } + return layerRef.current } @@ -71,17 +64,34 @@ const VesselAlertAndBeaconMalfunctionLayer = () => { useEffect(() => { if (isSuperUser && vessels?.length) { const features = vessels.reduce((_features, vessel) => { - if (!vessel.hasBeaconMalfunction) return _features - if (!vessel.vesselProperties.hasAlert) return _features - if (nonFilteredVesselsAreHidden && !vessel.isFiltered) return _features - if (previewFilteredVesselsMode && !vessel.filterPreview) return _features - if (hideVesselsAtPort && vessel.isAtPort) return _features - if (hideNonSelectedVessels && !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity)) return _features + if (!vessel.hasBeaconMalfunction) { + return _features + } + if (!vessel.vesselProperties.hasAlert) { + return _features + } + if (nonFilteredVesselsAreHidden && !vessel.isFiltered) { + return _features + } + if (previewFilteredVesselsMode && !vessel.filterPreview) { + return _features + } + if (hideVesselsAtPort && vessel.isAtPort) { + return _features + } + if ( + hideNonSelectedVessels && + !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + ) { + return _features + } const feature = new Feature({ geometry: new Point(vessel.coordinates) }) - feature.setId(`${LayerProperties.VESSEL_BEACON_MALFUNCTION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}`) + feature.setId( + `${LayerProperties.VESSEL_BEACON_MALFUNCTION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}` + ) _features.push(feature) return _features diff --git a/frontend/src/features/map/layers/Vessel/VesselAlertLayer.jsx b/frontend/src/features/map/layers/Vessel/VesselAlertLayer.jsx index 884c1e4999..e374b726c4 100644 --- a/frontend/src/features/map/layers/Vessel/VesselAlertLayer.jsx +++ b/frontend/src/features/map/layers/Vessel/VesselAlertLayer.jsx @@ -1,12 +1,12 @@ -import React, { useEffect, useRef } from 'react' -import { useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../../domain/entities/layers/constants' +import VectorSource from 'ol/source/Vector' +import React, { useEffect, useRef } from 'react' +import { useSelector } from 'react-redux' import { getVesselAlertStyle } from './style' +import { LayerProperties } from '../../../../domain/entities/layers/constants' import { getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, @@ -16,52 +16,44 @@ import { import { useIsSuperUser } from '../../../../hooks/authorization/useIsSuperUser' import { monitorfishMap } from '../../monitorfishMap' -const VesselAlertLayer = () => { +function VesselAlertLayer() { const isSuperUser = useIsSuperUser() - const { - vessels, - hideNonSelectedVessels, - selectedVesselIdentity, - vesselsTracksShowed - } = useSelector(state => state.vessel) + const { hideNonSelectedVessels, selectedVesselIdentity, vessels, vesselsTracksShowed } = useSelector( + state => state.vessel + ) - const { - nonFilteredVesselsAreHidden - } = useSelector(state => state.filter) + const { nonFilteredVesselsAreHidden } = useSelector(state => state.filter) - const { - previewFilteredVesselsMode - } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) - const { - vesselsLastPositionVisibility, - hideVesselsAtPort - } = useSelector(state => state.map) + const { hideVesselsAtPort, vesselsLastPositionVisibility } = useSelector(state => state.map) const vectorSourceRef = useRef(null) const layerRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ features: [], wrapX: false }) } + return vectorSourceRef.current } - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new Vector({ source: getVectorSource(), - zIndex: LayerProperties.VESSEL_ALERT.zIndex, + style: (_, resolution) => getVesselAlertStyle(resolution), updateWhileAnimating: true, updateWhileInteracting: true, - style: (_, resolution) => getVesselAlertStyle(resolution) + zIndex: LayerProperties.VESSEL_ALERT.zIndex }) } + return layerRef.current } @@ -78,16 +70,34 @@ const VesselAlertLayer = () => { useEffect(() => { if (isSuperUser && vessels?.length) { - const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) + const { vesselIsHidden, vesselIsOpacityReduced } = + getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) const features = vessels.reduce((features, vessel) => { - if (!vessel.vesselProperties.hasAlert) return features - if (vessel.hasBeaconMalfunction) return features - if (nonFilteredVesselsAreHidden && !vessel.isFiltered) return features - if (previewFilteredVesselsMode && !vessel.filterPreview) return features - if (hideVesselsAtPort && vessel.isAtPort) return features - if (hideNonSelectedVessels && !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity)) return features - if (!Vessel.getVesselOpacity(vessel.vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced)) return features + if (!vessel.vesselProperties.hasAlert) { + return features + } + if (vessel.hasBeaconMalfunction) { + return features + } + if (nonFilteredVesselsAreHidden && !vessel.isFiltered) { + return features + } + if (previewFilteredVesselsMode && !vessel.filterPreview) { + return features + } + if (hideVesselsAtPort && vessel.isAtPort) { + return features + } + if ( + hideNonSelectedVessels && + !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + ) { + return features + } + if (!Vessel.getVesselOpacity(vessel.vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { + return features + } const feature = new Feature({ geometry: new Point(vessel.coordinates) diff --git a/frontend/src/features/map/layers/Vessel/VesselBeaconMalfunctionLayer.jsx b/frontend/src/features/map/layers/Vessel/VesselBeaconMalfunctionLayer.jsx index bb3f8a9e4e..0746cac1a5 100644 --- a/frontend/src/features/map/layers/Vessel/VesselBeaconMalfunctionLayer.jsx +++ b/frontend/src/features/map/layers/Vessel/VesselBeaconMalfunctionLayer.jsx @@ -1,60 +1,53 @@ -import React, { useEffect, useRef } from 'react' -import { useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../../domain/entities/layers/constants' +import VectorSource from 'ol/source/Vector' +import React, { useEffect, useRef } from 'react' +import { useSelector } from 'react-redux' import { getVesselBeaconMalfunctionStyle } from './style' +import { LayerProperties } from '../../../../domain/entities/layers/constants' import { getVesselCompositeIdentifier, vesselIsShowed } from '../../../../domain/entities/vessel/vessel' import { useIsSuperUser } from '../../../../hooks/authorization/useIsSuperUser' import { monitorfishMap } from '../../monitorfishMap' -const VesselBeaconMalfunctionLayer = () => { +function VesselBeaconMalfunctionLayer() { const isSuperUser = useIsSuperUser() - const { - vessels, - hideNonSelectedVessels, - vesselsTracksShowed, - selectedVesselIdentity - } = useSelector(state => state.vessel) + const { hideNonSelectedVessels, selectedVesselIdentity, vessels, vesselsTracksShowed } = useSelector( + state => state.vessel + ) - const { - nonFilteredVesselsAreHidden - } = useSelector(state => state.filter) + const { nonFilteredVesselsAreHidden } = useSelector(state => state.filter) - const { - previewFilteredVesselsMode - } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) - const { - hideVesselsAtPort - } = useSelector(state => state.map) + const { hideVesselsAtPort } = useSelector(state => state.map) const vectorSourceRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ features: [], wrapX: false }) } + return vectorSourceRef.current } const layerRef = useRef(null) - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new Vector({ source: getVectorSource(), - zIndex: LayerProperties.VESSEL_BEACON_MALFUNCTION.zIndex, + style: (_, resolution) => getVesselBeaconMalfunctionStyle(resolution), updateWhileAnimating: true, updateWhileInteracting: true, - style: (_, resolution) => getVesselBeaconMalfunctionStyle(resolution) + zIndex: LayerProperties.VESSEL_BEACON_MALFUNCTION.zIndex }) } + return layerRef.current } @@ -72,17 +65,34 @@ const VesselBeaconMalfunctionLayer = () => { useEffect(() => { if (isSuperUser && vessels?.length) { const features = vessels.reduce((_features, vessel) => { - if (!vessel.hasBeaconMalfunction) return _features - if (vessel.vesselProperties.hasAlert) return _features - if (nonFilteredVesselsAreHidden && !vessel.isFiltered) return _features - if (previewFilteredVesselsMode && !vessel.filterPreview) return _features - if (hideVesselsAtPort && vessel.isAtPort) return _features - if (hideNonSelectedVessels && !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity)) return _features + if (!vessel.hasBeaconMalfunction) { + return _features + } + if (vessel.vesselProperties.hasAlert) { + return _features + } + if (nonFilteredVesselsAreHidden && !vessel.isFiltered) { + return _features + } + if (previewFilteredVesselsMode && !vessel.filterPreview) { + return _features + } + if (hideVesselsAtPort && vessel.isAtPort) { + return _features + } + if ( + hideNonSelectedVessels && + !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + ) { + return _features + } const feature = new Feature({ geometry: new Point(vessel.coordinates) }) - feature.setId(`${LayerProperties.VESSEL_BEACON_MALFUNCTION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}`) + feature.setId( + `${LayerProperties.VESSEL_BEACON_MALFUNCTION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}` + ) _features.push(feature) return _features diff --git a/frontend/src/features/map/layers/Vessel/VesselEstimatedPositionLayer.jsx b/frontend/src/features/map/layers/Vessel/VesselEstimatedPositionLayer.jsx index 19853ab0b4..85e9e2aa99 100644 --- a/frontend/src/features/map/layers/Vessel/VesselEstimatedPositionLayer.jsx +++ b/frontend/src/features/map/layers/Vessel/VesselEstimatedPositionLayer.jsx @@ -1,65 +1,57 @@ +import { Vector } from 'ol/layer' +import VectorSource from 'ol/source/Vector' import React, { useEffect, useRef } from 'react' import { useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' -import { LayerProperties } from '../../../../domain/entities/layers/constants' + import { EstimatedPosition } from '../../../../domain/entities/estimatedPosition' +import { LayerProperties } from '../../../../domain/entities/layers/constants' import { getVesselLastPositionVisibilityDates, Vessel, vesselIsShowed } from '../../../../domain/entities/vessel/vessel' -import { Vector } from 'ol/layer' -import { getEstimatedPositionStyle } from '../styles/vesselEstimatedPosition.style' import { monitorfishMap } from '../../monitorfishMap' +import { getEstimatedPositionStyle } from '../styles/vesselEstimatedPosition.style' -const VesselEstimatedPositionLayer = () => { - const { - vessels, - hideNonSelectedVessels, - vesselsTracksShowed, - selectedVesselIdentity - } = useSelector(state => state.vessel) +function VesselEstimatedPositionLayer() { + const { hideNonSelectedVessels, selectedVesselIdentity, vessels, vesselsTracksShowed } = useSelector( + state => state.vessel + ) - const { - nonFilteredVesselsAreHidden - } = useSelector(state => state.filter) + const { nonFilteredVesselsAreHidden } = useSelector(state => state.filter) - const { - previewFilteredVesselsMode - } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) - const { - selectedBaseLayer, - showingVesselsEstimatedPositions, - vesselsLastPositionVisibility, - hideVesselsAtPort - } = useSelector(state => state.map) + const { hideVesselsAtPort, selectedBaseLayer, showingVesselsEstimatedPositions, vesselsLastPositionVisibility } = + useSelector(state => state.map) const vectorSourceRef = useRef(null) const layerRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ features: [], wrapX: false }) } + return vectorSourceRef.current } - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new Vector({ renderBuffer: 4, source: getVectorSource(), - zIndex: LayerProperties.VESSEL_ESTIMATED_POSITION.zIndex, + style: feature => getEstimatedPositionStyle(feature), updateWhileAnimating: true, updateWhileInteracting: true, - style: feature => getEstimatedPositionStyle(feature) + zIndex: LayerProperties.VESSEL_ESTIMATED_POSITION.zIndex }) } + return layerRef.current } useEffect(() => { - function addLayerToMap () { + function addLayerToMap() { getLayer().name = LayerProperties.VESSEL_ESTIMATED_POSITION.code monitorfishMap.getLayers().push(getLayer()) } @@ -77,23 +69,29 @@ const VesselEstimatedPositionLayer = () => { } if (vessels && showingVesselsEstimatedPositions) { - function createEstimatedTrackFeatures (vessel, options) { - const { - isAtPort, - isFiltered, - filterPreview - } = vessel - - if (nonFilteredVesselsAreHidden && !isFiltered) return null - if (previewFilteredVesselsMode && !filterPreview) return null - if (hideVesselsAtPort && isAtPort) return null - - options.hideNonSelectedVessels = hideNonSelectedVessels && !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + function createEstimatedTrackFeatures(vessel, options) { + const { filterPreview, isAtPort, isFiltered } = vessel + + if (nonFilteredVesselsAreHidden && !isFiltered) { + return null + } + if (previewFilteredVesselsMode && !filterPreview) { + return null + } + if (hideVesselsAtPort && isAtPort) { + return null + } + + options.hideNonSelectedVessels = + hideNonSelectedVessels && + !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + return EstimatedPosition.getFeatures(vessel, options) } const isLight = Vessel.iconIsLight(selectedBaseLayer) - const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) + const { vesselIsHidden, vesselIsOpacityReduced } = + getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) const options = { isLight, vesselIsHidden, diff --git a/frontend/src/features/map/layers/Vessel/VesselInfractionSuspicionLayer.jsx b/frontend/src/features/map/layers/Vessel/VesselInfractionSuspicionLayer.jsx index e75da2964a..21aae0b966 100644 --- a/frontend/src/features/map/layers/Vessel/VesselInfractionSuspicionLayer.jsx +++ b/frontend/src/features/map/layers/Vessel/VesselInfractionSuspicionLayer.jsx @@ -1,12 +1,12 @@ -import React, { useEffect, useRef } from 'react' -import { useSelector } from 'react-redux' -import VectorSource from 'ol/source/Vector' import Feature from 'ol/Feature' import Point from 'ol/geom/Point' import { Vector } from 'ol/layer' -import { LayerProperties } from '../../../../domain/entities/layers/constants' +import VectorSource from 'ol/source/Vector' +import React, { useEffect, useRef } from 'react' +import { useSelector } from 'react-redux' import { getVesselInfractionSuspicionStyle } from './style' +import { LayerProperties } from '../../../../domain/entities/layers/constants' import { getVesselCompositeIdentifier, getVesselLastPositionVisibilityDates, @@ -16,52 +16,44 @@ import { import { useIsSuperUser } from '../../../../hooks/authorization/useIsSuperUser' import { monitorfishMap } from '../../monitorfishMap' -const VesselInfractionSuspicionLayer = () => { +function VesselInfractionSuspicionLayer() { const isSuperUser = useIsSuperUser() - const { - vessels, - hideNonSelectedVessels, - selectedVesselIdentity, - vesselsTracksShowed - } = useSelector(state => state.vessel) + const { hideNonSelectedVessels, selectedVesselIdentity, vessels, vesselsTracksShowed } = useSelector( + state => state.vessel + ) - const { - nonFilteredVesselsAreHidden - } = useSelector(state => state.filter) + const { nonFilteredVesselsAreHidden } = useSelector(state => state.filter) - const { - previewFilteredVesselsMode - } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) - const { - vesselsLastPositionVisibility, - hideVesselsAtPort - } = useSelector(state => state.map) + const { hideVesselsAtPort, vesselsLastPositionVisibility } = useSelector(state => state.map) const vectorSourceRef = useRef(null) const layerRef = useRef(null) - function getVectorSource () { + function getVectorSource() { if (vectorSourceRef.current === null) { vectorSourceRef.current = new VectorSource({ features: [], wrapX: false }) } + return vectorSourceRef.current } - function getLayer () { + function getLayer() { if (layerRef.current === null) { layerRef.current = new Vector({ source: getVectorSource(), - zIndex: LayerProperties.VESSEL_INFRACTION_SUSPICION.zIndex, + style: (_, resolution) => getVesselInfractionSuspicionStyle(resolution), updateWhileAnimating: true, updateWhileInteracting: true, - style: (_, resolution) => getVesselInfractionSuspicionStyle(resolution) + zIndex: LayerProperties.VESSEL_INFRACTION_SUSPICION.zIndex }) } + return layerRef.current } @@ -78,20 +70,38 @@ const VesselInfractionSuspicionLayer = () => { useEffect(() => { if (isSuperUser && vessels?.length) { - const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) + const { vesselIsHidden, vesselIsOpacityReduced } = + getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) const features = vessels.reduce((features, vessel) => { - if (!vessel.vesselProperties.hasInfractionSuspicion) return features - if (nonFilteredVesselsAreHidden && !vessel.isFiltered) return features - if (previewFilteredVesselsMode && !vessel.filterPreview) return features - if (hideVesselsAtPort && vessel.isAtPort) return features - if (hideNonSelectedVessels && !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity)) return features - if (!Vessel.getVesselOpacity(vessel.vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced)) return features + if (!vessel.vesselProperties.hasInfractionSuspicion) { + return features + } + if (nonFilteredVesselsAreHidden && !vessel.isFiltered) { + return features + } + if (previewFilteredVesselsMode && !vessel.filterPreview) { + return features + } + if (hideVesselsAtPort && vessel.isAtPort) { + return features + } + if ( + hideNonSelectedVessels && + !vesselIsShowed(vessel.vesselProperties, vesselsTracksShowed, selectedVesselIdentity) + ) { + return features + } + if (!Vessel.getVesselOpacity(vessel.vesselProperties.dateTime, vesselIsHidden, vesselIsOpacityReduced)) { + return features + } const feature = new Feature({ geometry: new Point(vessel.coordinates) }) - feature.setId(`${LayerProperties.VESSEL_INFRACTION_SUSPICION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}`) + feature.setId( + `${LayerProperties.VESSEL_INFRACTION_SUSPICION.code}:${getVesselCompositeIdentifier(vessel.vesselProperties)}` + ) features.push(feature) return features diff --git a/frontend/src/features/map/layers/Vessel/VesselsLabelsLayer.tsx b/frontend/src/features/map/layers/Vessel/VesselsLabelsLayer.tsx index 84a45f36d3..c6dc9bc799 100644 --- a/frontend/src/features/map/layers/Vessel/VesselsLabelsLayer.tsx +++ b/frontend/src/features/map/layers/Vessel/VesselsLabelsLayer.tsx @@ -37,7 +37,7 @@ export function VesselsLabelsLayer({ mapMovingAndZoomEvent }) { const vessels = useMainAppSelector(state => state.vessel.vessels) const vesselsTracksShowed = useMainAppSelector(state => state.vessel.vesselsTracksShowed) const areVesselsDisplayed = useMainAppSelector(state => state.displayedComponent.areVesselsDisplayed) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const hideVesselsAtPort = useMainAppSelector(state => state.map.hideVesselsAtPort) const riskFactorShowedOnMap = useMainAppSelector(state => state.map.riskFactorShowedOnMap) diff --git a/frontend/src/features/map/layers/Vessel/VesselsLayer/index.tsx b/frontend/src/features/map/layers/Vessel/VesselsLayer/index.tsx index 8ef0c20752..95b99e03fe 100644 --- a/frontend/src/features/map/layers/Vessel/VesselsLayer/index.tsx +++ b/frontend/src/features/map/layers/Vessel/VesselsLayer/index.tsx @@ -30,7 +30,7 @@ function UnmemoizedVesselsLayer() { const selectedBaseLayer = useMainAppSelector(state => state.map.selectedBaseLayer) const vesselsLastPositionVisibility = useMainAppSelector(state => state.map.vesselsLastPositionVisibility) - const previewFilteredVesselsMode = useMainAppSelector(state => state.global.previewFilteredVesselsMode) + const previewFilteredVesselsMode = useMainAppSelector(state => state.mainWindow.previewFilteredVesselsMode) const { filterColor, filters, nonFilteredVesselsAreHidden, showedFilter } = useMainAppSelector(state => { const nextShowedFilter = state.filter?.filters?.find(filter => filter.showed) @@ -60,7 +60,7 @@ function UnmemoizedVesselsLayer() { const isLight = Vessel.iconIsLight(selectedBaseLayer) const { vesselIsHidden, vesselIsOpacityReduced } = getVesselLastPositionVisibilityDates(vesselsLastPositionVisibility) - const filterColorRGBArray = customHexToRGB(filterColor || isLight ? theme.color.lightGray : COLORS.charcoal) + const filterColorRGBArray = customHexToRGB(filterColor ?? isLight ? theme.color.lightGray : COLORS.charcoal) const initStyles = { filterColorBlue: filterColorRGBArray[2], filterColorGreen: filterColorRGBArray[1], diff --git a/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx b/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx index 0676f5884e..195a124a96 100644 --- a/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx +++ b/frontend/src/features/preview_filtered_vessels/PreviewFilteredVessels.jsx @@ -1,23 +1,24 @@ import React from 'react' -import styled from 'styled-components' import { useDispatch, useSelector } from 'react-redux' +import styled from 'styled-components' + import { COLORS } from '../../constants/constants' import BackToVesselsListSVG from '../icons/Fleche_navigation_marees_gainsboro.svg?react' -import { setPreviewFilteredVesselsMode } from '../../domain/shared_slices/Global' +import { setPreviewFilteredVesselsMode } from '../MainWindow/slice' -const PreviewFilteredVessels = () => { +function PreviewFilteredVessels() { const dispatch = useDispatch() - const { previewFilteredVesselsMode } = useSelector(state => state.global) + const { previewFilteredVesselsMode } = useSelector(state => state.mainWindow) return ( <> {previewFilteredVesselsMode ? ( { dispatch(setPreviewFilteredVesselsMode(false)) }} - data-cy={'back-to-vessels-list'} > diff --git a/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.jsx b/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.jsx deleted file mode 100644 index 820decbe34..0000000000 --- a/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.jsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useEffect, useState } from 'react' - -export const useClickOutsideWhenOpenedWithinRef = (ref, isOpened, baseRef) => { - const [clicked, setClicked] = useState(null) - - useEffect(() => { - function handleClickOutside (event) { - if (ref.current && !ref.current.contains(event.target)) { - setClicked({}) - } else { - setClicked(null) - } - } - - // Bind the event listener - if (isOpened) { - if (baseRef) { - baseRef.current?.addEventListener('mousedown', handleClickOutside) - } else { - document.addEventListener('mousedown', handleClickOutside) - } - } - return () => { - // Unbind the event listener on clean up - if (baseRef) { - baseRef.current?.removeEventListener('mousedown', handleClickOutside) - } else { - document.addEventListener('mousedown', handleClickOutside) - } - } - }, [ref, isOpened]) - - return clicked -} diff --git a/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.tsx b/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.tsx new file mode 100644 index 0000000000..dda88c8d6d --- /dev/null +++ b/frontend/src/hooks/useClickOutsideWhenOpenedWithinRef.tsx @@ -0,0 +1,48 @@ +import { useEffect, useState, type MutableRefObject } from 'react' + +export const useClickOutsideWhenOpenedWithinRef = ( + ref: MutableRefObject, + isOpened: boolean, + // TODO Should be `MutableRefObject | undefined`. + baseRef: MutableRefObject | undefined +): {} | null => { + const [clicked, setClicked] = useState<{} | null>(null) + + useEffect( + () => { + function handleClickOutside(event) { + if (ref.current && !ref.current.contains(event.target)) { + setClicked({}) + } else { + setClicked(null) + } + } + + // Bind the event listener + if (isOpened) { + if (baseRef) { + baseRef.current?.addEventListener('mousedown', handleClickOutside) + } else { + document.addEventListener('mousedown', handleClickOutside) + } + } + + return () => { + // Unbind the event listener on clean up + if (baseRef) { + // TODO Not so sure about this disabled ESLint rule, quick workaround for TS migration. + // eslint-disable-next-line react-hooks/exhaustive-deps + baseRef.current?.removeEventListener('mousedown', handleClickOutside) + } else { + document.addEventListener('mousedown', handleClickOutside) + } + } + }, + + // TODO Not so sure about this disabled ESLint rule, quick workaround for TS migration. + // eslint-disable-next-line react-hooks/exhaustive-deps + [ref, isOpened] + ) + + return clicked +} diff --git a/frontend/src/store/reducers.ts b/frontend/src/store/reducers.ts index 74ab54c156..70e1c4e5d2 100644 --- a/frontend/src/store/reducers.ts +++ b/frontend/src/store/reducers.ts @@ -11,7 +11,6 @@ import { displayedErrorReducer } from '../domain/shared_slices/DisplayedError' import { favoriteVesselReducer } from '../domain/shared_slices/FavoriteVessel' import { filterReducer } from '../domain/shared_slices/Filter' import { gearReducer } from '../domain/shared_slices/Gear' -import { globalSliceReducer } from '../domain/shared_slices/Global' import { infractionReducer } from '../domain/shared_slices/Infraction' import { interestPointReducer } from '../domain/shared_slices/InterestPoint' import layer from '../domain/shared_slices/Layer' @@ -25,6 +24,7 @@ import { controlUnitDialogReducer } from '../features/ControlUnit/components/Con import { controlUnitListDialogPersistedReducer } from '../features/ControlUnit/components/ControlUnitListDialog/slice' import { customZoneReducer, type CustomZoneState } from '../features/CustomZone/slice' import { logbookReducer } from '../features/Logbook/slice' +import { mainWindowSliceReducer } from '../features/MainWindow/slice' import { missionFormReducer } from '../features/Mission/components/MissionForm/slice' import { missionListReducer, type MissionListState } from '../features/Mission/components/MissionList/slice' import { priorNotificationReducer } from '../features/PriorNotification/slice' @@ -54,7 +54,7 @@ const commonReducerList = { [monitorfishLightApi.reducerPath]: monitorfishLightApi.reducer, gear: gearReducer, - global: globalSliceReducer, + mainWindow: mainWindowSliceReducer, map: mapReducer, regulatory: regulatoryReducer, species: speciesReducer