From 0789d51eb66b0ccef7762e917c39662a736b341b Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 01/19] wip only display searched layers when not already shown catch all objects, not only first one --- frontend/src/domain/entities/AMPs.ts | 2 + .../src/domain/entities/layers/constants.ts | 14 +-- frontend/src/domain/shared_slices/Global.ts | 3 +- frontend/src/domain/types/map.ts | 74 ++++++++++++++- .../interestPoint/saveInterestPointFeature.ts | 9 +- .../HoveredMissionToAttachLayer.tsx | 10 +-- .../Layers/MissionToAttach/index.tsx | 3 +- .../Overlays/MissionToAttach/index.tsx | 9 +- .../Station/components/StationLayer/index.ts | 5 +- .../StationOverlay/StationCard/index.tsx | 4 +- .../components/StationOverlay/index.tsx | 5 +- .../src/features/layersSelector/index.tsx | 2 +- .../metadataPanel/ampMetadata/index.tsx | 2 +- .../regulatoryMetadata/index.tsx | 2 +- .../layersSelector/metadataPanel/slice.ts | 43 +++++++-- .../layersSelector/overlays/LayersOverlay.tsx | 34 +++++++ .../overlays/OverlayContent.tsx | 61 +++++++++++++ .../layersSelector/overlays/OverlayMenu.tsx | 28 ++++++ .../overlays/OverlayPositionOnCoordinate.tsx | 63 +++++++++++++ .../layersSelector/overlays/index.tsx | 53 +++++++++++ .../features/layersSelector/overlays/utils.ts | 26 ++++++ frontend/src/features/map/BaseMap.tsx | 89 ++++++++++++------- frontend/src/features/map/ShowAMPMetadata.ts | 24 ----- .../features/map/ShowRegulatoryMetadata.ts | 24 ----- frontend/src/features/map/index.tsx | 15 ++-- .../map/layers/AMP/AMPPreviewLayer.ts | 47 ++++------ frontend/src/features/map/layers/AMP/index.ts | 51 ++++------- .../layers/Missions/HoveredMissionLayer.ts | 6 +- .../features/map/layers/Missions/index.tsx | 5 +- .../Reportings/HoveredReportingLayer.tsx | 6 +- .../features/map/layers/Reportings/index.tsx | 3 +- .../features/map/layers/Semaphores/index.ts | 5 +- .../overlays/OverlayPositionOnCentroid.tsx | 68 +++++++------- .../features/map/overlays/actions/index.tsx | 11 ++- .../features/map/overlays/missions/index.tsx | 9 +- .../map/overlays/reportings/index.tsx | 9 +- .../map/overlays/semaphores/index.tsx | 16 ++-- frontend/src/features/map/utils.ts | 41 +++++++++ .../HoveredReportingToAttachLayer.tsx | 9 +- .../Layers/ReportingToAttach/index.tsx | 3 +- .../Overlays/ReportingToAttach/index.tsx | 8 +- frontend/src/store/index.ts | 4 +- frontend/src/store/reducers.ts | 4 +- frontend/src/types.ts | 5 -- 44 files changed, 645 insertions(+), 269 deletions(-) create mode 100644 frontend/src/features/layersSelector/overlays/LayersOverlay.tsx create mode 100644 frontend/src/features/layersSelector/overlays/OverlayContent.tsx create mode 100644 frontend/src/features/layersSelector/overlays/OverlayMenu.tsx create mode 100644 frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx create mode 100644 frontend/src/features/layersSelector/overlays/index.tsx create mode 100644 frontend/src/features/layersSelector/overlays/utils.ts delete mode 100644 frontend/src/features/map/ShowAMPMetadata.ts delete mode 100644 frontend/src/features/map/ShowRegulatoryMetadata.ts create mode 100644 frontend/src/features/map/utils.ts diff --git a/frontend/src/domain/entities/AMPs.ts b/frontend/src/domain/entities/AMPs.ts index 8fcb12c6fa..55530bf6b8 100644 --- a/frontend/src/domain/entities/AMPs.ts +++ b/frontend/src/domain/entities/AMPs.ts @@ -11,3 +11,5 @@ export type AMPFromAPI = { url_legicem: string | null } export type AMP = AMPFromAPI & { bbox: Extent } + +export type AMPPRoperties = Omit diff --git a/frontend/src/domain/entities/layers/constants.ts b/frontend/src/domain/entities/layers/constants.ts index a2dbe0f833..b6f0a41318 100644 --- a/frontend/src/domain/entities/layers/constants.ts +++ b/frontend/src/domain/entities/layers/constants.ts @@ -262,11 +262,15 @@ export const SelectableLayers = [ Layers.REPORTING_TO_ATTACH_ON_MISSION.code ] export const HoverableLayers = [ - Layers.MISSIONS.code, Layers.ACTIONS.code, - Layers.STATIONS.code, - Layers.SEMAPHORES.code, - Layers.REPORTINGS.code, + Layers.AMP.code, + Layers.AMP_PREVIEW.code, Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code + Layers.MISSIONS.code, + Layers.REGULATORY_ENV_PREVIEW.code, + Layers.REGULATORY_ENV.code, + Layers.REPORTING_TO_ATTACH_ON_MISSION.code, + Layers.REPORTINGS.code, + Layers.SEMAPHORES.code, + Layers.STATIONS.code ] diff --git a/frontend/src/domain/shared_slices/Global.ts b/frontend/src/domain/shared_slices/Global.ts index 9983800434..ece8872ae5 100644 --- a/frontend/src/domain/shared_slices/Global.ts +++ b/frontend/src/domain/shared_slices/Global.ts @@ -4,6 +4,7 @@ import { createSlice, type PayloadAction } from '@reduxjs/toolkit' import type { MapToolType } from '../entities/map/constants' +import type { Extent } from 'ol/extent' export enum ReportingContext { MAP = 'map', @@ -27,7 +28,7 @@ type Toast = { } type OverlayCoordinates = { - coordinates: [number, number] + coordinates: Extent name: string } type GlobalOverlayCoordinates = { diff --git a/frontend/src/domain/types/map.ts b/frontend/src/domain/types/map.ts index f85f95dd0c..c0888719b4 100644 --- a/frontend/src/domain/types/map.ts +++ b/frontend/src/domain/types/map.ts @@ -1,11 +1,81 @@ -import type { InteractionListener, InteractionType } from '../entities/map/constants' +import Feature, { type FeatureLike } from 'ol/Feature' +import { GeoJSON } from 'ol/format' +import { OPENLAYERS_PROJECTION, type InteractionListener, type InteractionType } from '../entities/map/constants' + +import type { AMPPRoperties } from 'domain/entities/AMPs' +import type { MonitorEnvLayers } from 'domain/entities/layers/constants' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' +import type { Coordinate } from 'ol/coordinate' +import type { Geometry } from 'ol/geom' + +export type OverlayItem = { + layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV + properties: AMPPRoperties | RegulatoryLayerCompactProperties +} export type MapClickEvent = { + coordinates: Coordinate | undefined ctrlKeyPressed: boolean - feature: Object + feature: SerializedFeature> | undefined + featureList: SerializedFeature>[] | undefined } export type InteractionTypeAndListener = { listener: InteractionListener type: InteractionType } + +export type SerializedFeature

= { + geometry: Geometry + id: string | number + properties: P +} + +export const convertToSerializedFeature =

( + feature: Feature | undefined +): SerializedFeature

| undefined => { + if (!feature) { + return undefined + } + const geometry = feature.getGeometry() + if (!geometry) { + return undefined + } + + return { + geometry, + id: feature.getId() as string | number, + properties: feature.getProperties() as P + } +} + +const parser = new GeoJSON({ featureProjection: OPENLAYERS_PROJECTION }) + +export function getGeoJSONFromFeature

(feature: Feature | FeatureLike | undefined) { + if (!feature || !(feature instanceof Feature)) { + return undefined + } + + return parser.writeFeatureObject(feature) as SerializedFeature

+} + +export const getGeoJSONFromFeatureList = (features: (Feature | FeatureLike | undefined)[]) => + features.reduce((acc, feature) => { + const geoJSONFeature = getGeoJSONFromFeature(feature) + if (geoJSONFeature) { + acc.push(geoJSONFeature) + } + + return acc + }, [] as SerializedFeature[]) + +export const convertToFeature =

( + serializedFeature: SerializedFeature

| undefined +): Feature | undefined => { + if (!serializedFeature) { + return undefined + } + const feature = parser.readFeature(serializedFeature) + + return feature +} diff --git a/frontend/src/domain/use_cases/interestPoint/saveInterestPointFeature.ts b/frontend/src/domain/use_cases/interestPoint/saveInterestPointFeature.ts index 523540b444..89a0c9475a 100644 --- a/frontend/src/domain/use_cases/interestPoint/saveInterestPointFeature.ts +++ b/frontend/src/domain/use_cases/interestPoint/saveInterestPointFeature.ts @@ -1,8 +1,7 @@ +import { getGeoJSONFromFeature } from 'domain/types/map' import Feature from 'ol/Feature' -import GeoJSON from 'ol/format/GeoJSON' import Point from 'ol/geom/Point' -import { OPENLAYERS_PROJECTION } from '../../entities/map/constants' import { updateInterestPointKeyBeingDrawed } from '../../shared_slices/InterestPoint' export const saveInterestPointFeature = (feature?: Feature | undefined) => (dispatch, getState) => { @@ -31,9 +30,3 @@ export const saveInterestPointFeature = (feature?: Feature | undefined) => (disp }) ) } - -function getGeoJSONFromFeature(feature) { - const parser = new GeoJSON() - - return parser.writeFeatureObject(feature, { featureProjection: OPENLAYERS_PROJECTION }) -} diff --git a/frontend/src/features/Reportings/Layers/MissionToAttach/HoveredMissionToAttachLayer.tsx b/frontend/src/features/Reportings/Layers/MissionToAttach/HoveredMissionToAttachLayer.tsx index c71133dd0d..d1cd492fac 100644 --- a/frontend/src/features/Reportings/Layers/MissionToAttach/HoveredMissionToAttachLayer.tsx +++ b/frontend/src/features/Reportings/Layers/MissionToAttach/HoveredMissionToAttachLayer.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { type MutableRefObject, useEffect, useRef } from 'react' @@ -34,12 +35,9 @@ export function HoveredMissionToAttachLayer({ currentFeatureOver, map }: BaseMap useEffect(() => { vectorSourceRef.current?.clear(true) - - if ( - currentFeatureOver && - currentFeatureOver.getId()?.toString()?.includes(Layers.MISSION_TO_ATTACH_ON_REPORTING.code) - ) { - vectorSourceRef.current?.addFeature(currentFeatureOver) + const feature = convertToFeature(currentFeatureOver) + if (feature && feature.getId()?.toString()?.includes(Layers.MISSION_TO_ATTACH_ON_REPORTING.code)) { + vectorSourceRef.current?.addFeature(feature) } }, [currentFeatureOver]) diff --git a/frontend/src/features/Reportings/Layers/MissionToAttach/index.tsx b/frontend/src/features/Reportings/Layers/MissionToAttach/index.tsx index c17f2e8203..32ac4e0c7c 100644 --- a/frontend/src/features/Reportings/Layers/MissionToAttach/index.tsx +++ b/frontend/src/features/Reportings/Layers/MissionToAttach/index.tsx @@ -1,4 +1,5 @@ import { customDayjs } from '@mtes-mct/monitor-ui' +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { useEffect, useMemo, useRef, type MutableRefObject } from 'react' @@ -72,7 +73,7 @@ export function MissionToAttachLayer({ map, mapClickEvent }: BaseMapChildrenProp }, [isMissionAttachmentInProgress]) useEffect(() => { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) if (feature && feature.getId()?.toString()?.includes(Layers.MISSION_TO_ATTACH_ON_REPORTING.code)) { const { missionId } = feature.getProperties() dispatch(attachMission(missionId)) diff --git a/frontend/src/features/Reportings/Overlays/MissionToAttach/index.tsx b/frontend/src/features/Reportings/Overlays/MissionToAttach/index.tsx index 30d06a40e9..d54ca3582d 100644 --- a/frontend/src/features/Reportings/Overlays/MissionToAttach/index.tsx +++ b/frontend/src/features/Reportings/Overlays/MissionToAttach/index.tsx @@ -1,4 +1,5 @@ import { OverlayPositionOnCentroid } from '@features/map/overlays/OverlayPositionOnCentroid' +import { convertToFeature } from 'domain/types/map' import { Layers } from '../../../../domain/entities/layers/constants' import { useAppSelector } from '../../../../hooks/useAppSelector' @@ -19,20 +20,20 @@ const OPTIONS = { export function MissionToAttachOverlays({ currentFeatureOver, map }: BaseMapChildrenProps) { const displayMissionToAttachLayer = useAppSelector(state => state.global.displayMissionToAttachLayer) - - const currentfeatureId = currentFeatureOver?.getId() + const feature = convertToFeature(currentFeatureOver) + const currentfeatureId = feature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(Layers.MISSION_TO_ATTACH_ON_REPORTING.code) return ( - + ) } diff --git a/frontend/src/features/Station/components/StationLayer/index.ts b/frontend/src/features/Station/components/StationLayer/index.ts index 39ad87d539..c0a497ae54 100644 --- a/frontend/src/features/Station/components/StationLayer/index.ts +++ b/frontend/src/features/Station/components/StationLayer/index.ts @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { useEffect, useMemo, useRef } from 'react' @@ -54,12 +55,12 @@ export function StationLayer({ map, mapClickEvent }: BaseMapChildrenProps) { // Features Events useEffect(() => { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) if (!feature) { return } - const featureId = mapClickEvent?.feature?.getId()?.toString() + const featureId = feature?.getId()?.toString() if (!featureId?.startsWith(Layers.STATIONS.code)) { return } diff --git a/frontend/src/features/Station/components/StationOverlay/StationCard/index.tsx b/frontend/src/features/Station/components/StationOverlay/StationCard/index.tsx index 315540be99..181455ee0a 100644 --- a/frontend/src/features/Station/components/StationOverlay/StationCard/index.tsx +++ b/frontend/src/features/Station/components/StationOverlay/StationCard/index.tsx @@ -14,9 +14,9 @@ import { stationActions } from '../../../slice' import type { ControlUnit } from '../../../../../domain/entities/controlUnit' import type { Station } from '../../../../../domain/entities/station' -import type { Feature } from 'ol' +import type { FeatureLike } from 'ol/Feature' -export function StationCard({ feature, selected = false }: { feature: Feature; selected?: boolean }) { +export function StationCard({ feature, selected = false }: { feature: FeatureLike; selected?: boolean }) { const [controlUnits, setControlUnits] = useState([]) const dispatch = useAppDispatch() diff --git a/frontend/src/features/Station/components/StationOverlay/index.tsx b/frontend/src/features/Station/components/StationOverlay/index.tsx index 7831822177..64bec51011 100644 --- a/frontend/src/features/Station/components/StationOverlay/index.tsx +++ b/frontend/src/features/Station/components/StationOverlay/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { useMemo } from 'react' import { OVERLAY_MARGINS } from './constants' @@ -9,9 +10,9 @@ import { OverlayPositionOnCentroid } from '../../../map/overlays/OverlayPosition import type { BaseMapChildrenProps } from '../../../map/BaseMap' -export function StationOverlay({ currentFeatureOver: hoveredFeature, map }: BaseMapChildrenProps) { +export function StationOverlay({ currentFeatureOver, map }: BaseMapChildrenProps) { const selectedBaseFeatureId = useAppSelector(state => state.station.selectedFeatureId) - + const hoveredFeature = convertToFeature(currentFeatureOver) const selectedFeature = useMemo( () => findMapFeatureById(map, Layers.STATIONS.code, selectedBaseFeatureId), diff --git a/frontend/src/features/layersSelector/index.tsx b/frontend/src/features/layersSelector/index.tsx index 2ca1cd825b..090c48c393 100644 --- a/frontend/src/features/layersSelector/index.tsx +++ b/frontend/src/features/layersSelector/index.tsx @@ -18,7 +18,7 @@ import { useAppDispatch } from '../../hooks/useAppDispatch' import { useAppSelector } from '../../hooks/useAppSelector' export function LayersSidebar() { - const { metadataLayerId, metadataLayerType, metadataPanelIsOpen } = useAppSelector(state => state.metadataPanel) + const { metadataLayerId, metadataLayerType, metadataPanelIsOpen } = useAppSelector(state => state.layersMetadata) const isLayersSidebarVisible = useAppSelector(state => state.global.isLayersSidebarVisible) const displayLayersSidebar = useAppSelector(state => state.global.displayLayersSidebar) const regulatoryAreas = useGetRegulatoryLayersQuery() diff --git a/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx b/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx index b9f7bdd30a..4f0504482d 100644 --- a/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx +++ b/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx @@ -17,7 +17,7 @@ const FOUR_HOURS = 4 * 60 * 60 * 1000 export function AmpMetadata() { const dispatch = useAppDispatch() - const { metadataLayerId, metadataPanelIsOpen } = useAppSelector(state => state.metadataPanel) + const { metadataLayerId, metadataPanelIsOpen } = useAppSelector(state => state.layersMetadata) const { ampMetadata } = useGetAMPsQuery(undefined, { pollingInterval: FOUR_HOURS, diff --git a/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx b/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx index 98abcdd969..9217518d75 100644 --- a/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx +++ b/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx @@ -18,7 +18,7 @@ const FOUR_HOURS = 4 * 60 * 60 * 1000 export function RegulatoryMetadata() { const dispatch = useAppDispatch() - const { metadataLayerId, metadataPanelIsOpen } = useAppSelector(state => state.metadataPanel) + const { metadataLayerId, metadataPanelIsOpen } = useAppSelector(state => state.layersMetadata) const { currentData: regulatoryMetadata } = useGetRegulatoryLayerByIdQuery(metadataLayerId ?? skipToken, { pollingInterval: FOUR_HOURS diff --git a/frontend/src/features/layersSelector/metadataPanel/slice.ts b/frontend/src/features/layersSelector/metadataPanel/slice.ts index e898d96809..44957cd9e5 100644 --- a/frontend/src/features/layersSelector/metadataPanel/slice.ts +++ b/frontend/src/features/layersSelector/metadataPanel/slice.ts @@ -4,46 +4,73 @@ import { includes } from 'lodash' import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' import type { HomeRootState } from '@store/index' +import type { OverlayItem } from 'domain/types/map' +import type { Coordinate } from 'ol/coordinate' type MetadataPanelSliceState = { + layerOverlayCoordinates: Coordinate | undefined + layerOverlayIsOpen: boolean + layerOverlayItems: OverlayItem[] metadataLayerId: number | undefined metadataLayerType: MonitorEnvLayers.REGULATORY_ENV | MonitorEnvLayers.AMP | undefined metadataPanelIsOpen: boolean } -const metadataPanelSlice = createSlice({ +const layersMetadataSlice = createSlice({ initialState: { + layerOverlayCoordinates: undefined, + layerOverlayIsOpen: false, + layerOverlayItems: [], metadataLayerId: undefined, metadataLayerType: undefined, metadataPanelIsOpen: false } as MetadataPanelSliceState, - name: 'metadataPanel', + name: 'layersMetadata', reducers: { + closeLayerOverlay(state) { + state.layerOverlayIsOpen = false + state.layerOverlayCoordinates = undefined + state.layerOverlayItems = [] + }, closeMetadataPanel(state) { state.metadataPanelIsOpen = false state.metadataLayerId = undefined state.metadataLayerType = undefined }, - openAMPMetadataPanel(state, action) { + openAMPMetadataPanel(state, action: { payload: number }) { state.metadataPanelIsOpen = true state.metadataLayerType = MonitorEnvLayers.AMP state.metadataLayerId = action.payload }, + openLayerOverlay(state, action: { payload: Coordinate }) { + state.layerOverlayIsOpen = true + state.layerOverlayCoordinates = action.payload + }, openRegulatoryMetadataPanel(state, action) { state.metadataPanelIsOpen = true state.metadataLayerType = MonitorEnvLayers.REGULATORY_ENV state.metadataLayerId = action.payload + }, + setLayerOverlayItems(state, action) { + state.layerOverlayItems = action.payload } } }) -export const { closeMetadataPanel, openAMPMetadataPanel, openRegulatoryMetadataPanel } = metadataPanelSlice.actions +export const { + closeLayerOverlay, + closeMetadataPanel, + openAMPMetadataPanel, + openLayerOverlay, + openRegulatoryMetadataPanel, + setLayerOverlayItems +} = layersMetadataSlice.actions -export const metadataPanelSliceReducer = metadataPanelSlice.reducer +export const layersMetadataSliceReducer = layersMetadataSlice.reducer -const isMetadataPanelOpen = (state: HomeRootState) => state.metadataPanel.metadataPanelIsOpen -const getMetadataLayerType = (state: HomeRootState) => state.metadataPanel.metadataLayerType -const getMetadataLayerId = (state: HomeRootState) => state.metadataPanel.metadataLayerId +const isMetadataPanelOpen = (state: HomeRootState) => state.layersMetadata.metadataPanelIsOpen +const getMetadataLayerType = (state: HomeRootState) => state.layersMetadata.metadataLayerType +const getMetadataLayerId = (state: HomeRootState) => state.layersMetadata.metadataLayerId export const getMetadataIsOpenForRegulatoryLayerId = createSelector( [isMetadataPanelOpen, getMetadataLayerType, getMetadataLayerId, (_, layerId: number) => layerId], diff --git a/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx b/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx new file mode 100644 index 0000000000..c214a35350 --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx @@ -0,0 +1,34 @@ +import { getHoveredItems } from '@features/map/utils' +import { useAppSelector } from '@hooks/useAppSelector' +import { createPortal } from 'react-dom' + +import { OverlayContent } from './OverlayContent' +import { OverlayMenu } from './OverlayMenu' +import { OverlayPositionOnCoordinates } from './OverlayPositionOnCoordinate' + +import type { BaseMapChildrenProps } from '@features/map/BaseMap' + +export function LayersOverlay({ currentFeatureListOver, map, pixel }: BaseMapChildrenProps) { + const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector( + state => state.layersMetadata + ) + const hoveredItems = getHoveredItems(currentFeatureListOver) + + return ( + <> + + {layerOverlayIsOpen && } + + {createPortal( + !layerOverlayIsOpen && hoveredItems && hoveredItems.length > 0 && pixel && ( + + ), + document.body as HTMLElement + )} + + ) +} diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx new file mode 100644 index 0000000000..e120f4fbc9 --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -0,0 +1,61 @@ +import styled from 'styled-components' + +import { getName, getType } from './utils' +import { LayerLegend } from '../utils/LayerLegend.style' + +import type { OverlayItem } from 'domain/types/map' + +type OverlayContentProps = { + items: OverlayItem[] | undefined +} + +export function OverlayContent({ items }: OverlayContentProps) { + return ( + + {items?.slice(0, 3).map(item => { + const name = getName(item.properties, item.layerType) + const type = getType(item.properties, item.layerType) + + return ( + + + {name} + / {type} + + ) + })} + {items?.length === 4 && 1 autre zone} + {items && items.length > 4 && {items.length - 2} autres zones} + + ) +} + +const Layerlist = styled.ul` + min-width: 200px; + min-height: 100px; + border: 1px solid red; + list-style: none; + padding: 0; + margin: 0; +` + +const LayerItem = styled.li` + padding: 5px; + background-color: white; + border-bottom: 1px solid #cccfd6; ; +` +const Name = styled.span` + color: ${p => p.theme.color.gunMetal}; + font: normal normal bold 13px/18px Marianne; +` + +const Type = styled.span` + color: ${p => p.theme.color.gunMetal}; + font: normal normal normal 13px/18px Marianne; +` +const More = styled.li` + background-color: white; + color: ${p => p.theme.color.slateGray}; + font: italic normal bold 13px/18px Marianne; + padding: 5px; +` diff --git a/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx b/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx new file mode 100644 index 0000000000..824a6c4a36 --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx @@ -0,0 +1,28 @@ +import styled from 'styled-components' + +import { OverlayContent } from './OverlayContent' + +import type { OverlayItem } from 'domain/types/map' + +export function OverlayMenu({ items, pixel }: { items: OverlayItem[]; pixel: number[] }) { + if (!pixel) { + return null + } + + const [x, y] = pixel + + return ( +

+ + + ) +} + +const Menu = styled.div<{ x: number | undefined; y: number | undefined }>` + position: absolute; + top: ${p => String(p.y ?? 0 + 10)}px; + left: ${p => String(p.x ?? 0 + 10)}px; + max-width: 440px; + box-shadow: 0px 2px 4px #707785bf; + pointer-events: none; +` diff --git a/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx b/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx new file mode 100644 index 0000000000..4c5b5b2cc2 --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx @@ -0,0 +1,63 @@ +import Overlay from 'ol/Overlay' +import { useCallback, useEffect, useRef } from 'react' +import styled from 'styled-components' + +import type OpenLayerMap from 'ol/Map' + +type OverlayPositionOnCoordinatesProps = { + children: React.ReactNode + coordinates: number[] | undefined + layerOverlayIsOpen: boolean + map: OpenLayerMap +} +export function OverlayPositionOnCoordinates({ + children, + coordinates, + layerOverlayIsOpen, + map +}: OverlayPositionOnCoordinatesProps) { + const containerRef = useRef(null) + const olOverlayRef = useRef(null) + + const attachContentToOverlay = useCallback( + (ref: HTMLDivElement) => { + containerRef.current = ref + if (ref) { + olOverlayRef.current = new Overlay({ + className: `ol-overlay-container ol-selectable layer-overlay`, + element: ref + }) + } else { + olOverlayRef.current = null + } + }, + [containerRef, olOverlayRef] + ) + + useEffect(() => { + if (map && olOverlayRef.current) { + map.addOverlay(olOverlayRef.current) + } + + return () => { + if (map && olOverlayRef.current) { + map.removeOverlay(olOverlayRef.current) + } + } + }, [map, olOverlayRef]) + + useEffect(() => { + if (containerRef.current) { + if (layerOverlayIsOpen) { + containerRef.current.style.display = 'flex' + olOverlayRef.current?.setPosition(coordinates) + } else { + containerRef.current.style.display = 'none' + } + } + }, [layerOverlayIsOpen, coordinates]) + + return {layerOverlayIsOpen && children} +} + +const Container = styled.div`` diff --git a/frontend/src/features/layersSelector/overlays/index.tsx b/frontend/src/features/layersSelector/overlays/index.tsx new file mode 100644 index 0000000000..ca272aed6d --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/index.tsx @@ -0,0 +1,53 @@ +import { getClickedAmpFeatures, getClickedItems, getClickedRegulatoryFeatures } from '@features/map/utils' +import { useAppDispatch } from '@hooks/useAppDispatch' +import { convertToFeature } from 'domain/types/map' +import { useEffect } from 'react' + +import { + closeLayerOverlay, + closeMetadataPanel, + openAMPMetadataPanel, + openLayerOverlay, + openRegulatoryMetadataPanel, + setLayerOverlayItems +} from '../metadataPanel/slice' + +import type { BaseMapChildrenProps } from '@features/map/BaseMap' + +export function LayerOverlay({ mapClickEvent }: BaseMapChildrenProps) { + const dispatch = useAppDispatch() + + useEffect(() => { + const clickedAmpFeatures = getClickedAmpFeatures(mapClickEvent) + const clickedRegulatoryFeatures = getClickedRegulatoryFeatures(mapClickEvent) + const numberOfClickedFeatures = (clickedAmpFeatures?.length ?? 0) + (clickedRegulatoryFeatures?.length ?? 0) + if (numberOfClickedFeatures === 0) { + dispatch(closeLayerOverlay()) + } + + if (numberOfClickedFeatures === 1 && clickedAmpFeatures && clickedAmpFeatures.length === 1) { + const feature = convertToFeature(clickedAmpFeatures[0]) + if (feature) { + const layerId = feature.get('id') + dispatch(openAMPMetadataPanel(layerId)) + } + } + + if (numberOfClickedFeatures === 1 && clickedRegulatoryFeatures && clickedRegulatoryFeatures.length === 1) { + const feature = convertToFeature(clickedRegulatoryFeatures[0]) + if (feature) { + const layerId = feature.get('id') + dispatch(openRegulatoryMetadataPanel(layerId)) + } + } + + if (numberOfClickedFeatures > 1 && mapClickEvent.coordinates) { + dispatch(closeMetadataPanel()) + dispatch(openLayerOverlay(mapClickEvent.coordinates)) + const items = getClickedItems(mapClickEvent) + dispatch(setLayerOverlayItems(items)) + } + }, [dispatch, mapClickEvent]) + + return null +} diff --git a/frontend/src/features/layersSelector/overlays/utils.ts b/frontend/src/features/layersSelector/overlays/utils.ts new file mode 100644 index 0000000000..ddac2e37e3 --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/utils.ts @@ -0,0 +1,26 @@ +import { MonitorEnvLayers } from 'domain/entities/layers/constants' + +import type { AMPPRoperties } from 'domain/entities/AMPs' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' + +export const getName = ( + layer: AMPPRoperties | RegulatoryLayerCompactProperties, + layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV +) => { + if (layerType === MonitorEnvLayers.AMP) { + return (layer as AMPPRoperties).name + } + + return (layer as RegulatoryLayerCompactProperties).entity_name +} + +export const getType = ( + layer: AMPPRoperties | RegulatoryLayerCompactProperties, + layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV +) => { + if (layerType === MonitorEnvLayers.AMP) { + return (layer as AMPPRoperties).type + } + + return (layer as RegulatoryLayerCompactProperties).thematique +} diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index 03e2837423..a63cfe4086 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -1,4 +1,10 @@ import { MultiRadio } from '@mtes-mct/monitor-ui' +import { + getGeoJSONFromFeature, + getGeoJSONFromFeatureList, + type MapClickEvent, + type SerializedFeature +} from 'domain/types/map' import { throttle } from 'lodash' import { ScaleLine, defaults as defaultControls } from 'ol/control' import Zoom from 'ol/control/Zoom' @@ -29,25 +35,33 @@ import { useAppSelector } from '../../hooks/useAppSelector' import { useClickOutsideWhenOpened } from '../../hooks/useClickOutsideWhenOpened' import type { VectorLayerWithName } from '../../domain/types/layer' -import type { MapClickEvent } from '../../types' -import type { Feature, MapBrowserEvent } from 'ol' -import type { Geometry } from 'ol/geom' +import type { MapBrowserEvent } from 'ol' export type BaseMapChildrenProps = { - currentFeatureOver: Feature | undefined + currentFeatureListOver: SerializedFeature>[] | undefined + currentFeatureOver: SerializedFeature> | undefined map: OpenLayerMap mapClickEvent: MapClickEvent + pixel: number[] | undefined } export function BaseMap({ children }: { children: Array | null> }) { const dispatch = useAppDispatch() const [currentMap, setCurrentMap] = useState(undefined) - const [mapClickEvent, setMapClickEvent] = useState<{ - ctrlKeyPressed: boolean - feature: Feature | undefined - }>({ ctrlKeyPressed: false, feature: undefined }) + const [mapClickEvent, setMapClickEvent] = useState({ + coordinates: undefined, + ctrlKeyPressed: false, + feature: undefined, + featureList: undefined + }) - const [currentFeatureOver, setCurrentFeatureOver] = useState | undefined>(undefined) + const [currentFeatureOver, setCurrentFeatureOver] = useState> | undefined>( + undefined + ) + const [currentFeatureListOver, setCurrentFeatureListOver] = useState< + SerializedFeature>[] | undefined + >(undefined) + const [pixel, setPixel] = useState(undefined) const mapElement = useRef() as MutableRefObject @@ -59,22 +73,25 @@ export function BaseMap({ children }: { children: Array, current_map: OpenLayerMap) => { if (event && current_map) { - const feature = current_map.forEachFeatureAtPixel>( - event.pixel, - featureAtPixel => featureAtPixel as Feature, - { - hitTolerance: HIT_PIXEL_TO_TOLERANCE, - layerFilter: layer => { - const typedLayer = layer as VectorLayerWithName + const featureList = current_map.getFeaturesAtPixel(event.pixel, { + hitTolerance: HIT_PIXEL_TO_TOLERANCE, + layerFilter: layer => { + const typedLayer = layer as VectorLayerWithName - const layerName = typedLayer.name ?? typedLayer.get('name') + const layerName = typedLayer.name ?? typedLayer.get('name') - return !!layerName && SelectableLayers.includes(layerName) - } + return !!layerName && SelectableLayers.includes(layerName) } - ) + }) + const feature = getGeoJSONFromFeature>(featureList?.[0]) + const featuresAsGeoJSON = getGeoJSONFromFeatureList(featureList) const isCtrl = platformModifierKeyOnly(event) - setMapClickEvent({ ctrlKeyPressed: isCtrl, feature }) + setMapClickEvent({ + coordinates: event.coordinate, + ctrlKeyPressed: isCtrl, + feature, + featureList: featuresAsGeoJSON + }) } }, [setMapClickEvent] @@ -84,19 +101,21 @@ export function BaseMap({ children }: { children: Array throttle((event: MapBrowserEvent, current_map: OpenLayerMap) => { if (event && current_map) { - const feature = current_map.forEachFeatureAtPixel>( - event.pixel, - featureAtPixel => featureAtPixel as Feature, - { - hitTolerance: HIT_PIXEL_TO_TOLERANCE, - layerFilter: layer => { - const typedLayer = layer as VectorLayerWithName - - return !!typedLayer.name && HoverableLayers.includes(typedLayer.name) - } + const features = current_map.getFeaturesAtPixel(event.pixel, { + hitTolerance: HIT_PIXEL_TO_TOLERANCE, + layerFilter: layer => { + const typedLayer = layer as VectorLayerWithName + + const layerName = typedLayer.name ?? typedLayer.get('name') + + return !!layerName && HoverableLayers.includes(layerName) } - ) - setCurrentFeatureOver(feature) + }) + setCurrentFeatureListOver(getGeoJSONFromFeatureList(features)) + + setCurrentFeatureOver(getGeoJSONFromFeature(features?.[0])) + + setPixel(event.pixel) } }, 50), [setCurrentFeatureOver] @@ -167,9 +186,11 @@ export function BaseMap({ children }: { children: Array child && cloneElement(child, { + currentFeatureListOver, currentFeatureOver, map: currentMap, - mapClickEvent + mapClickEvent, + pixel }) )} diff --git a/frontend/src/features/map/ShowAMPMetadata.ts b/frontend/src/features/map/ShowAMPMetadata.ts deleted file mode 100644 index 76ce617113..0000000000 --- a/frontend/src/features/map/ShowAMPMetadata.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect } from 'react' - -import { Layers } from '../../domain/entities/layers/constants' -import { useAppDispatch } from '../../hooks/useAppDispatch' -import { openAMPMetadataPanel } from '../layersSelector/metadataPanel/slice' - -import type { BaseMapChildrenProps } from './BaseMap' - -export function ShowAMPMetadata({ mapClickEvent }: BaseMapChildrenProps) { - const dispatch = useAppDispatch() - - useEffect(() => { - if (mapClickEvent?.feature) { - const { feature } = mapClickEvent - const featureId = feature?.getId()?.toString() - if (featureId?.includes(Layers.AMP_PREVIEW.code) || featureId?.includes(Layers.AMP.code)) { - const layerId = feature.get('id') - dispatch(openAMPMetadataPanel(layerId)) - } - } - }, [dispatch, mapClickEvent]) - - return null -} diff --git a/frontend/src/features/map/ShowRegulatoryMetadata.ts b/frontend/src/features/map/ShowRegulatoryMetadata.ts deleted file mode 100644 index 4ca718552f..0000000000 --- a/frontend/src/features/map/ShowRegulatoryMetadata.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useEffect } from 'react' - -import { Layers } from '../../domain/entities/layers/constants' -import { useAppDispatch } from '../../hooks/useAppDispatch' -import { openRegulatoryMetadataPanel } from '../layersSelector/metadataPanel/slice' - -import type { BaseMapChildrenProps } from './BaseMap' - -export function ShowRegulatoryMetadata({ mapClickEvent }: BaseMapChildrenProps) { - const dispatch = useAppDispatch() - - useEffect(() => { - if (mapClickEvent?.feature) { - const { feature } = mapClickEvent - const featureId = feature?.getId()?.toString() - if (featureId?.includes(Layers.REGULATORY_ENV_PREVIEW.code) || featureId?.includes(Layers.REGULATORY_ENV.code)) { - const layerId = feature.get('id') - dispatch(openRegulatoryMetadataPanel(layerId)) - } - } - }, [dispatch, mapClickEvent]) - - return null -} diff --git a/frontend/src/features/map/index.tsx b/frontend/src/features/map/index.tsx index 0e0c2b0a4f..d3e108d626 100644 --- a/frontend/src/features/map/index.tsx +++ b/frontend/src/features/map/index.tsx @@ -1,3 +1,6 @@ +import { LayerOverlay } from '@features/layersSelector/overlays' +import { LayersOverlay } from '@features/layersSelector/overlays/LayersOverlay' + import { BaseMap } from './BaseMap' import { MapAttributionsBox } from './controls/MapAttributionsBox' import { MapCoordinatesBox } from './controls/MapCoordinatesBox' @@ -25,8 +28,6 @@ import { ActionOverlay } from './overlays/actions' import { MissionOverlays } from './overlays/missions' import { ReportingOverlay } from './overlays/reportings' import { SemaphoreOverlay } from './overlays/semaphores' -import { ShowAMPMetadata } from './ShowAMPMetadata' -import { ShowRegulatoryMetadata } from './ShowRegulatoryMetadata' import { ZoomListener } from './ZoomListener' import { ReportingToAttachLayer } from '../missions/Layers/ReportingToAttach' import { HoveredReportingToAttachLayer } from '../missions/Layers/ReportingToAttach/HoveredReportingToAttachLayer' @@ -64,16 +65,18 @@ export function Map() { {/* @ts-ignore */} {/* @ts-ignore */} - - {/* @ts-ignore */} {/* @ts-ignore */} {/* @ts-ignore */} - - {/* @ts-ignore */} {/* @ts-ignore */} + + {/* @ts-ignore */} + + + {/* MAP */} + {/* @ts-ignore */} {/* @ts-ignore */} diff --git a/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts b/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts index 1e2888e227..e196c37132 100644 --- a/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts +++ b/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts @@ -15,6 +15,7 @@ import { useAppSelector } from '../../../../hooks/useAppSelector' import { dottedLayerStyle } from '../styles/dottedLayer.style' import type { BaseMapChildrenProps } from '../../BaseMap' +import type { VectorLayerWithName } from 'domain/types/layer' export const metadataIsShowedPropertyName = 'metadataIsShowed' @@ -23,39 +24,21 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { const ampsSearchResult = useAppSelector(state => state.layerSearch.ampsSearchResult) const isAmpSearchResultsVisible = useAppSelector(state => state.layerSearch.isAmpSearchResultsVisible) const searchExtent = useAppSelector(state => state.layerSearch.searchExtent) + const myAmpLayerIds = useAppSelector(state => state.amp.selectedAmpLayerIds) // or showedAmpLayerIds ? const { data: ampLayers } = useGetAMPsQuery() const { isLayersSidebarVisible } = useAppSelector(state => state.global) - const ampLayerRef = useRef() as MutableRefObject> - const ampVectorSourceRef = useRef() as MutableRefObject + const ampLayerRef = useRef() as MutableRefObject + const ampVectorSourceRef = useRef(new VectorSource()) const isThrottled = useRef(false) - function getAMPVectorSource() { - if (!ampVectorSourceRef.current) { - ampVectorSourceRef.current = new VectorSource({ - features: [] - }) - } - - return ampVectorSourceRef.current - } - const searchExtentLayerRef = useRef() as MutableRefObject> - const seachExtentVectorSourceRef = useRef() as MutableRefObject - function getSearchExtentVectorSource() { - if (!seachExtentVectorSourceRef.current) { - seachExtentVectorSourceRef.current = new VectorSource({ - features: [] - }) - } - - return seachExtentVectorSourceRef.current - } + const seachExtentVectorSourceRef = useRef(new VectorSource()) useEffect(() => { if (map) { - const features = getAMPVectorSource().getFeatures() + const features = ampVectorSourceRef.current.getFeatures() if (features?.length) { features.forEach(f => f.set(metadataIsShowedPropertyName, f.get('id') === ampMetadataLayerId)) } @@ -64,9 +47,12 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { useEffect(() => { function refreshPreviewLayer() { - getAMPVectorSource().clear() + ampVectorSourceRef.current.clear() if (ampsSearchResult && ampLayers?.entities) { const features = ampsSearchResult.reduce((amplayers, id) => { + if (myAmpLayerIds.includes(id)) { + return amplayers + } const layer = ampLayers.entities[id] if (layer && layer.geom) { @@ -88,7 +74,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { return amplayers }, [] as Feature[]) - getAMPVectorSource().addFeatures(features) + ampVectorSourceRef.current.addFeatures(features) } } @@ -104,7 +90,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { refreshPreviewLayer() }, 300) } - }, [map, ampsSearchResult, ampLayers]) + }, [map, ampsSearchResult, ampLayers, myAmpLayerIds]) useEffect(() => { function getLayer() { @@ -115,11 +101,12 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { }, renderBuffer: 4, renderOrder: (a, b) => b.get('area') - a.get('area'), - source: getAMPVectorSource(), + source: ampVectorSourceRef.current, style: getAMPLayerStyle, updateWhileAnimating: true, updateWhileInteracting: true }) + ampLayerRef.current.name = Layers.AMP.code } return ampLayerRef.current @@ -149,10 +136,10 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { useEffect(() => { if (map) { - getSearchExtentVectorSource().clear() + seachExtentVectorSourceRef.current.clear() if (searchExtent) { const feature = new Feature(fromExtent(searchExtent)) - getSearchExtentVectorSource().addFeature(feature) + seachExtentVectorSourceRef.current.addFeature(feature) } } }, [map, searchExtent]) @@ -161,7 +148,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { function getLayer() { if (!searchExtentLayerRef.current) { searchExtentLayerRef.current = new Vector({ - source: getSearchExtentVectorSource(), + source: seachExtentVectorSourceRef.current, style: dottedLayerStyle, updateWhileAnimating: true, updateWhileInteracting: true diff --git a/frontend/src/features/map/layers/AMP/index.ts b/frontend/src/features/map/layers/AMP/index.ts index 8a63975f90..488d5b01de 100644 --- a/frontend/src/features/map/layers/AMP/index.ts +++ b/frontend/src/features/map/layers/AMP/index.ts @@ -2,7 +2,7 @@ import GeoJSON from 'ol/format/GeoJSON' import { Vector } from 'ol/layer' import VectorSource from 'ol/source/Vector' import { getArea } from 'ol/sphere' -import { type MutableRefObject, useEffect, useRef } from 'react' +import { useEffect, useRef } from 'react' import { getAMPLayerStyle } from './AMPLayers.style' import { useGetAMPsQuery } from '../../../../api/ampsAPI' @@ -11,6 +11,7 @@ import { OPENLAYERS_PROJECTION } from '../../../../domain/entities/map/constants import { useAppSelector } from '../../../../hooks/useAppSelector' import type { BaseMapChildrenProps } from '../../BaseMap' +import type { VectorLayerWithName } from 'domain/types/layer' import type { Feature } from 'ol' export const metadataIsShowedPropertyName = 'metadataIsShowed' @@ -20,50 +21,36 @@ export function AMPLayers({ map }: BaseMapChildrenProps) { const { data: ampLayers } = useGetAMPsQuery() - const vectorSourceRef = useRef() as MutableRefObject - function getVectorSource() { - if (!vectorSourceRef.current) { - vectorSourceRef.current = new VectorSource({ - features: [] - }) - } + const vectorSourceRef = useRef(new VectorSource()) - return vectorSourceRef.current - } - const layerRef = useRef() as MutableRefObject> + const layerRef = useRef( + new Vector({ + renderBuffer: 4, + renderOrder: (a, b) => b.get('area') - a.get('area'), + source: vectorSourceRef.current, + style: getAMPLayerStyle, + updateWhileAnimating: true, + updateWhileInteracting: true + }) + ) + layerRef.current.name = Layers.AMP.code useEffect(() => { - function getLayer() { - if (!layerRef.current) { - layerRef.current = new Vector({ - properties: { - name: Layers.AMP.code - }, - renderBuffer: 4, - renderOrder: (a, b) => b.get('area') - a.get('area'), - source: getVectorSource(), - style: getAMPLayerStyle, - updateWhileAnimating: true, - updateWhileInteracting: true - }) - } - - return layerRef.current - } + const layer = layerRef.current if (map) { - map.getLayers().push(getLayer()) + map.getLayers().push(layerRef.current) } return () => { if (map) { - map.removeLayer(getLayer()) + map.removeLayer(layer) } } }, [map]) useEffect(() => { if (map) { - getVectorSource().clear() + vectorSourceRef.current.clear() if (ampLayers?.entities) { const features = showedAmpLayerIds.reduce((feats: Feature[], layerId) => { const ampLayer = ampLayers.entities[layerId] @@ -83,7 +70,7 @@ export function AMPLayers({ map }: BaseMapChildrenProps) { return feats }, []) - getVectorSource().addFeatures(features) + vectorSourceRef.current.addFeatures(features) } } }, [map, ampLayers, showedAmpLayerIds]) diff --git a/frontend/src/features/map/layers/Missions/HoveredMissionLayer.ts b/frontend/src/features/map/layers/Missions/HoveredMissionLayer.ts index e5201e2050..a123d7fc01 100644 --- a/frontend/src/features/map/layers/Missions/HoveredMissionLayer.ts +++ b/frontend/src/features/map/layers/Missions/HoveredMissionLayer.ts @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { type MutableRefObject, useEffect, useRef } from 'react' @@ -39,8 +40,9 @@ export function HoveredMissionLayer({ currentFeatureOver, map }: BaseMapChildren useEffect(() => { hoveredMissionVectorSourceRef.current?.clear(true) - if (currentFeatureOver && currentFeatureOver.getId()?.toString()?.includes(Layers.MISSIONS.code)) { - hoveredMissionVectorSourceRef.current?.addFeature(currentFeatureOver) + const feature = convertToFeature(currentFeatureOver) + if (feature && feature.getId()?.toString()?.includes(Layers.MISSIONS.code)) { + hoveredMissionVectorSourceRef.current?.addFeature(feature) } }, [currentFeatureOver]) diff --git a/frontend/src/features/map/layers/Missions/index.tsx b/frontend/src/features/map/layers/Missions/index.tsx index e2cef828e9..5c403650a6 100644 --- a/frontend/src/features/map/layers/Missions/index.tsx +++ b/frontend/src/features/map/layers/Missions/index.tsx @@ -1,4 +1,5 @@ import { removeOverlayCoordinatesByName } from 'domain/shared_slices/Global' +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { useEffect, useMemo, useRef, type MutableRefObject } from 'react' @@ -126,8 +127,8 @@ export function MissionsLayer({ map, mapClickEvent }: BaseMapChildrenProps) { }, [isLayerVisible]) useEffect(() => { - if (mapClickEvent?.feature) { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) + if (feature) { if (feature.getId()?.toString()?.includes(Layers.MISSIONS.code)) { const { missionId } = feature.getProperties() dispatch(missionActions.setSelectedMissionIdOnMap(missionId)) diff --git a/frontend/src/features/map/layers/Reportings/HoveredReportingLayer.tsx b/frontend/src/features/map/layers/Reportings/HoveredReportingLayer.tsx index 05c7fb5f05..4016d54092 100644 --- a/frontend/src/features/map/layers/Reportings/HoveredReportingLayer.tsx +++ b/frontend/src/features/map/layers/Reportings/HoveredReportingLayer.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { type MutableRefObject, useEffect, useRef } from 'react' @@ -34,8 +35,9 @@ export function HoveredReportingLayer({ currentFeatureOver, map }: BaseMapChildr useEffect(() => { vectorSourceRef.current?.clear(true) - if (currentFeatureOver && currentFeatureOver.getId()?.toString()?.includes(Layers.REPORTINGS.code)) { - vectorSourceRef.current?.addFeature(currentFeatureOver) + const feature = convertToFeature(currentFeatureOver) + if (feature && feature.getId()?.toString()?.includes(Layers.REPORTINGS.code)) { + vectorSourceRef.current?.addFeature(feature) } }, [currentFeatureOver]) diff --git a/frontend/src/features/map/layers/Reportings/index.tsx b/frontend/src/features/map/layers/Reportings/index.tsx index 1b3ccff127..f677654f85 100644 --- a/frontend/src/features/map/layers/Reportings/index.tsx +++ b/frontend/src/features/map/layers/Reportings/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { reduce } from 'lodash' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' @@ -147,7 +148,7 @@ export function ReportingsLayer({ map, mapClickEvent }: BaseMapChildrenProps) { }, [isLayerVisible]) useEffect(() => { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) if (feature && feature.getId()?.toString()?.includes(Layers.REPORTINGS.code)) { const { id } = feature.getProperties() dispatch(reportingActions.setSelectedReportingIdOnMap(id)) diff --git a/frontend/src/features/map/layers/Semaphores/index.ts b/frontend/src/features/map/layers/Semaphores/index.ts index 52a23e9b00..d6e00f82b4 100644 --- a/frontend/src/features/map/layers/Semaphores/index.ts +++ b/frontend/src/features/map/layers/Semaphores/index.ts @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { cloneDeep, reduce } from 'lodash' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' @@ -138,8 +139,8 @@ export function SemaphoresLayer({ map, mapClickEvent }: BaseMapChildrenProps) { }, [isLayerVisible, GetVectorLayer]) useEffect(() => { - if (mapClickEvent?.feature) { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) + if (feature) { if (feature.getId()?.toString()?.includes(Layers.SEMAPHORES.code)) { const { id } = feature.getProperties() dispatch(setSelectedSemaphore(id)) diff --git a/frontend/src/features/map/overlays/OverlayPositionOnCentroid.tsx b/frontend/src/features/map/overlays/OverlayPositionOnCentroid.tsx index bf22e801a7..394ae04392 100644 --- a/frontend/src/features/map/overlays/OverlayPositionOnCentroid.tsx +++ b/frontend/src/features/map/overlays/OverlayPositionOnCentroid.tsx @@ -1,5 +1,4 @@ -// @ts-nocheck -import { getCenter } from 'ol/extent' +import { getCenter, type Extent } from 'ol/extent' import Overlay from 'ol/Overlay' import React, { useCallback, useEffect, useRef, useState } from 'react' import styled from 'styled-components' @@ -9,8 +8,8 @@ import { setOverlayCoordinatesByName } from '../../../domain/shared_slices/Globa import { useAppDispatch } from '../../../hooks/useAppDispatch' import { useMoveOverlayWhenDragging } from '../../../hooks/useMoveOverlayWhenDragging' -import type { Feature } from 'ol' -import type { Geometry } from 'ol/geom' +import type { FeatureLike } from 'ol/Feature' +import type OpenLayerMap from 'ol/Map' const OVERLAY_HEIGHT = 174 @@ -27,9 +26,9 @@ const defaultMargins = { type OverlayPositionOnCentroidProps = { appClassName: string children: React.ReactNode - feature: Feature | null | undefined + feature: FeatureLike | null | undefined featureIsShowed?: boolean - map: any + map: OpenLayerMap options?: { margins?: { xLeft: number @@ -52,35 +51,36 @@ export function OverlayPositionOnCentroid({ zIndex }: OverlayPositionOnCentroidProps) { const dispatch = useAppDispatch() - const overlayRef = useRef(null) - const olOverlayObjectRef = useRef(null) + const containerRef = useRef(null) + const olOverlayRef = useRef(null) + const isThrottled = useRef(false) const [showed, setShowed] = useState(false) - const currentCoordinates = useRef([]) + const currentCoordinates = useRef() const [overlayTopLeftMargin, setOverlayTopLeftMargin] = useState([margins.yBottom, margins.xMiddle]) const currentOffset = useRef(INITIAL_OFFSET_VALUE) - const overlayCallback = useCallback( + const attachContentToOverlay = useCallback( (ref: HTMLDivElement) => { - overlayRef.current = ref + containerRef.current = ref if (ref) { - olOverlayObjectRef.current = new Overlay({ + olOverlayRef.current = new Overlay({ className: `ol-overlay-container ol-selectable ${appClassName}`, element: ref, offset: currentOffset.current }) } else { - olOverlayObjectRef.current = null + olOverlayRef.current = null } }, - [overlayRef, olOverlayObjectRef, appClassName] + [containerRef, olOverlayRef, appClassName] ) useEffect(() => { - if (olOverlayObjectRef.current) { + if (olOverlayRef.current) { currentOffset.current = INITIAL_OFFSET_VALUE - olOverlayObjectRef.current.setOffset(INITIAL_OFFSET_VALUE) + olOverlayRef.current.setOffset(INITIAL_OFFSET_VALUE) } if (feature) { currentCoordinates.current = feature.getGeometry()?.getExtent() @@ -90,8 +90,8 @@ export function OverlayPositionOnCentroid({ }, [feature]) useEffect(() => { - if (map) { - map.addOverlay(olOverlayObjectRef.current) + if (map && olOverlayRef.current) { + map.addOverlay(olOverlayRef.current) if (featureIsShowed && !showed) { setShowed(true) @@ -99,9 +99,11 @@ export function OverlayPositionOnCentroid({ } return () => { - map.removeOverlay(olOverlayObjectRef.current) + if (map && olOverlayRef.current) { + map.removeOverlay(olOverlayRef.current) + } } - }, [map, olOverlayObjectRef, featureIsShowed, showed]) + }, [map, olOverlayRef, featureIsShowed, showed]) const moveCardWithThrottle = useCallback( (target, delay) => { @@ -120,9 +122,10 @@ export function OverlayPositionOnCentroid({ const nextYPixelCenter = pixel[1] + offset[1] + overlayTopLeftMargin[0] const nextCoordinates = map.getCoordinateFromPixel([nextXPixelCenter, nextYPixelCenter]) - const featureContext = feature.getId().split(':')[0] - dispatch(setOverlayCoordinatesByName({ coordinates: nextCoordinates, name: featureContext })) - + const featureContext = String(feature?.getId())?.split(':')[0] + if (featureContext) { + dispatch(setOverlayCoordinatesByName({ coordinates: nextCoordinates, name: featureContext })) + } isThrottled.current = false } }, delay) @@ -134,30 +137,31 @@ export function OverlayPositionOnCentroid({ function getNextOverlayPosition(featureCenter) { const [x, y] = featureCenter const extent = map.getView().calculateExtent() - const boxSize = map.getView().getResolution() * OVERLAY_HEIGHT + const boxSize = (map.getView()?.getResolution() ?? 0) * OVERLAY_HEIGHT const position = getOverlayPositionForCentroid(boxSize, x, y, extent) return position } - if (overlayRef.current && olOverlayObjectRef.current) { + if (containerRef.current && olOverlayRef.current) { if (feature && feature.getGeometry()) { - const featureCenter = getCenter(feature.getGeometry().getExtent()) - olOverlayObjectRef.current.setPosition(featureCenter) + const extent = feature.getGeometry()?.getExtent() + const featureCenter = extent && getCenter(extent) + olOverlayRef.current.setPosition(featureCenter) const nextOverlayPosition = getNextOverlayPosition(featureCenter) setOverlayTopLeftMargin(getTopLeftMargin(nextOverlayPosition, margins)) - overlayRef.current.style.display = 'flex' + containerRef.current.style.display = 'flex' } else { - overlayRef.current.style.display = 'none' + containerRef.current.style.display = 'none' } } - }, [dispatch, feature, overlayRef, olOverlayObjectRef, map, margins]) + }, [dispatch, feature, containerRef, olOverlayRef, map, margins]) - useMoveOverlayWhenDragging(olOverlayObjectRef.current, map, currentOffset, moveCardWithThrottle, showed) + useMoveOverlayWhenDragging(olOverlayRef.current, map, currentOffset, moveCardWithThrottle, showed) return ( - + {feature && children} ) diff --git a/frontend/src/features/map/overlays/actions/index.tsx b/frontend/src/features/map/overlays/actions/index.tsx index 0bd544c206..9b28e856c1 100644 --- a/frontend/src/features/map/overlays/actions/index.tsx +++ b/frontend/src/features/map/overlays/actions/index.tsx @@ -1,3 +1,5 @@ +import { convertToFeature } from 'domain/types/map' + import { ControlCard } from './ControlCard' import { SurveillanceCard } from './SurveillanceCard' import { Layers } from '../../../../domain/entities/layers/constants' @@ -7,7 +9,8 @@ import { OverlayPositionOnCentroid } from '../OverlayPositionOnCentroid' import type { BaseMapChildrenProps } from '../../BaseMap' export function ActionOverlay({ currentFeatureOver, map }: BaseMapChildrenProps) { - const currentfeatureId = currentFeatureOver?.getId() + const hoveredFeature = convertToFeature(currentFeatureOver) + const currentfeatureId = hoveredFeature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(`${Layers.ACTIONS.code}`) const displayControlCard = @@ -20,12 +23,12 @@ export function ActionOverlay({ currentFeatureOver, map }: BaseMapChildrenProps) return ( - {displayControlCard && } - {displaySurveillanceCard && } + {displayControlCard && } + {displaySurveillanceCard && } ) } diff --git a/frontend/src/features/map/overlays/missions/index.tsx b/frontend/src/features/map/overlays/missions/index.tsx index 0fd914783b..158fa07102 100644 --- a/frontend/src/features/map/overlays/missions/index.tsx +++ b/frontend/src/features/map/overlays/missions/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { useState } from 'react' import { MissionCard } from './MissionCard' @@ -34,7 +35,9 @@ export function MissionOverlays({ currentFeatureOver, map }: BaseMapChildrenProp ) ?.getSource() ?.getFeatureById(`${Layers.MISSIONS.code}:${selectedMissionId}`) - const currentfeatureId = currentFeatureOver?.getId() + + const hoveredFeature = convertToFeature(currentFeatureOver) + const currentfeatureId = hoveredFeature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(Layers.MISSIONS.code) && @@ -66,12 +69,12 @@ export function MissionOverlays({ currentFeatureOver, map }: BaseMapChildrenProp - + ) diff --git a/frontend/src/features/map/overlays/reportings/index.tsx b/frontend/src/features/map/overlays/reportings/index.tsx index 51eb9a4334..1b9e76a057 100644 --- a/frontend/src/features/map/overlays/reportings/index.tsx +++ b/frontend/src/features/map/overlays/reportings/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { useState } from 'react' import { ReportingCard } from './ReportingCard' @@ -34,7 +35,9 @@ export function ReportingOverlay({ currentFeatureOver, map }: BaseMapChildrenPro ) ?.getSource() ?.getFeatureById(`${Layers.REPORTINGS.code}:${selectedReportingIdOnMap}`) - const currentfeatureId = currentFeatureOver?.getId() + + const hoveredFeature = convertToFeature(currentFeatureOver) + const currentfeatureId = hoveredFeature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(Layers.REPORTINGS.code) && @@ -66,12 +69,12 @@ export function ReportingOverlay({ currentFeatureOver, map }: BaseMapChildrenPro - + ) diff --git a/frontend/src/features/map/overlays/semaphores/index.tsx b/frontend/src/features/map/overlays/semaphores/index.tsx index fe487f91bb..cfd32054d3 100644 --- a/frontend/src/features/map/overlays/semaphores/index.tsx +++ b/frontend/src/features/map/overlays/semaphores/index.tsx @@ -1,3 +1,5 @@ +import { convertToFeature } from 'domain/types/map' + import { SemaphoreCard } from './SemaphoreCard' import { Layers } from '../../../../domain/entities/layers/constants' import { useAppSelector } from '../../../../hooks/useAppSelector' @@ -18,7 +20,7 @@ export function SemaphoreOverlay({ currentFeatureOver, map }: BaseMapChildrenPro const selectedSemaphoreId = useAppSelector(state => state.semaphoresSlice.selectedSemaphoreId) const displaySemaphoreOverlay = useAppSelector(state => state.global.displaySemaphoreOverlay) - const feature = map + const selectedFeature = map ?.getLayers() ?.getArray() ?.find( @@ -27,7 +29,9 @@ export function SemaphoreOverlay({ currentFeatureOver, map }: BaseMapChildrenPro ) ?.getSource() ?.getFeatureById(`${Layers.SEMAPHORES.code}:${selectedSemaphoreId}`) - const currentfeatureId = currentFeatureOver?.getId() + + const hoveredFeature = convertToFeature(currentFeatureOver) + const currentfeatureId = hoveredFeature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(Layers.SEMAPHORES.code) && @@ -37,22 +41,22 @@ export function SemaphoreOverlay({ currentFeatureOver, map }: BaseMapChildrenPro <> - + - + ) diff --git a/frontend/src/features/map/utils.ts b/frontend/src/features/map/utils.ts new file mode 100644 index 0000000000..589ed66bc3 --- /dev/null +++ b/frontend/src/features/map/utils.ts @@ -0,0 +1,41 @@ +// selectors taht will be moved to the slice + +import { Layers, MonitorEnvLayers } from 'domain/entities/layers/constants' + +import type { AMPPRoperties } from 'domain/entities/AMPs' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' +import type { MapClickEvent, OverlayItem, SerializedFeature } from 'domain/types/map' + +export const getClickedRegulatoryFeatures = (mapClickEvent: MapClickEvent) => + mapClickEvent.featureList?.filter(feature => { + const featureId = String(feature.id) + + return ( + featureId && + (featureId.includes(Layers.REGULATORY_ENV_PREVIEW.code) || featureId.includes(Layers.REGULATORY_ENV.code)) + ) + }) + +export const getClickedAmpFeatures = (mapClickEvent: MapClickEvent) => + mapClickEvent.featureList?.filter(feature => { + const featureId = String(feature.id) + + return featureId && (featureId.includes(Layers.AMP_PREVIEW.code) || featureId.includes(Layers.AMP.code)) + }) + +export const getOverlayItemsFromFeatures = (features: SerializedFeature>[] | undefined) => + features?.reduce((acc, feature) => { + const type = String(feature.id).split(':')[0] + + if (type === MonitorEnvLayers.AMP || type === MonitorEnvLayers.REGULATORY_ENV) { + const { geometry, ...properties } = feature.properties + acc.push({ layerType: type, properties: properties as AMPPRoperties | RegulatoryLayerCompactProperties }) + } + + return acc + }, [] as OverlayItem[]) + +export const getClickedItems = (mapClickEvent: MapClickEvent) => getOverlayItemsFromFeatures(mapClickEvent?.featureList) + +export const getHoveredItems = (features: SerializedFeature>[] | undefined) => + getOverlayItemsFromFeatures(features) diff --git a/frontend/src/features/missions/Layers/ReportingToAttach/HoveredReportingToAttachLayer.tsx b/frontend/src/features/missions/Layers/ReportingToAttach/HoveredReportingToAttachLayer.tsx index 5137013a6c..d4f8c98853 100644 --- a/frontend/src/features/missions/Layers/ReportingToAttach/HoveredReportingToAttachLayer.tsx +++ b/frontend/src/features/missions/Layers/ReportingToAttach/HoveredReportingToAttachLayer.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import { type MutableRefObject, useEffect, useRef } from 'react' @@ -34,11 +35,9 @@ export function HoveredReportingToAttachLayer({ currentFeatureOver, map }: BaseM useEffect(() => { vectorSourceRef.current?.clear(true) - if ( - currentFeatureOver && - currentFeatureOver.getId()?.toString()?.includes(Layers.REPORTING_TO_ATTACH_ON_MISSION.code) - ) { - vectorSourceRef.current?.addFeature(currentFeatureOver) + const feature = convertToFeature(currentFeatureOver) + if (feature && feature.getId()?.toString()?.includes(Layers.REPORTING_TO_ATTACH_ON_MISSION.code)) { + vectorSourceRef.current?.addFeature(feature) } }, [currentFeatureOver]) diff --git a/frontend/src/features/missions/Layers/ReportingToAttach/index.tsx b/frontend/src/features/missions/Layers/ReportingToAttach/index.tsx index b2894faa84..9e9c5b6a96 100644 --- a/frontend/src/features/missions/Layers/ReportingToAttach/index.tsx +++ b/frontend/src/features/missions/Layers/ReportingToAttach/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { reduce } from 'lodash' import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' @@ -100,7 +101,7 @@ export function ReportingToAttachLayer({ map, mapClickEvent }: BaseMapChildrenPr }, [isReportingAttachmentInProgress]) useEffect(() => { - const feature = mapClickEvent?.feature + const feature = convertToFeature(mapClickEvent?.feature) if (feature && feature.getId()?.toString()?.includes(Layers.REPORTING_TO_ATTACH_ON_MISSION.code)) { const { id } = feature.getProperties() dispatch(attachReportingFromMap(id)) diff --git a/frontend/src/features/missions/Overlays/ReportingToAttach/index.tsx b/frontend/src/features/missions/Overlays/ReportingToAttach/index.tsx index 5e4fc0c08c..cbc5570ae6 100644 --- a/frontend/src/features/missions/Overlays/ReportingToAttach/index.tsx +++ b/frontend/src/features/missions/Overlays/ReportingToAttach/index.tsx @@ -1,3 +1,4 @@ +import { convertToFeature } from 'domain/types/map' import { useState } from 'react' import { Layers } from '../../../../domain/entities/layers/constants' @@ -23,7 +24,8 @@ export function ReportingToAttachOverlays({ currentFeatureOver, map }: BaseMapCh const [hoveredOptions, setHoveredOptions] = useState(OPTIONS) - const currentfeatureId = currentFeatureOver?.getId() + const hoveredFeature = convertToFeature(currentFeatureOver) + const currentfeatureId = hoveredFeature?.getId() const displayHoveredFeature = typeof currentfeatureId === 'string' && currentfeatureId.startsWith(Layers.REPORTING_TO_ATTACH_ON_MISSION.code) @@ -36,12 +38,12 @@ export function ReportingToAttachOverlays({ currentFeatureOver, map }: BaseMapCh return ( - + ) } diff --git a/frontend/src/store/index.ts b/frontend/src/store/index.ts index 967dc6ddde..4a210cfda3 100644 --- a/frontend/src/store/index.ts +++ b/frontend/src/store/index.ts @@ -10,8 +10,8 @@ const homeStore = configureStore({ getDefaultMiddleware({ // https://redux-toolkit.js.org/api/serializabilityMiddleware serializableCheck: { - ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], - ignoredPaths: ['layerSearch'], + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER, 'monitorenvPrivateApi/*'], + ignoredPaths: ['layerSearch', 'monitorenvPrivateApi'], // TODO Replace all Redux state Dates by strings & Error by a strict-typed POJO. isSerializable: (value: any) => isPlain(value) || value instanceof Date || value instanceof Error } diff --git a/frontend/src/store/reducers.ts b/frontend/src/store/reducers.ts index 665473ceef..75238d9cd2 100644 --- a/frontend/src/store/reducers.ts +++ b/frontend/src/store/reducers.ts @@ -17,7 +17,7 @@ import { backOfficeReducer } from '../features/BackOffice/slice' import { controlUnitDialogReducer } from '../features/ControlUnit/components/ControlUnitDialog/slice' import { controlUnitListDialogPersistedReducer } from '../features/ControlUnit/components/ControlUnitListDialog/slice' import { controlUnitTablePersistedReducer } from '../features/ControlUnit/components/ControlUnitTable/slice' -import { metadataPanelSliceReducer } from '../features/layersSelector/metadataPanel/slice' +import { layersMetadataSliceReducer } from '../features/layersSelector/metadataPanel/slice' import { layerSearchSliceReducer } from '../features/layersSelector/search/slice' import { mainWindowReducer } from '../features/MainWindow/slice' import { attachReportingToMissionsSliceReducer } from '../features/missions/MissionForm/AttachReporting/slice' @@ -44,12 +44,12 @@ export const homeReducers = { global: globalReducer, interestPoint: interestPointSlicePersistedReducer, layerSearch: layerSearchSliceReducer, + layersMetadata: layersMetadataSliceReducer, mainWindow: mainWindowReducer, map: mapSliceReducer, mapControlUnitDialog: controlUnitDialogReducer, mapControlUnitListDialog: controlUnitListDialogPersistedReducer, measurement: measurementSlicePersistedReducer, - metadataPanel: metadataPanelSliceReducer, mission: missionSliceReducer, missionFilters: missionFiltersPersistedReducer, missionForms: missionFormsSliceReducer, diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 5d88b8c47e..13ed20af5a 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -4,11 +4,6 @@ import type BaseLayer from 'ol/layer/Base' import type VectorImageLayer from 'ol/layer/VectorImage' import type VectorSource from 'ol/source/Vector' -export type MapClickEvent = { - ctrlKeyPressed: boolean - feature?: Feature -} - export type MonitorEnvBaseLayer = BaseLayer & { layerId?: number type?: string From 877caa291771d350f3ceb02a208b205d54772707 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 02/19] style --- .../overlays/HoveredOverlay.tsx | 48 ++++++++++++ .../layersSelector/overlays/LayerEvents.tsx | 53 +++++++++++++ .../layersSelector/overlays/LayersOverlay.tsx | 34 -------- .../overlays/OverlayContent.tsx | 28 +++---- .../layersSelector/overlays/OverlayMenu.tsx | 28 ------- .../overlays/OverlayPositionOnCoordinate.tsx | 2 +- .../layersSelector/overlays/PinnedOverlay.tsx | 37 +++++++++ .../layersSelector/overlays/index.tsx | 77 +++++++------------ frontend/src/features/map/index.tsx | 6 +- 9 files changed, 186 insertions(+), 127 deletions(-) create mode 100644 frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx create mode 100644 frontend/src/features/layersSelector/overlays/LayerEvents.tsx delete mode 100644 frontend/src/features/layersSelector/overlays/LayersOverlay.tsx delete mode 100644 frontend/src/features/layersSelector/overlays/OverlayMenu.tsx create mode 100644 frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx diff --git a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx new file mode 100644 index 0000000000..08a11f670f --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx @@ -0,0 +1,48 @@ +import styled from 'styled-components' + +import { OverlayContent } from './OverlayContent' + +import type { OverlayItem } from 'domain/types/map' + +export function HoveredOverlay({ items, pixel }: { items: OverlayItem[]; pixel: number[] }) { + if (!pixel) { + return null + } + + const [x, y] = pixel + + return ( + + + {items?.length > 1 && ( +
+ {items?.length === 4 && 1 autre zone} + {items && items.length > 4 && {items.length - 2} autres zones} + + {items && items.length > 3 && ' – '}cliquez pour tout afficher et sélectionner une zone + +
+ )} +
+ ) +} + +const Menu = styled.div<{ x: number | undefined; y: number | undefined }>` + position: absolute; + top: ${p => String(p.y ?? 0 + 10)}px; + left: ${p => String(p.x ?? 0 + 10)}px; + width: 440px; + box-shadow: 0px 2px 4px #707785bf; + pointer-events: none; +` +const Footer = styled.div` + background-color: white; + color: ${p => p.theme.color.slateGray}; + padding: 5px; +` +const More = styled.span` + font: italic normal bold 13px/18px Marianne; +` +const ClickForMore = styled.span` + font: italic normal normal 13px/18px Marianne; +` diff --git a/frontend/src/features/layersSelector/overlays/LayerEvents.tsx b/frontend/src/features/layersSelector/overlays/LayerEvents.tsx new file mode 100644 index 0000000000..037b888b2c --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/LayerEvents.tsx @@ -0,0 +1,53 @@ +import { getClickedAmpFeatures, getClickedItems, getClickedRegulatoryFeatures } from '@features/map/utils' +import { useAppDispatch } from '@hooks/useAppDispatch' +import { convertToFeature } from 'domain/types/map' +import { useEffect } from 'react' + +import { + closeLayerOverlay, + closeMetadataPanel, + openAMPMetadataPanel, + openLayerOverlay, + openRegulatoryMetadataPanel, + setLayerOverlayItems +} from '../metadataPanel/slice' + +import type { BaseMapChildrenProps } from '@features/map/BaseMap' + +export function LayerEvents({ mapClickEvent }: BaseMapChildrenProps) { + const dispatch = useAppDispatch() + + useEffect(() => { + const clickedAmpFeatures = getClickedAmpFeatures(mapClickEvent) + const clickedRegulatoryFeatures = getClickedRegulatoryFeatures(mapClickEvent) + const numberOfClickedFeatures = (clickedAmpFeatures?.length ?? 0) + (clickedRegulatoryFeatures?.length ?? 0) + if (numberOfClickedFeatures === 0) { + dispatch(closeLayerOverlay()) + } + + if (numberOfClickedFeatures === 1 && clickedAmpFeatures && clickedAmpFeatures.length === 1) { + const feature = convertToFeature(clickedAmpFeatures[0]) + if (feature) { + const layerId = feature.get('id') + dispatch(openAMPMetadataPanel(layerId)) + } + } + + if (numberOfClickedFeatures === 1 && clickedRegulatoryFeatures && clickedRegulatoryFeatures.length === 1) { + const feature = convertToFeature(clickedRegulatoryFeatures[0]) + if (feature) { + const layerId = feature.get('id') + dispatch(openRegulatoryMetadataPanel(layerId)) + } + } + + if (numberOfClickedFeatures > 1 && mapClickEvent.coordinates) { + dispatch(closeMetadataPanel()) + dispatch(openLayerOverlay(mapClickEvent.coordinates)) + const items = getClickedItems(mapClickEvent) + dispatch(setLayerOverlayItems(items)) + } + }, [dispatch, mapClickEvent]) + + return null +} diff --git a/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx b/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx deleted file mode 100644 index c214a35350..0000000000 --- a/frontend/src/features/layersSelector/overlays/LayersOverlay.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { getHoveredItems } from '@features/map/utils' -import { useAppSelector } from '@hooks/useAppSelector' -import { createPortal } from 'react-dom' - -import { OverlayContent } from './OverlayContent' -import { OverlayMenu } from './OverlayMenu' -import { OverlayPositionOnCoordinates } from './OverlayPositionOnCoordinate' - -import type { BaseMapChildrenProps } from '@features/map/BaseMap' - -export function LayersOverlay({ currentFeatureListOver, map, pixel }: BaseMapChildrenProps) { - const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector( - state => state.layersMetadata - ) - const hoveredItems = getHoveredItems(currentFeatureListOver) - - return ( - <> - - {layerOverlayIsOpen && } - - {createPortal( - !layerOverlayIsOpen && hoveredItems && hoveredItems.length > 0 && pixel && ( - - ), - document.body as HTMLElement - )} - - ) -} diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index e120f4fbc9..ba6b117a00 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -1,6 +1,9 @@ +import { useAppDispatch } from '@hooks/useAppDispatch' +import { MonitorEnvLayers } from 'domain/entities/layers/constants' import styled from 'styled-components' import { getName, getType } from './utils' +import { openAMPMetadataPanel, openRegulatoryMetadataPanel } from '../metadataPanel/slice' import { LayerLegend } from '../utils/LayerLegend.style' import type { OverlayItem } from 'domain/types/map' @@ -10,30 +13,35 @@ type OverlayContentProps = { } export function OverlayContent({ items }: OverlayContentProps) { + const dispatch = useAppDispatch() + const handleClick = (type, id) => () => { + if (type === MonitorEnvLayers.AMP) { + dispatch(openAMPMetadataPanel(id)) + } + if (type === MonitorEnvLayers.REGULATORY_ENV) { + dispatch(openRegulatoryMetadataPanel(id)) + } + } + return ( - {items?.slice(0, 3).map(item => { + {items?.map(item => { const name = getName(item.properties, item.layerType) const type = getType(item.properties, item.layerType) return ( - + {name} / {type} ) })} - {items?.length === 4 && 1 autre zone} - {items && items.length > 4 && {items.length - 2} autres zones} ) } const Layerlist = styled.ul` - min-width: 200px; - min-height: 100px; - border: 1px solid red; list-style: none; padding: 0; margin: 0; @@ -53,9 +61,3 @@ const Type = styled.span` color: ${p => p.theme.color.gunMetal}; font: normal normal normal 13px/18px Marianne; ` -const More = styled.li` - background-color: white; - color: ${p => p.theme.color.slateGray}; - font: italic normal bold 13px/18px Marianne; - padding: 5px; -` diff --git a/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx b/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx deleted file mode 100644 index 824a6c4a36..0000000000 --- a/frontend/src/features/layersSelector/overlays/OverlayMenu.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import styled from 'styled-components' - -import { OverlayContent } from './OverlayContent' - -import type { OverlayItem } from 'domain/types/map' - -export function OverlayMenu({ items, pixel }: { items: OverlayItem[]; pixel: number[] }) { - if (!pixel) { - return null - } - - const [x, y] = pixel - - return ( - - - - ) -} - -const Menu = styled.div<{ x: number | undefined; y: number | undefined }>` - position: absolute; - top: ${p => String(p.y ?? 0 + 10)}px; - left: ${p => String(p.x ?? 0 + 10)}px; - max-width: 440px; - box-shadow: 0px 2px 4px #707785bf; - pointer-events: none; -` diff --git a/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx b/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx index 4c5b5b2cc2..824ff46188 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayPositionOnCoordinate.tsx @@ -49,7 +49,7 @@ export function OverlayPositionOnCoordinates({ useEffect(() => { if (containerRef.current) { if (layerOverlayIsOpen) { - containerRef.current.style.display = 'flex' + containerRef.current.style.display = 'block' olOverlayRef.current?.setPosition(coordinates) } else { containerRef.current.style.display = 'none' diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx new file mode 100644 index 0000000000..e7966b352d --- /dev/null +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -0,0 +1,37 @@ +import { useAppDispatch } from '@hooks/useAppDispatch' +import { IconButton, Icon, Size, Accent } from '@mtes-mct/monitor-ui' +import styled from 'styled-components' + +import { OverlayContent } from './OverlayContent' +import { closeLayerOverlay } from '../metadataPanel/slice' + +import type { OverlayItem } from 'domain/types/map' + +export function PinnedOverlay({ items }: { items: OverlayItem[] }) { + const dispatch = useAppDispatch() + const handleClick = () => { + dispatch(closeLayerOverlay()) + } + + return ( + +
+ {items.length} zones superposées sur ce point{' '} + +
+ +
+ ) +} + +const Card = styled.div` + width: 440px; +` +const Header = styled.div` + display: flex; + justify-content: space-between; + background-color: ${p => p.theme.color.lightGray}; + color: ${p => p.theme.color.gunMetal}; + font: normal normal medium 13px/18px Marianne; + padding: 5px; +` diff --git a/frontend/src/features/layersSelector/overlays/index.tsx b/frontend/src/features/layersSelector/overlays/index.tsx index ca272aed6d..343d44adac 100644 --- a/frontend/src/features/layersSelector/overlays/index.tsx +++ b/frontend/src/features/layersSelector/overlays/index.tsx @@ -1,53 +1,34 @@ -import { getClickedAmpFeatures, getClickedItems, getClickedRegulatoryFeatures } from '@features/map/utils' -import { useAppDispatch } from '@hooks/useAppDispatch' -import { convertToFeature } from 'domain/types/map' -import { useEffect } from 'react' +import { getHoveredItems } from '@features/map/utils' +import { useAppSelector } from '@hooks/useAppSelector' +import { createPortal } from 'react-dom' -import { - closeLayerOverlay, - closeMetadataPanel, - openAMPMetadataPanel, - openLayerOverlay, - openRegulatoryMetadataPanel, - setLayerOverlayItems -} from '../metadataPanel/slice' +import { HoveredOverlay } from './HoveredOverlay' +import { OverlayPositionOnCoordinates } from './OverlayPositionOnCoordinate' +import { PinnedOverlay } from './PinnedOverlay' import type { BaseMapChildrenProps } from '@features/map/BaseMap' -export function LayerOverlay({ mapClickEvent }: BaseMapChildrenProps) { - const dispatch = useAppDispatch() - - useEffect(() => { - const clickedAmpFeatures = getClickedAmpFeatures(mapClickEvent) - const clickedRegulatoryFeatures = getClickedRegulatoryFeatures(mapClickEvent) - const numberOfClickedFeatures = (clickedAmpFeatures?.length ?? 0) + (clickedRegulatoryFeatures?.length ?? 0) - if (numberOfClickedFeatures === 0) { - dispatch(closeLayerOverlay()) - } - - if (numberOfClickedFeatures === 1 && clickedAmpFeatures && clickedAmpFeatures.length === 1) { - const feature = convertToFeature(clickedAmpFeatures[0]) - if (feature) { - const layerId = feature.get('id') - dispatch(openAMPMetadataPanel(layerId)) - } - } - - if (numberOfClickedFeatures === 1 && clickedRegulatoryFeatures && clickedRegulatoryFeatures.length === 1) { - const feature = convertToFeature(clickedRegulatoryFeatures[0]) - if (feature) { - const layerId = feature.get('id') - dispatch(openRegulatoryMetadataPanel(layerId)) - } - } - - if (numberOfClickedFeatures > 1 && mapClickEvent.coordinates) { - dispatch(closeMetadataPanel()) - dispatch(openLayerOverlay(mapClickEvent.coordinates)) - const items = getClickedItems(mapClickEvent) - dispatch(setLayerOverlayItems(items)) - } - }, [dispatch, mapClickEvent]) - - return null +export function LayersOverlay({ currentFeatureListOver, map, pixel }: BaseMapChildrenProps) { + const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector( + state => state.layersMetadata + ) + const hoveredItems = getHoveredItems(currentFeatureListOver) + + return ( + <> + + {layerOverlayIsOpen && } + + {createPortal( + !layerOverlayIsOpen && hoveredItems && hoveredItems.length > 0 && pixel && ( + + ), + document.body as HTMLElement + )} + + ) } diff --git a/frontend/src/features/map/index.tsx b/frontend/src/features/map/index.tsx index d3e108d626..995505cd00 100644 --- a/frontend/src/features/map/index.tsx +++ b/frontend/src/features/map/index.tsx @@ -1,5 +1,5 @@ -import { LayerOverlay } from '@features/layersSelector/overlays' -import { LayersOverlay } from '@features/layersSelector/overlays/LayersOverlay' +import { LayersOverlay } from '@features/layersSelector/overlays' +import { LayerEvents } from '@features/layersSelector/overlays/LayerEvents' import { BaseMap } from './BaseMap' import { MapAttributionsBox } from './controls/MapAttributionsBox' @@ -71,7 +71,7 @@ export function Map() { {/* @ts-ignore */} {/* @ts-ignore */} - + {/* @ts-ignore */} From a8b702d525b05a7b75629e0402921d576e29633a Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 03/19] fix style --- .../layersSelector/overlays/HoveredOverlay.tsx | 5 +++-- .../layersSelector/overlays/OverlayContent.tsx | 15 +++++++++++---- .../layersSelector/overlays/PinnedOverlay.tsx | 4 +++- .../layersSelector/utils/LayerLegend.style.tsx | 15 +++++++++------ frontend/src/features/map/layers/AMP/index.ts | 13 +++++++++++++ .../src/features/map/layers/Regulatory/index.ts | 4 +++- 6 files changed, 42 insertions(+), 14 deletions(-) diff --git a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx index 08a11f670f..47744d7e96 100644 --- a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx @@ -32,13 +32,14 @@ const Menu = styled.div<{ x: number | undefined; y: number | undefined }>` top: ${p => String(p.y ?? 0 + 10)}px; left: ${p => String(p.x ?? 0 + 10)}px; width: 440px; - box-shadow: 0px 2px 4px #707785bf; + box-shadow: 0px 2px 4px ${p => p.theme.color.slateGray}bf; pointer-events: none; ` const Footer = styled.div` - background-color: white; + background-color: ${p => p.theme.color.white}; color: ${p => p.theme.color.slateGray}; padding: 5px; + padding-left: 10px; ` const More = styled.span` font: italic normal bold 13px/18px Marianne; diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index ba6b117a00..a615132ba2 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -1,4 +1,5 @@ import { useAppDispatch } from '@hooks/useAppDispatch' +import { Size } from '@mtes-mct/monitor-ui' import { MonitorEnvLayers } from 'domain/entities/layers/constants' import styled from 'styled-components' @@ -31,7 +32,7 @@ export function OverlayContent({ items }: OverlayContentProps) { return ( - + {name} / {type} @@ -48,9 +49,15 @@ const Layerlist = styled.ul` ` const LayerItem = styled.li` - padding: 5px; - background-color: white; - border-bottom: 1px solid #cccfd6; ; + display: flex; + align-items: center; + height: 32px; + padding: 7px 8px 8px 8px; + background-color: ${p => p.theme.color.white}; + border-bottom: 1px solid ${p => p.theme.color.lightGray}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; ` const Name = styled.span` color: ${p => p.theme.color.gunMetal}; diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx index e7966b352d..fe37bae007 100644 --- a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -26,12 +26,14 @@ export function PinnedOverlay({ items }: { items: OverlayItem[] }) { const Card = styled.div` width: 440px; + box-shadow: 0px 2px 4px ${p => p.theme.color.slateGray}bf; ` const Header = styled.div` display: flex; + height: 32px; justify-content: space-between; background-color: ${p => p.theme.color.lightGray}; color: ${p => p.theme.color.gunMetal}; font: normal normal medium 13px/18px Marianne; - padding: 5px; + padding: 8px 7px; ` diff --git a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx index 9796d661b3..4b4e27bf06 100644 --- a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx +++ b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx @@ -1,3 +1,4 @@ +import { Size } from '@mtes-mct/monitor-ui' import styled from 'styled-components' import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' @@ -9,25 +10,27 @@ type LayerTypeEnum = MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV export function LayerLegend({ layerType, name, + size = Size.SMALL, type }: { layerType: LayerTypeEnum name: string | null + size?: Size type: string | null }) { switch (layerType) { case MonitorEnvLayers.AMP: - return + return case MonitorEnvLayers.REGULATORY_ENV: - return + return default: - return + return } } -const Rectangle = styled.div<{ $vectorLayerColor?: string }>` - width: 14px; - height: 14px; +const Rectangle = styled.div<{ $size: Size; $vectorLayerColor?: string }>` + width: ${p => (p.$size === Size.SMALL ? '14px' : '16px')}; + height: ${p => (p.$size === Size.SMALL ? '14px' : '16px')}; background: ${p => p.$vectorLayerColor ?? p.theme.color.gainsboro}; border: 1px solid ${p => p.theme.color.slateGray}; display: inline-block; diff --git a/frontend/src/features/map/layers/AMP/index.ts b/frontend/src/features/map/layers/AMP/index.ts index 488d5b01de..d9f625bee1 100644 --- a/frontend/src/features/map/layers/AMP/index.ts +++ b/frontend/src/features/map/layers/AMP/index.ts @@ -1,3 +1,4 @@ +import { getDisplayedMetadataAMPLayerId } from '@features/layersSelector/metadataPanel/slice' import GeoJSON from 'ol/format/GeoJSON' import { Vector } from 'ol/layer' import VectorSource from 'ol/source/Vector' @@ -18,6 +19,7 @@ export const metadataIsShowedPropertyName = 'metadataIsShowed' export function AMPLayers({ map }: BaseMapChildrenProps) { const showedAmpLayerIds = useAppSelector(state => state.amp.showedAmpLayerIds) + const shcwedAmpMetadataLayerId = useAppSelector(state => getDisplayedMetadataAMPLayerId(state)) const { data: ampLayers } = useGetAMPsQuery() @@ -75,5 +77,16 @@ export function AMPLayers({ map }: BaseMapChildrenProps) { } }, [map, ampLayers, showedAmpLayerIds]) + useEffect(() => { + if (map) { + const features = vectorSourceRef.current.getFeatures() + if (features?.length) { + features.forEach(f => { + f.set(metadataIsShowedPropertyName, f.get('id') === shcwedAmpMetadataLayerId) + }) + } + } + }, [map, shcwedAmpMetadataLayerId]) + return null } diff --git a/frontend/src/features/map/layers/Regulatory/index.ts b/frontend/src/features/map/layers/Regulatory/index.ts index 112595de01..d8d418dbb7 100644 --- a/frontend/src/features/map/layers/Regulatory/index.ts +++ b/frontend/src/features/map/layers/Regulatory/index.ts @@ -92,7 +92,9 @@ export function RegulatoryLayers({ map }: BaseMapChildrenProps) { if (map) { const features = getVectorSource().getFeatures() if (features?.length) { - features.forEach(f => f.set(metadataIsShowedPropertyName, f.get('layerId') === regulatoryMetadataLayerId)) + features.forEach(f => { + f.set(metadataIsShowedPropertyName, f.get('id') === regulatoryMetadataLayerId) + }) } } }, [map, regulatoryMetadataLayerId]) From d8169a36b528063039cdf2f63052c738fbca70a1 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 04/19] fix overlay on preview layers --- frontend/src/domain/entities/AMPs.ts | 2 +- .../src/domain/entities/layers/constants.ts | 13 +++++ frontend/src/domain/types/map.ts | 13 ++--- .../layersSelector/metadataPanel/slice.ts | 6 ++- .../overlays/HoveredOverlay.tsx | 11 ++++- .../overlays/OverlayContent.tsx | 24 ++++++---- .../layersSelector/overlays/PinnedOverlay.tsx | 9 +++- .../features/layersSelector/overlays/utils.ts | 20 ++++---- .../utils/LayerLegend.style.tsx | 8 ++-- frontend/src/features/map/BaseMap.tsx | 47 +++++++++---------- frontend/src/features/map/utils.ts | 21 ++++++--- 11 files changed, 106 insertions(+), 68 deletions(-) diff --git a/frontend/src/domain/entities/AMPs.ts b/frontend/src/domain/entities/AMPs.ts index 55530bf6b8..fb13a820d3 100644 --- a/frontend/src/domain/entities/AMPs.ts +++ b/frontend/src/domain/entities/AMPs.ts @@ -12,4 +12,4 @@ export type AMPFromAPI = { } export type AMP = AMPFromAPI & { bbox: Extent } -export type AMPPRoperties = Omit +export type AMPProperties = Omit diff --git a/frontend/src/domain/entities/layers/constants.ts b/frontend/src/domain/entities/layers/constants.ts index b6f0a41318..ca3bb52883 100644 --- a/frontend/src/domain/entities/layers/constants.ts +++ b/frontend/src/domain/entities/layers/constants.ts @@ -274,3 +274,16 @@ export const HoverableLayers = [ Layers.SEMAPHORES.code, Layers.STATIONS.code ] + +export type RegulatoryOrAMPLayerType = + | MonitorEnvLayers.AMP + | MonitorEnvLayers.AMP_PREVIEW + | MonitorEnvLayers.REGULATORY_ENV + | MonitorEnvLayers.REGULATORY_ENV_PREVIEW + +export const RegulatoryOrAMPLayerTypeAsList = [ + MonitorEnvLayers.AMP, + MonitorEnvLayers.AMP_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV, + MonitorEnvLayers.REGULATORY_ENV_PREVIEW +] diff --git a/frontend/src/domain/types/map.ts b/frontend/src/domain/types/map.ts index c0888719b4..863b5670d9 100644 --- a/frontend/src/domain/types/map.ts +++ b/frontend/src/domain/types/map.ts @@ -3,15 +3,12 @@ import { GeoJSON } from 'ol/format' import { OPENLAYERS_PROJECTION, type InteractionListener, type InteractionType } from '../entities/map/constants' -import type { AMPPRoperties } from 'domain/entities/AMPs' -import type { MonitorEnvLayers } from 'domain/entities/layers/constants' -import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { Coordinate } from 'ol/coordinate' import type { Geometry } from 'ol/geom' -export type OverlayItem = { - layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV - properties: AMPPRoperties | RegulatoryLayerCompactProperties +export type OverlayItem = { + layerType: T + properties: P } export type MapClickEvent = { coordinates: Coordinate | undefined @@ -25,10 +22,10 @@ export type InteractionTypeAndListener = { type: InteractionType } -export type SerializedFeature

= { +export type SerializedFeature = { geometry: Geometry id: string | number - properties: P + properties: T } export const convertToSerializedFeature =

( diff --git a/frontend/src/features/layersSelector/metadataPanel/slice.ts b/frontend/src/features/layersSelector/metadataPanel/slice.ts index 44957cd9e5..57f70039c4 100644 --- a/frontend/src/features/layersSelector/metadataPanel/slice.ts +++ b/frontend/src/features/layersSelector/metadataPanel/slice.ts @@ -1,16 +1,18 @@ import { createSelector, createSlice } from '@reduxjs/toolkit' import { includes } from 'lodash' -import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' +import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from '../../../domain/entities/layers/constants' import type { HomeRootState } from '@store/index' +import type { AMPProperties } from 'domain/entities/AMPs' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { OverlayItem } from 'domain/types/map' import type { Coordinate } from 'ol/coordinate' type MetadataPanelSliceState = { layerOverlayCoordinates: Coordinate | undefined layerOverlayIsOpen: boolean - layerOverlayItems: OverlayItem[] + layerOverlayItems: OverlayItem[] metadataLayerId: number | undefined metadataLayerType: MonitorEnvLayers.REGULATORY_ENV | MonitorEnvLayers.AMP | undefined metadataPanelIsOpen: boolean diff --git a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx index 47744d7e96..fb9a07896a 100644 --- a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx @@ -2,9 +2,18 @@ import styled from 'styled-components' import { OverlayContent } from './OverlayContent' +import type { AMPProperties } from 'domain/entities/AMPs' +import type { RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { OverlayItem } from 'domain/types/map' -export function HoveredOverlay({ items, pixel }: { items: OverlayItem[]; pixel: number[] }) { +export function HoveredOverlay({ + items, + pixel +}: { + items: OverlayItem[] + pixel: number[] +}) { if (!pixel) { return null } diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index a615132ba2..b307adbc6d 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -1,25 +1,27 @@ import { useAppDispatch } from '@hooks/useAppDispatch' import { Size } from '@mtes-mct/monitor-ui' -import { MonitorEnvLayers } from 'domain/entities/layers/constants' +import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' import styled from 'styled-components' import { getName, getType } from './utils' import { openAMPMetadataPanel, openRegulatoryMetadataPanel } from '../metadataPanel/slice' import { LayerLegend } from '../utils/LayerLegend.style' +import type { AMPProperties } from 'domain/entities/AMPs' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { OverlayItem } from 'domain/types/map' type OverlayContentProps = { - items: OverlayItem[] | undefined + items: OverlayItem[] | undefined } export function OverlayContent({ items }: OverlayContentProps) { const dispatch = useAppDispatch() const handleClick = (type, id) => () => { - if (type === MonitorEnvLayers.AMP) { + if (type === MonitorEnvLayers.AMP || type === MonitorEnvLayers.AMP_PREVIEW) { dispatch(openAMPMetadataPanel(id)) } - if (type === MonitorEnvLayers.REGULATORY_ENV) { + if (type === MonitorEnvLayers.REGULATORY_ENV || type === MonitorEnvLayers.REGULATORY_ENV_PREVIEW) { dispatch(openRegulatoryMetadataPanel(id)) } } @@ -33,8 +35,8 @@ export function OverlayContent({ items }: OverlayContentProps) { return ( - {name} - / {type} + {name} + / {type} ) })} @@ -55,16 +57,20 @@ const LayerItem = styled.li` padding: 7px 8px 8px 8px; background-color: ${p => p.theme.color.white}; border-bottom: 1px solid ${p => p.theme.color.lightGray}; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; ` + const Name = styled.span` color: ${p => p.theme.color.gunMetal}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; font: normal normal bold 13px/18px Marianne; ` const Type = styled.span` color: ${p => p.theme.color.gunMetal}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; font: normal normal normal 13px/18px Marianne; ` diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx index fe37bae007..c62c281974 100644 --- a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -5,9 +5,16 @@ import styled from 'styled-components' import { OverlayContent } from './OverlayContent' import { closeLayerOverlay } from '../metadataPanel/slice' +import type { AMPProperties } from 'domain/entities/AMPs' +import type { RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' +import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { OverlayItem } from 'domain/types/map' -export function PinnedOverlay({ items }: { items: OverlayItem[] }) { +export function PinnedOverlay({ + items +}: { + items: OverlayItem[] +}) { const dispatch = useAppDispatch() const handleClick = () => { dispatch(closeLayerOverlay()) diff --git a/frontend/src/features/layersSelector/overlays/utils.ts b/frontend/src/features/layersSelector/overlays/utils.ts index ddac2e37e3..e09a7ee857 100644 --- a/frontend/src/features/layersSelector/overlays/utils.ts +++ b/frontend/src/features/layersSelector/overlays/utils.ts @@ -1,25 +1,25 @@ -import { MonitorEnvLayers } from 'domain/entities/layers/constants' +import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' -import type { AMPPRoperties } from 'domain/entities/AMPs' +import type { AMPProperties } from 'domain/entities/AMPs' import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' export const getName = ( - layer: AMPPRoperties | RegulatoryLayerCompactProperties, - layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV + layer: AMPProperties | RegulatoryLayerCompactProperties, + layerType: RegulatoryOrAMPLayerType ) => { - if (layerType === MonitorEnvLayers.AMP) { - return (layer as AMPPRoperties).name + if (layerType === MonitorEnvLayers.AMP || layerType === MonitorEnvLayers.AMP_PREVIEW) { + return (layer as AMPProperties).name } return (layer as RegulatoryLayerCompactProperties).entity_name } export const getType = ( - layer: AMPPRoperties | RegulatoryLayerCompactProperties, - layerType: MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV + layer: AMPProperties | RegulatoryLayerCompactProperties, + layerType: RegulatoryOrAMPLayerType ) => { - if (layerType === MonitorEnvLayers.AMP) { - return (layer as AMPPRoperties).type + if (layerType === MonitorEnvLayers.AMP || layerType === MonitorEnvLayers.AMP_PREVIEW) { + return (layer as AMPProperties).type } return (layer as RegulatoryLayerCompactProperties).thematique diff --git a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx index 4b4e27bf06..db5a1e1420 100644 --- a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx +++ b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx @@ -1,27 +1,27 @@ import { Size } from '@mtes-mct/monitor-ui' import styled from 'styled-components' -import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' +import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from '../../../domain/entities/layers/constants' import { getAMPColorWithAlpha } from '../../map/layers/AMP/AMPLayers.style' import { getRegulatoryEnvColorWithAlpha } from '../../map/layers/styles/administrativeAndRegulatoryLayers.style' -type LayerTypeEnum = MonitorEnvLayers.AMP | MonitorEnvLayers.REGULATORY_ENV - export function LayerLegend({ layerType, name, size = Size.SMALL, type }: { - layerType: LayerTypeEnum + layerType: RegulatoryOrAMPLayerType name: string | null size?: Size type: string | null }) { switch (layerType) { case MonitorEnvLayers.AMP: + case MonitorEnvLayers.AMP_PREVIEW: return case MonitorEnvLayers.REGULATORY_ENV: + case MonitorEnvLayers.REGULATORY_ENV_PREVIEW: return default: return diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index a63cfe4086..4294c54df6 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -70,32 +70,29 @@ export function BaseMap({ children }: { children: Array, current_map: OpenLayerMap) => { - if (event && current_map) { - const featureList = current_map.getFeaturesAtPixel(event.pixel, { - hitTolerance: HIT_PIXEL_TO_TOLERANCE, - layerFilter: layer => { - const typedLayer = layer as VectorLayerWithName + const handleMapClick = useCallback((event: MapBrowserEvent, current_map: OpenLayerMap) => { + if (event && current_map) { + const featureList = current_map.getFeaturesAtPixel(event.pixel, { + hitTolerance: HIT_PIXEL_TO_TOLERANCE, + layerFilter: layer => { + const typedLayer = layer as VectorLayerWithName - const layerName = typedLayer.name ?? typedLayer.get('name') + const layerName = typedLayer.name ?? typedLayer.get('name') - return !!layerName && SelectableLayers.includes(layerName) - } - }) - const feature = getGeoJSONFromFeature>(featureList?.[0]) - const featuresAsGeoJSON = getGeoJSONFromFeatureList(featureList) - const isCtrl = platformModifierKeyOnly(event) - setMapClickEvent({ - coordinates: event.coordinate, - ctrlKeyPressed: isCtrl, - feature, - featureList: featuresAsGeoJSON - }) - } - }, - [setMapClickEvent] - ) + return !!layerName && SelectableLayers.includes(layerName) + } + }) + const feature = getGeoJSONFromFeature>(featureList?.[0]) + const featuresAsGeoJSON = getGeoJSONFromFeatureList(featureList) + const isCtrl = platformModifierKeyOnly(event) + setMapClickEvent({ + coordinates: event.coordinate, + ctrlKeyPressed: isCtrl, + feature, + featureList: featuresAsGeoJSON + }) + } + }, []) const handleMouseOverFeature = useMemo( () => @@ -118,7 +115,7 @@ export function BaseMap({ children }: { children: Array() diff --git a/frontend/src/features/map/utils.ts b/frontend/src/features/map/utils.ts index 589ed66bc3..ce8a0edbc1 100644 --- a/frontend/src/features/map/utils.ts +++ b/frontend/src/features/map/utils.ts @@ -1,8 +1,13 @@ // selectors taht will be moved to the slice -import { Layers, MonitorEnvLayers } from 'domain/entities/layers/constants' - -import type { AMPPRoperties } from 'domain/entities/AMPs' +import { + Layers, + MonitorEnvLayers, + type RegulatoryOrAMPLayerType, + RegulatoryOrAMPLayerTypeAsList +} from 'domain/entities/layers/constants' + +import type { AMPProperties } from 'domain/entities/AMPs' import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { MapClickEvent, OverlayItem, SerializedFeature } from 'domain/types/map' @@ -22,18 +27,20 @@ export const getClickedAmpFeatures = (mapClickEvent: MapClickEvent) => return featureId && (featureId.includes(Layers.AMP_PREVIEW.code) || featureId.includes(Layers.AMP.code)) }) - export const getOverlayItemsFromFeatures = (features: SerializedFeature>[] | undefined) => features?.reduce((acc, feature) => { const type = String(feature.id).split(':')[0] - if (type === MonitorEnvLayers.AMP || type === MonitorEnvLayers.REGULATORY_ENV) { + if (RegulatoryOrAMPLayerTypeAsList.includes(type as MonitorEnvLayers)) { const { geometry, ...properties } = feature.properties - acc.push({ layerType: type, properties: properties as AMPPRoperties | RegulatoryLayerCompactProperties }) + acc.push({ + layerType: type as RegulatoryOrAMPLayerType, + properties: properties as AMPProperties | RegulatoryLayerCompactProperties + }) } return acc - }, [] as OverlayItem[]) + }, [] as OverlayItem[]) export const getClickedItems = (mapClickEvent: MapClickEvent) => getOverlayItemsFromFeatures(mapClickEvent?.featureList) From 3d9fb1d80837dde4d7194857b0bb5c462f4638ef Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 05/19] fix display search result in preview if layer is selected but not shown --- frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts b/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts index e196c37132..0b1ede21f6 100644 --- a/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts +++ b/frontend/src/features/map/layers/AMP/AMPPreviewLayer.ts @@ -24,7 +24,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { const ampsSearchResult = useAppSelector(state => state.layerSearch.ampsSearchResult) const isAmpSearchResultsVisible = useAppSelector(state => state.layerSearch.isAmpSearchResultsVisible) const searchExtent = useAppSelector(state => state.layerSearch.searchExtent) - const myAmpLayerIds = useAppSelector(state => state.amp.selectedAmpLayerIds) // or showedAmpLayerIds ? + const showedAmpLayerIds = useAppSelector(state => state.amp.showedAmpLayerIds) const { data: ampLayers } = useGetAMPsQuery() const { isLayersSidebarVisible } = useAppSelector(state => state.global) @@ -50,7 +50,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { ampVectorSourceRef.current.clear() if (ampsSearchResult && ampLayers?.entities) { const features = ampsSearchResult.reduce((amplayers, id) => { - if (myAmpLayerIds.includes(id)) { + if (showedAmpLayerIds.includes(id)) { return amplayers } const layer = ampLayers.entities[id] @@ -90,7 +90,7 @@ export function AMPPreviewLayer({ map }: BaseMapChildrenProps) { refreshPreviewLayer() }, 300) } - }, [map, ampsSearchResult, ampLayers, myAmpLayerIds]) + }, [map, ampsSearchResult, ampLayers, showedAmpLayerIds]) useEffect(() => { function getLayer() { From 5046c03bcc5157a3925028e09862b0c7b9cf2710 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 06/19] fix after review --- .../features/layersSelector/overlays/HoveredOverlay.tsx | 4 ++-- .../features/layersSelector/overlays/PinnedOverlay.tsx | 1 + frontend/src/features/layersSelector/overlays/index.tsx | 8 +++++--- frontend/src/features/map/layers/AMP/index.ts | 6 +++--- frontend/src/features/map/layers/Regulatory/index.ts | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx index fb9a07896a..1410a5899e 100644 --- a/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/HoveredOverlay.tsx @@ -38,8 +38,8 @@ export function HoveredOverlay({ const Menu = styled.div<{ x: number | undefined; y: number | undefined }>` position: absolute; - top: ${p => String(p.y ?? 0 + 10)}px; - left: ${p => String(p.x ?? 0 + 10)}px; + top: ${p => String(p.y)}px; + left: ${p => String(p.x)}px; width: 440px; box-shadow: 0px 2px 4px ${p => p.theme.color.slateGray}bf; pointer-events: none; diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx index c62c281974..fb3e57594d 100644 --- a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -39,6 +39,7 @@ const Header = styled.div` display: flex; height: 32px; justify-content: space-between; + align-items: center; background-color: ${p => p.theme.color.lightGray}; color: ${p => p.theme.color.gunMetal}; font: normal normal medium 13px/18px Marianne; diff --git a/frontend/src/features/layersSelector/overlays/index.tsx b/frontend/src/features/layersSelector/overlays/index.tsx index 343d44adac..cd8a353ac1 100644 --- a/frontend/src/features/layersSelector/overlays/index.tsx +++ b/frontend/src/features/layersSelector/overlays/index.tsx @@ -9,9 +9,11 @@ import { PinnedOverlay } from './PinnedOverlay' import type { BaseMapChildrenProps } from '@features/map/BaseMap' export function LayersOverlay({ currentFeatureListOver, map, pixel }: BaseMapChildrenProps) { - const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector( - state => state.layersMetadata - ) + const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector(state => ({ + layerOverlayCoordinates: state.layersMetadata.layerOverlayCoordinates, + layerOverlayIsOpen: state.layersMetadata.layerOverlayIsOpen, + layerOverlayItems: state.layersMetadata.layerOverlayItems + })) const hoveredItems = getHoveredItems(currentFeatureListOver) return ( diff --git a/frontend/src/features/map/layers/AMP/index.ts b/frontend/src/features/map/layers/AMP/index.ts index d9f625bee1..29125c2869 100644 --- a/frontend/src/features/map/layers/AMP/index.ts +++ b/frontend/src/features/map/layers/AMP/index.ts @@ -19,7 +19,7 @@ export const metadataIsShowedPropertyName = 'metadataIsShowed' export function AMPLayers({ map }: BaseMapChildrenProps) { const showedAmpLayerIds = useAppSelector(state => state.amp.showedAmpLayerIds) - const shcwedAmpMetadataLayerId = useAppSelector(state => getDisplayedMetadataAMPLayerId(state)) + const showedAmpMetadataLayerId = useAppSelector(state => getDisplayedMetadataAMPLayerId(state)) const { data: ampLayers } = useGetAMPsQuery() @@ -82,11 +82,11 @@ export function AMPLayers({ map }: BaseMapChildrenProps) { const features = vectorSourceRef.current.getFeatures() if (features?.length) { features.forEach(f => { - f.set(metadataIsShowedPropertyName, f.get('id') === shcwedAmpMetadataLayerId) + f.set(metadataIsShowedPropertyName, f.get('id') === showedAmpMetadataLayerId) }) } } - }, [map, shcwedAmpMetadataLayerId]) + }, [map, showedAmpMetadataLayerId]) return null } diff --git a/frontend/src/features/map/layers/Regulatory/index.ts b/frontend/src/features/map/layers/Regulatory/index.ts index d8d418dbb7..04c39a9ad2 100644 --- a/frontend/src/features/map/layers/Regulatory/index.ts +++ b/frontend/src/features/map/layers/Regulatory/index.ts @@ -92,8 +92,8 @@ export function RegulatoryLayers({ map }: BaseMapChildrenProps) { if (map) { const features = getVectorSource().getFeatures() if (features?.length) { - features.forEach(f => { - f.set(metadataIsShowedPropertyName, f.get('id') === regulatoryMetadataLayerId) + features.forEach(feature => { + feature.set(metadataIsShowedPropertyName, feature.get('id') === regulatoryMetadataLayerId) }) } } From 918c46c6c8fd4ad739d6dbcc3bcb13d90d5b1dfb Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 07/19] add early return + comment to PinnedOverlay --- .../src/features/layersSelector/overlays/PinnedOverlay.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx index fb3e57594d..7eba9f1b3f 100644 --- a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -19,6 +19,10 @@ export function PinnedOverlay({ const handleClick = () => { dispatch(closeLayerOverlay()) } + // component should not be called if items.length < 2 + if (items.length < 2) { + return null + } return ( From 0171466905fc96b547011cc0184a949dd9dc2307 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 08/19] change cursor type and prevent text selection on layer popup --- .../src/features/layersSelector/overlays/PinnedOverlay.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx index 7eba9f1b3f..a596a36f95 100644 --- a/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx +++ b/frontend/src/features/layersSelector/overlays/PinnedOverlay.tsx @@ -38,6 +38,10 @@ export function PinnedOverlay({ const Card = styled.div` width: 440px; box-shadow: 0px 2px 4px ${p => p.theme.color.slateGray}bf; + cursor: pointer; + > * { + user-select: none; + } ` const Header = styled.div` display: flex; From ed305b9ee5e3af45bba879c3dfda9d02e9f54cc6 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 09/19] close layer popup when clicking on a single layer --- frontend/src/features/layersSelector/overlays/LayerEvents.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/features/layersSelector/overlays/LayerEvents.tsx b/frontend/src/features/layersSelector/overlays/LayerEvents.tsx index 037b888b2c..34ce8b5781 100644 --- a/frontend/src/features/layersSelector/overlays/LayerEvents.tsx +++ b/frontend/src/features/layersSelector/overlays/LayerEvents.tsx @@ -26,6 +26,7 @@ export function LayerEvents({ mapClickEvent }: BaseMapChildrenProps) { } if (numberOfClickedFeatures === 1 && clickedAmpFeatures && clickedAmpFeatures.length === 1) { + dispatch(closeLayerOverlay()) const feature = convertToFeature(clickedAmpFeatures[0]) if (feature) { const layerId = feature.get('id') From ba98b5324383542c531520b30b0aee6a9df558a2 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 10/19] scroll for more than 10 items + higlight selected layer --- .../layersSelector/metadataPanel/slice.ts | 5 +++++ .../overlays/OverlayContent.tsx | 22 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/frontend/src/features/layersSelector/metadataPanel/slice.ts b/frontend/src/features/layersSelector/metadataPanel/slice.ts index 57f70039c4..4ecad81f05 100644 --- a/frontend/src/features/layersSelector/metadataPanel/slice.ts +++ b/frontend/src/features/layersSelector/metadataPanel/slice.ts @@ -104,3 +104,8 @@ export const getDisplayedMetadataRegulatoryLayerId = createSelector( [isMetadataPanelOpen, getMetadataLayerType, getMetadataLayerId], (isOpen, layerType, layerId) => (isOpen && layerType === MonitorEnvLayers.REGULATORY_ENV ? layerId : undefined) ) + +export const getDisplayedMetadataLayerIdAndType = createSelector( + [getMetadataLayerType, getMetadataLayerId], + (layerType, layerId) => ({ layerId, layerType }) +) diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index b307adbc6d..f71f48442d 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -1,10 +1,15 @@ import { useAppDispatch } from '@hooks/useAppDispatch' +import { useAppSelector } from '@hooks/useAppSelector' import { Size } from '@mtes-mct/monitor-ui' import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' import styled from 'styled-components' import { getName, getType } from './utils' -import { openAMPMetadataPanel, openRegulatoryMetadataPanel } from '../metadataPanel/slice' +import { + getDisplayedMetadataLayerIdAndType, + openAMPMetadataPanel, + openRegulatoryMetadataPanel +} from '../metadataPanel/slice' import { LayerLegend } from '../utils/LayerLegend.style' import type { AMPProperties } from 'domain/entities/AMPs' @@ -17,6 +22,8 @@ type OverlayContentProps = { export function OverlayContent({ items }: OverlayContentProps) { const dispatch = useAppDispatch() + const { layerId, layerType } = useAppSelector(state => getDisplayedMetadataLayerIdAndType(state)) + const handleClick = (type, id) => () => { if (type === MonitorEnvLayers.AMP || type === MonitorEnvLayers.AMP_PREVIEW) { dispatch(openAMPMetadataPanel(id)) @@ -31,9 +38,14 @@ export function OverlayContent({ items }: OverlayContentProps) { {items?.map(item => { const name = getName(item.properties, item.layerType) const type = getType(item.properties, item.layerType) + const isSelected = item.properties.id === layerId && item.layerType === layerType return ( - + {name} / {type} @@ -48,14 +60,16 @@ const Layerlist = styled.ul` list-style: none; padding: 0; margin: 0; + max-height: 320px; + overflow-y: auto; ` -const LayerItem = styled.li` +const LayerItem = styled.li<{ $isSelected: boolean }>` display: flex; align-items: center; height: 32px; padding: 7px 8px 8px 8px; - background-color: ${p => p.theme.color.white}; + background-color: ${p => (p.$isSelected ? p.theme.color.blueYonder25 : p.theme.color.white)}; border-bottom: 1px solid ${p => p.theme.color.lightGray}; ` From dfb87fec7f9cc3f76b2f1ebfcfbb80e566cd60e0 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 11/19] handle layer priority --- .../src/domain/entities/layers/constants.ts | 32 +++++++++++-------- frontend/src/features/map/BaseMap.tsx | 15 ++++++--- frontend/src/features/map/utils.ts | 15 +++++++++ 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/frontend/src/domain/entities/layers/constants.ts b/frontend/src/domain/entities/layers/constants.ts index ca3bb52883..839db847d4 100644 --- a/frontend/src/domain/entities/layers/constants.ts +++ b/frontend/src/domain/entities/layers/constants.ts @@ -250,29 +250,35 @@ export const BaseLayerLabel: Record = { [BaseLayer.SHOM]: 'Carte marine (SHOM)' } +// Priority of selectable items is determined by the order of the layers in this array +// The first layer in the array has the highest priority export const SelectableLayers = [ Layers.MISSIONS.code, - Layers.REGULATORY_ENV_PREVIEW.code, - Layers.REGULATORY_ENV.code, - Layers.AMP.code, - Layers.STATIONS.code, - Layers.SEMAPHORES.code, - Layers.REPORTINGS.code, Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code + Layers.REPORTING_TO_ATTACH_ON_MISSION.code, + Layers.REPORTINGS.code, + Layers.SEMAPHORES.code, + Layers.STATIONS.code, + Layers.AMP.code, + Layers.AMP_PREVIEW.code, + Layers.REGULATORY_ENV_PREVIEW.code, + Layers.REGULATORY_ENV.code ] + +// Priority of hoverable items is determined by the order of the layers in this array +// The first layer in the array has the highest priority export const HoverableLayers = [ Layers.ACTIONS.code, - Layers.AMP.code, - Layers.AMP_PREVIEW.code, - Layers.MISSION_TO_ATTACH_ON_REPORTING.code, Layers.MISSIONS.code, - Layers.REGULATORY_ENV_PREVIEW.code, - Layers.REGULATORY_ENV.code, + Layers.MISSION_TO_ATTACH_ON_REPORTING.code, Layers.REPORTING_TO_ATTACH_ON_MISSION.code, Layers.REPORTINGS.code, Layers.SEMAPHORES.code, - Layers.STATIONS.code + Layers.STATIONS.code, + Layers.AMP.code, + Layers.AMP_PREVIEW.code, + Layers.REGULATORY_ENV_PREVIEW.code, + Layers.REGULATORY_ENV.code ] export type RegulatoryOrAMPLayerType = diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index 4294c54df6..34f121efdd 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -25,6 +25,7 @@ import { } from 'react' import styled from 'styled-components' +import { getHighestPriorityFeatures } from './utils' import { HIT_PIXEL_TO_TOLERANCE } from '../../constants' import { SelectableLayers, HoverableLayers } from '../../domain/entities/layers/constants' import { DistanceUnit, OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../domain/entities/map/constants' @@ -72,7 +73,7 @@ export function BaseMap({ children }: { children: Array, current_map: OpenLayerMap) => { if (event && current_map) { - const featureList = current_map.getFeaturesAtPixel(event.pixel, { + const features = current_map.getFeaturesAtPixel(event.pixel, { hitTolerance: HIT_PIXEL_TO_TOLERANCE, layerFilter: layer => { const typedLayer = layer as VectorLayerWithName @@ -82,8 +83,10 @@ export function BaseMap({ children }: { children: Array>(featureList?.[0]) - const featuresAsGeoJSON = getGeoJSONFromFeatureList(featureList) + const priorityFeatures = getHighestPriorityFeatures(features, SelectableLayers) + + const feature = getGeoJSONFromFeature>(priorityFeatures?.[0]) + const featuresAsGeoJSON = getGeoJSONFromFeatureList(priorityFeatures) const isCtrl = platformModifierKeyOnly(event) setMapClickEvent({ coordinates: event.coordinate, @@ -108,9 +111,11 @@ export function BaseMap({ children }: { children: Array mapClickEvent.featureList?.filter(feature => { @@ -46,3 +47,17 @@ export const getClickedItems = (mapClickEvent: MapClickEvent) => getOverlayItems export const getHoveredItems = (features: SerializedFeature>[] | undefined) => getOverlayItemsFromFeatures(features) + +export const getHighestPriorityFeatures = (features: FeatureLike[], priorityOrderTypes: string[]) => { + const highestPriorityFeatureType = priorityOrderTypes.find(layerType => + features.some(feature => String(feature.getId()).includes(layerType)) + ) + if (!highestPriorityFeatureType) { + return [] + } + const highestPriorityFeatures = features.filter(feature => + String(feature.getId()).includes(highestPriorityFeatureType) + ) + + return highestPriorityFeatures +} From 7f5f624f1d713933412cc051b9498065f34bba9b Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Fri, 12 Apr 2024 17:01:06 +0200 Subject: [PATCH 12/19] adjust hoverable/selectable layers on zoomlevel --- .../src/domain/entities/layers/constants.ts | 32 +++++++++++++++++-- frontend/src/features/map/BaseMap.tsx | 30 ++++++++++++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/frontend/src/domain/entities/layers/constants.ts b/frontend/src/domain/entities/layers/constants.ts index 839db847d4..38ff126216 100644 --- a/frontend/src/domain/entities/layers/constants.ts +++ b/frontend/src/domain/entities/layers/constants.ts @@ -252,7 +252,22 @@ export const BaseLayerLabel: Record = { // Priority of selectable items is determined by the order of the layers in this array // The first layer in the array has the highest priority -export const SelectableLayers = [ +// Different hoverable arrays are used depending on the zoom level +// Zoom level goes from 0 (furthest from earth) to 26 (closest from earth) +export const SelectableLayers0To7 = [ + Layers.MISSIONS.code, + Layers.MISSION_TO_ATTACH_ON_REPORTING.code, + Layers.REPORTING_TO_ATTACH_ON_MISSION.code, + Layers.REPORTINGS.code, + Layers.SEMAPHORES.code, + Layers.STATIONS.code, + Layers.AMP.code, + Layers.AMP_PREVIEW.code, + Layers.REGULATORY_ENV_PREVIEW.code, + Layers.REGULATORY_ENV.code +] + +export const SelectableLayers7To26 = [ Layers.MISSIONS.code, Layers.MISSION_TO_ATTACH_ON_REPORTING.code, Layers.REPORTING_TO_ATTACH_ON_MISSION.code, @@ -267,7 +282,20 @@ export const SelectableLayers = [ // Priority of hoverable items is determined by the order of the layers in this array // The first layer in the array has the highest priority -export const HoverableLayers = [ +// Different hoverable arrays are used depending on the zoom level +// Zoom level goes from 0 (furthest from earth) to 26 (closest from earth) + +export const HoverableLayers0To7 = [ + Layers.ACTIONS.code, + Layers.MISSIONS.code, + Layers.MISSION_TO_ATTACH_ON_REPORTING.code, + Layers.REPORTING_TO_ATTACH_ON_MISSION.code, + Layers.REPORTINGS.code, + Layers.SEMAPHORES.code, + Layers.STATIONS.code +] + +export const HoverableLayers7To26 = [ Layers.ACTIONS.code, Layers.MISSIONS.code, Layers.MISSION_TO_ATTACH_ON_REPORTING.code, diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index 34f121efdd..234078d24f 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -27,7 +27,12 @@ import styled from 'styled-components' import { getHighestPriorityFeatures } from './utils' import { HIT_PIXEL_TO_TOLERANCE } from '../../constants' -import { SelectableLayers, HoverableLayers } from '../../domain/entities/layers/constants' +import { + HoverableLayers0To7, + HoverableLayers7To26, + SelectableLayers0To7, + SelectableLayers7To26 +} from '../../domain/entities/layers/constants' import { DistanceUnit, OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../domain/entities/map/constants' import { setDistanceUnit } from '../../domain/shared_slices/Map' import { updateMeasurementsWithNewDistanceUnit } from '../../domain/use_cases/map/updateMeasurementsWithNewDistanceUnit' @@ -73,6 +78,13 @@ export function BaseMap({ children }: { children: Array, current_map: OpenLayerMap) => { if (event && current_map) { + const zoomLevel = current_map.getView().getZoom() + if (!zoomLevel) { + return + } + + const priorityLayersOrder = zoomLevel < 7 ? SelectableLayers0To7 : SelectableLayers7To26 + const features = current_map.getFeaturesAtPixel(event.pixel, { hitTolerance: HIT_PIXEL_TO_TOLERANCE, layerFilter: layer => { @@ -80,10 +92,11 @@ export function BaseMap({ children }: { children: Array>(priorityFeatures?.[0]) const featuresAsGeoJSON = getGeoJSONFromFeatureList(priorityFeatures) @@ -101,6 +114,13 @@ export function BaseMap({ children }: { children: Array throttle((event: MapBrowserEvent, current_map: OpenLayerMap) => { if (event && current_map) { + const zoomLevel = current_map.getView().getZoom() + if (!zoomLevel) { + return + } + + const priorityLayersOrder = zoomLevel < 7 ? HoverableLayers0To7 : HoverableLayers7To26 + const features = current_map.getFeaturesAtPixel(event.pixel, { hitTolerance: HIT_PIXEL_TO_TOLERANCE, layerFilter: layer => { @@ -108,10 +128,10 @@ export function BaseMap({ children }: { children: Array Date: Fri, 12 Apr 2024 17:21:17 +0200 Subject: [PATCH 13/19] add useShallowEqualSelector --- frontend/src/features/layersSelector/overlays/index.tsx | 4 ++-- frontend/src/hooks/useAppSelector.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/features/layersSelector/overlays/index.tsx b/frontend/src/features/layersSelector/overlays/index.tsx index cd8a353ac1..7c284b805e 100644 --- a/frontend/src/features/layersSelector/overlays/index.tsx +++ b/frontend/src/features/layersSelector/overlays/index.tsx @@ -1,5 +1,5 @@ import { getHoveredItems } from '@features/map/utils' -import { useAppSelector } from '@hooks/useAppSelector' +import { useShallowEqualSelector } from '@hooks/useAppSelector' import { createPortal } from 'react-dom' import { HoveredOverlay } from './HoveredOverlay' @@ -9,7 +9,7 @@ import { PinnedOverlay } from './PinnedOverlay' import type { BaseMapChildrenProps } from '@features/map/BaseMap' export function LayersOverlay({ currentFeatureListOver, map, pixel }: BaseMapChildrenProps) { - const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useAppSelector(state => ({ + const { layerOverlayCoordinates, layerOverlayIsOpen, layerOverlayItems } = useShallowEqualSelector(state => ({ layerOverlayCoordinates: state.layersMetadata.layerOverlayCoordinates, layerOverlayIsOpen: state.layersMetadata.layerOverlayIsOpen, layerOverlayItems: state.layersMetadata.layerOverlayItems diff --git a/frontend/src/hooks/useAppSelector.ts b/frontend/src/hooks/useAppSelector.ts index b727ae4071..32d6224df1 100644 --- a/frontend/src/hooks/useAppSelector.ts +++ b/frontend/src/hooks/useAppSelector.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-restricted-imports */ -import { useSelector } from 'react-redux' +import { shallowEqual, useSelector } from 'react-redux' import type { HomeRootState } from '../store' import type { TypedUseSelectorHook } from 'react-redux' @@ -8,3 +8,8 @@ import type { TypedUseSelectorHook } from 'react-redux' * @see https://react-redux.js.org/using-react-redux/usage-with-typescript#typing-the-useselector-hook */ export const useAppSelector: TypedUseSelectorHook = useSelector + +// https://react-redux.js.org/api/hooks#recipe-useshallowequalselector +export const useShallowEqualSelector: TypedUseSelectorHook = ( + selector: (state: HomeRootState) => T +): T => useSelector(selector, shallowEqual) From 9b91b2a572f47ee369ad4d49cc46973af8c5a97f Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Mon, 15 Apr 2024 12:39:07 +0200 Subject: [PATCH 14/19] fix layer priority on multiple layers + displayed layer name --- .../src/domain/entities/layers/constants.ts | 82 ++++++++++--------- .../entities/layers}/utils.ts | 26 +++++- frontend/src/domain/entities/regulatory.ts | 2 - .../metadataPanel/ampMetadata/index.tsx | 8 +- .../regulatoryMetadata/index.tsx | 4 +- .../layersSelector/myAmps/MyAMPLayerZone.tsx | 3 +- .../MyRegulatoryLayerZone.tsx | 4 +- .../overlays/OverlayContent.tsx | 20 +++-- .../ResultsList/AMPLayerGroup/AMPLayer.tsx | 2 +- .../RegulatoryLayerGroup/RegulatoryLayer.tsx | 2 +- .../ResultsList/ResultListLayerGroup.tsx | 3 +- .../utils/LayerLegend.style.tsx | 10 +-- .../layersSelector/utils/MyLayerZone.tsx | 2 +- frontend/src/features/map/BaseMap.tsx | 4 +- frontend/src/features/map/utils.ts | 12 +-- 15 files changed, 110 insertions(+), 74 deletions(-) rename frontend/src/{features/layersSelector/overlays => domain/entities/layers}/utils.ts (52%) diff --git a/frontend/src/domain/entities/layers/constants.ts b/frontend/src/domain/entities/layers/constants.ts index 38ff126216..0f7b36da65 100644 --- a/frontend/src/domain/entities/layers/constants.ts +++ b/frontend/src/domain/entities/layers/constants.ts @@ -255,29 +255,33 @@ export const BaseLayerLabel: Record = { // Different hoverable arrays are used depending on the zoom level // Zoom level goes from 0 (furthest from earth) to 26 (closest from earth) export const SelectableLayers0To7 = [ - Layers.MISSIONS.code, - Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code, - Layers.REPORTINGS.code, - Layers.SEMAPHORES.code, - Layers.STATIONS.code, - Layers.AMP.code, - Layers.AMP_PREVIEW.code, - Layers.REGULATORY_ENV_PREVIEW.code, - Layers.REGULATORY_ENV.code + [MonitorEnvLayers.MISSIONS], + [MonitorEnvLayers.MISSION_TO_ATTACH_ON_REPORTING], + [MonitorEnvLayers.REPORTING_TO_ATTACH_ON_MISSION], + [MonitorEnvLayers.REPORTINGS], + [MonitorEnvLayers.SEMAPHORES], + [MonitorEnvLayers.STATIONS], + [ + MonitorEnvLayers.AMP, + MonitorEnvLayers.AMP_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV + ] ] export const SelectableLayers7To26 = [ - Layers.MISSIONS.code, - Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code, - Layers.REPORTINGS.code, - Layers.SEMAPHORES.code, - Layers.STATIONS.code, - Layers.AMP.code, - Layers.AMP_PREVIEW.code, - Layers.REGULATORY_ENV_PREVIEW.code, - Layers.REGULATORY_ENV.code + [MonitorEnvLayers.MISSIONS], + [MonitorEnvLayers.MISSION_TO_ATTACH_ON_REPORTING], + [MonitorEnvLayers.REPORTING_TO_ATTACH_ON_MISSION], + [MonitorEnvLayers.REPORTINGS], + [MonitorEnvLayers.SEMAPHORES], + [MonitorEnvLayers.STATIONS], + [ + MonitorEnvLayers.AMP, + MonitorEnvLayers.AMP_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV + ] ] // Priority of hoverable items is determined by the order of the layers in this array @@ -286,27 +290,29 @@ export const SelectableLayers7To26 = [ // Zoom level goes from 0 (furthest from earth) to 26 (closest from earth) export const HoverableLayers0To7 = [ - Layers.ACTIONS.code, - Layers.MISSIONS.code, - Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code, - Layers.REPORTINGS.code, - Layers.SEMAPHORES.code, - Layers.STATIONS.code + [MonitorEnvLayers.ACTIONS], + [MonitorEnvLayers.MISSIONS], + [MonitorEnvLayers.MISSION_TO_ATTACH_ON_REPORTING], + [MonitorEnvLayers.REPORTING_TO_ATTACH_ON_MISSION], + [MonitorEnvLayers.REPORTINGS], + [MonitorEnvLayers.SEMAPHORES], + [MonitorEnvLayers.STATIONS] ] export const HoverableLayers7To26 = [ - Layers.ACTIONS.code, - Layers.MISSIONS.code, - Layers.MISSION_TO_ATTACH_ON_REPORTING.code, - Layers.REPORTING_TO_ATTACH_ON_MISSION.code, - Layers.REPORTINGS.code, - Layers.SEMAPHORES.code, - Layers.STATIONS.code, - Layers.AMP.code, - Layers.AMP_PREVIEW.code, - Layers.REGULATORY_ENV_PREVIEW.code, - Layers.REGULATORY_ENV.code + [MonitorEnvLayers.ACTIONS], + [MonitorEnvLayers.MISSIONS], + [MonitorEnvLayers.MISSION_TO_ATTACH_ON_REPORTING], + [MonitorEnvLayers.REPORTING_TO_ATTACH_ON_MISSION], + [MonitorEnvLayers.REPORTINGS], + [MonitorEnvLayers.SEMAPHORES], + [MonitorEnvLayers.STATIONS], + [ + MonitorEnvLayers.AMP, + MonitorEnvLayers.AMP_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV_PREVIEW, + MonitorEnvLayers.REGULATORY_ENV + ] ] export type RegulatoryOrAMPLayerType = diff --git a/frontend/src/features/layersSelector/overlays/utils.ts b/frontend/src/domain/entities/layers/utils.ts similarity index 52% rename from frontend/src/features/layersSelector/overlays/utils.ts rename to frontend/src/domain/entities/layers/utils.ts index e09a7ee857..ce06fc0b76 100644 --- a/frontend/src/features/layersSelector/overlays/utils.ts +++ b/frontend/src/domain/entities/layers/utils.ts @@ -3,9 +3,33 @@ import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from 'domain/entities import type { AMPProperties } from 'domain/entities/AMPs' import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' +export const getTitle = name => (name ? `${name?.replace(/[_]/g, ' ')}` : '') + +export const getGroupName = ( + layer: AMPProperties | RegulatoryLayerCompactProperties, + layerType: RegulatoryOrAMPLayerType +) => { + if (layerType === MonitorEnvLayers.AMP || layerType === MonitorEnvLayers.AMP_PREVIEW) { + return (layer as AMPProperties).name + } + + return (layer as RegulatoryLayerCompactProperties).layer_name +} + export const getName = ( layer: AMPProperties | RegulatoryLayerCompactProperties, layerType: RegulatoryOrAMPLayerType +) => { + if (layerType === MonitorEnvLayers.AMP || layerType === MonitorEnvLayers.AMP_PREVIEW) { + return (layer as AMPProperties).type + } + + return (layer as RegulatoryLayerCompactProperties).entity_name +} + +export const getLegendKey = ( + layer: AMPProperties | RegulatoryLayerCompactProperties, + layerType: RegulatoryOrAMPLayerType ) => { if (layerType === MonitorEnvLayers.AMP || layerType === MonitorEnvLayers.AMP_PREVIEW) { return (layer as AMPProperties).name @@ -14,7 +38,7 @@ export const getName = ( return (layer as RegulatoryLayerCompactProperties).entity_name } -export const getType = ( +export const getLegendType = ( layer: AMPProperties | RegulatoryLayerCompactProperties, layerType: RegulatoryOrAMPLayerType ) => { diff --git a/frontend/src/domain/entities/regulatory.ts b/frontend/src/domain/entities/regulatory.ts index f493e8f5a6..4d529c9f0f 100644 --- a/frontend/src/domain/entities/regulatory.ts +++ b/frontend/src/domain/entities/regulatory.ts @@ -1,7 +1,5 @@ import type { GeoJSON } from '../types/GeoJSON' -export const getTitle = topic => (topic ? `${topic?.replace(/[_]/g, ' ')}` : '') - export type RegulatoryLayerWithMetadataFromAPI = { entity_name: string facade: string diff --git a/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx b/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx index 4f0504482d..4cd45c4af6 100644 --- a/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx +++ b/frontend/src/features/layersSelector/metadataPanel/ampMetadata/index.tsx @@ -2,12 +2,12 @@ import { useGetAMPsQuery } from '@api/ampsAPI' import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' +import { getTitle } from 'domain/entities/layers/utils' import { useCallback } from 'react' import { FingerprintSpinner } from 'react-epic-spinners' import styled from 'styled-components' import { MonitorEnvLayers } from '../../../../domain/entities/layers/constants' -import { getTitle } from '../../../../domain/entities/regulatory' import { LayerLegend } from '../../utils/LayerLegend.style' import { Key, Value, Fields, Field, Zone, Body, NoValue } from '../MetadataPanel.style' import { RegulatorySummary } from '../RegulatorySummary' @@ -35,8 +35,8 @@ export function AmpMetadata() { {ampMetadata ? ( <>

- - {getTitle(ampMetadata?.name)} + + {getTitle(ampMetadata?.name)} ` transition: all 0.5s; ` -const RegulatoryZoneName = styled.span` +const Name = styled.span` flex: 1; line-height: initial; white-space: nowrap; diff --git a/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx b/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx index 9217518d75..05e1200535 100644 --- a/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx +++ b/frontend/src/features/layersSelector/metadataPanel/regulatoryMetadata/index.tsx @@ -3,13 +3,13 @@ import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' import { skipToken } from '@reduxjs/toolkit/query' +import { getTitle } from 'domain/entities/layers/utils' import { useCallback } from 'react' import { FingerprintSpinner } from 'react-epic-spinners' import styled from 'styled-components' import { Identification } from './Identification' import { MonitorEnvLayers } from '../../../../domain/entities/layers/constants' -import { getTitle } from '../../../../domain/entities/regulatory' import { LayerLegend } from '../../utils/LayerLegend.style' import { RegulatorySummary } from '../RegulatorySummary' import { closeMetadataPanel } from '../slice' @@ -35,7 +35,7 @@ export function RegulatoryMetadata() {
diff --git a/frontend/src/features/layersSelector/myAmps/MyAMPLayerZone.tsx b/frontend/src/features/layersSelector/myAmps/MyAMPLayerZone.tsx index 4b479f035e..3e3c8092bc 100644 --- a/frontend/src/features/layersSelector/myAmps/MyAMPLayerZone.tsx +++ b/frontend/src/features/layersSelector/myAmps/MyAMPLayerZone.tsx @@ -1,4 +1,5 @@ import { useAppSelector } from '@hooks/useAppSelector' +import { getTitle } from 'domain/entities/layers/utils' import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' import { hideAmpLayer, removeAmpZonesFromMyLayers, showAmpLayer } from '../../../domain/shared_slices/Amp' @@ -15,7 +16,7 @@ export function MyAMPLayerZone({ amp, isDisplayed }: { amp: AMP; isDisplayed: bo const handleRemoveZone = () => dispatch(removeAmpZonesFromMyLayers([amp.id])) - const displayedName = amp?.type?.replace(/[_]/g, ' ') ?? 'AUNCUN NOM' + const displayedName = getTitle(amp?.type) ?? 'AUNCUN NOM' const toggleAmpZoneMetadata = () => { if (metadataIsShown) { diff --git a/frontend/src/features/layersSelector/myRegulatoryLayers/MyRegulatoryLayerZone.tsx b/frontend/src/features/layersSelector/myRegulatoryLayers/MyRegulatoryLayerZone.tsx index c8ad1223e9..c50457d2cc 100644 --- a/frontend/src/features/layersSelector/myRegulatoryLayers/MyRegulatoryLayerZone.tsx +++ b/frontend/src/features/layersSelector/myRegulatoryLayers/MyRegulatoryLayerZone.tsx @@ -1,3 +1,5 @@ +import { getTitle } from 'domain/entities/layers/utils' + import { MonitorEnvLayers } from '../../../domain/entities/layers/constants' import { hideRegulatoryLayer, @@ -26,7 +28,7 @@ export function RegulatoryLayerZone({ regulatoryZone }: RegulatoryLayerZoneProps const handleRemoveZone = () => dispatch(removeRegulatoryZonesFromMyLayers([regulatoryZone.id])) - const displayedName = regulatoryZone?.entity_name?.replace(/[_]/g, ' ') || 'AUNCUN NOM' + const displayedName = getTitle(regulatoryZone?.entity_name) ?? 'AUNCUN NOM' const toggleRegulatoryZoneMetadata = () => { if (metadataIsShown) { diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index f71f48442d..c69ee47799 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -2,9 +2,10 @@ import { useAppDispatch } from '@hooks/useAppDispatch' import { useAppSelector } from '@hooks/useAppSelector' import { Size } from '@mtes-mct/monitor-ui' import { MonitorEnvLayers, type RegulatoryOrAMPLayerType } from 'domain/entities/layers/constants' +import { type RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import styled from 'styled-components' -import { getName, getType } from './utils' +import { getGroupName, getLegendKey, getLegendType, getName, getTitle } from '../../../domain/entities/layers/utils' import { getDisplayedMetadataLayerIdAndType, openAMPMetadataPanel, @@ -13,7 +14,6 @@ import { import { LayerLegend } from '../utils/LayerLegend.style' import type { AMPProperties } from 'domain/entities/AMPs' -import type { RegulatoryLayerCompactProperties } from 'domain/entities/regulatory' import type { OverlayItem } from 'domain/types/map' type OverlayContentProps = { @@ -36,9 +36,11 @@ export function OverlayContent({ items }: OverlayContentProps) { return ( {items?.map(item => { + const groupName = getGroupName(item.properties, item.layerType) const name = getName(item.properties, item.layerType) - const type = getType(item.properties, item.layerType) - const isSelected = item.properties.id === layerId && item.layerType === layerType + const legendType = getLegendType(item.properties, item.layerType) + const legendKey = getLegendKey(item.properties, item.layerType) + const isSelected = item.properties.id === layerId && !!layerType && item.layerType.includes(layerType) return ( - - {name} - / {type} + + {getTitle(groupName)} +  / {getTitle(name) || 'AUCUN NOM'} ) })} @@ -73,7 +75,7 @@ const LayerItem = styled.li<{ $isSelected: boolean }>` border-bottom: 1px solid ${p => p.theme.color.lightGray}; ` -const Name = styled.span` +const GroupName = styled.span` color: ${p => p.theme.color.gunMetal}; overflow: hidden; text-overflow: ellipsis; @@ -81,7 +83,7 @@ const Name = styled.span` font: normal normal bold 13px/18px Marianne; ` -const Type = styled.span` +const Name = styled.span` color: ${p => p.theme.color.gunMetal}; overflow: hidden; text-overflow: ellipsis; diff --git a/frontend/src/features/layersSelector/search/ResultsList/AMPLayerGroup/AMPLayer.tsx b/frontend/src/features/layersSelector/search/ResultsList/AMPLayerGroup/AMPLayer.tsx index a8f87a812a..2b19d5125e 100644 --- a/frontend/src/features/layersSelector/search/ResultsList/AMPLayerGroup/AMPLayer.tsx +++ b/frontend/src/features/layersSelector/search/ResultsList/AMPLayerGroup/AMPLayer.tsx @@ -73,7 +73,7 @@ export function AMPLayer({ layerId, searchedText }: { layerId: number; searchedT return ( - + diff --git a/frontend/src/features/layersSelector/search/ResultsList/ResultListLayerGroup.tsx b/frontend/src/features/layersSelector/search/ResultsList/ResultListLayerGroup.tsx index dc3be1aa42..093e2a094a 100644 --- a/frontend/src/features/layersSelector/search/ResultsList/ResultListLayerGroup.tsx +++ b/frontend/src/features/layersSelector/search/ResultsList/ResultListLayerGroup.tsx @@ -1,5 +1,6 @@ import { useAppDispatch } from '@hooks/useAppDispatch' import { Accent, Icon, IconButton, THEME } from '@mtes-mct/monitor-ui' +import { getTitle } from 'domain/entities/layers/utils' import { setFitToExtent } from 'domain/shared_slices/Map' import _ from 'lodash' import { useState } from 'react' @@ -66,7 +67,7 @@ export function ResultListLayerGroup({ autoEscape highlightClassName="highlight" searchWords={searchedText && searchedText.length > 0 ? searchedText.split(' ') : []} - textToHighlight={groupName ?? ''} + textToHighlight={getTitle(groupName) ?? ''} /> diff --git a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx index db5a1e1420..dc3abeb76b 100644 --- a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx +++ b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx @@ -7,22 +7,22 @@ import { getRegulatoryEnvColorWithAlpha } from '../../map/layers/styles/administ export function LayerLegend({ layerType, - name, + legendKey, size = Size.SMALL, type }: { layerType: RegulatoryOrAMPLayerType - name: string | null + legendKey?: string | null size?: Size - type: string | null + type?: string | null }) { switch (layerType) { case MonitorEnvLayers.AMP: case MonitorEnvLayers.AMP_PREVIEW: - return + return case MonitorEnvLayers.REGULATORY_ENV: case MonitorEnvLayers.REGULATORY_ENV_PREVIEW: - return + return default: return } diff --git a/frontend/src/features/layersSelector/utils/MyLayerZone.tsx b/frontend/src/features/layersSelector/utils/MyLayerZone.tsx index 507e483131..2942d04e42 100644 --- a/frontend/src/features/layersSelector/utils/MyLayerZone.tsx +++ b/frontend/src/features/layersSelector/utils/MyLayerZone.tsx @@ -70,7 +70,7 @@ export function MyLayerZone({ return ( - + {displayedName} diff --git a/frontend/src/features/map/BaseMap.tsx b/frontend/src/features/map/BaseMap.tsx index 234078d24f..afd7c2fb06 100644 --- a/frontend/src/features/map/BaseMap.tsx +++ b/frontend/src/features/map/BaseMap.tsx @@ -92,7 +92,7 @@ export function BaseMap({ children }: { children: Array getOverlayItems export const getHoveredItems = (features: SerializedFeature>[] | undefined) => getOverlayItemsFromFeatures(features) -export const getHighestPriorityFeatures = (features: FeatureLike[], priorityOrderTypes: string[]) => { - const highestPriorityFeatureType = priorityOrderTypes.find(layerType => - features.some(feature => String(feature.getId()).includes(layerType)) +export const getHighestPriorityFeatures = (features: FeatureLike[], priorityOrderTypes: Array) => { + const highestPriorityFeatureTypes = priorityOrderTypes.find(layerTypes => + features.some(feature => layerTypes.some(layerType => String(feature.getId()).includes(layerType))) ) - if (!highestPriorityFeatureType) { + if (!highestPriorityFeatureTypes) { return [] } const highestPriorityFeatures = features.filter(feature => - String(feature.getId()).includes(highestPriorityFeatureType) + highestPriorityFeatureTypes.some(highestPriorityFeatureType => + String(feature.getId()).includes(highestPriorityFeatureType) + ) ) return highestPriorityFeatures From 7266798a4de538f136a90e0c63b1c3c4850f80f6 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Mon, 15 Apr 2024 14:52:35 +0200 Subject: [PATCH 15/19] fix test --- .../e2e/main_window/layerTree/regulatory_layers.spec.ts | 2 +- frontend/cypress/e2e/side_window/reporting/filters.spec.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frontend/cypress/e2e/main_window/layerTree/regulatory_layers.spec.ts b/frontend/cypress/e2e/main_window/layerTree/regulatory_layers.spec.ts index 4dff87585b..5a4cab916b 100644 --- a/frontend/cypress/e2e/main_window/layerTree/regulatory_layers.spec.ts +++ b/frontend/cypress/e2e/main_window/layerTree/regulatory_layers.spec.ts @@ -20,7 +20,7 @@ context('LayerTree > Regulatory Layers', () => { cy.getDataCy('regulatory-layers-result-title').contains('2 résultat').click() cy.log("zoom on the regulation's zone and show metadata") - cy.getDataCy('result-group').contains('ZMEL_Cale_Querlen').click() + cy.getDataCy('result-group').contains('ZMEL Cale Querlen').click() cy.getDataCy('regulatory-result-zone').contains('Zone au sud de la cale').click() cy.getDataCy('regulatory-metadata-header').contains('ZMEL Cale Querlen').click() cy.wait(1000) // let OL do the rendering diff --git a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts index 4be09db581..0a291fc54a 100644 --- a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts @@ -201,9 +201,9 @@ context('Reportings', () => { cy.wait(500) cy.wait('@getReportings') - // because we have two enpty tr at the beginning and at the end of the table - // we add 2 to the length - cy.get('.Table-SimpleTable tr').should('have.length', 5) + // because we have one empty tr at the end of the table + // we add 1 to the expected length (3 results + 1) + cy.get('.Table-SimpleTable tr').should('have.length', 4) cy.fill('Rechercher une cible', undefined) }) From 889b3b2abdd7d9a16257dd039fef905065cba289 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Mon, 15 Apr 2024 15:21:08 +0200 Subject: [PATCH 16/19] fix https://github.com/MTES-MCT/monitorenv/issues/1129 --- .../layersSelector/myAmps/MyAMPLayerGroup.tsx | 2 +- .../myRegulatoryLayers/MyRegulatoryLayerGroup.tsx | 11 ++++++++--- .../src/features/layersSelector/utils/MyLayerZone.tsx | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/frontend/src/features/layersSelector/myAmps/MyAMPLayerGroup.tsx b/frontend/src/features/layersSelector/myAmps/MyAMPLayerGroup.tsx index 2457f379e4..7f5fff8b0b 100644 --- a/frontend/src/features/layersSelector/myAmps/MyAMPLayerGroup.tsx +++ b/frontend/src/features/layersSelector/myAmps/MyAMPLayerGroup.tsx @@ -78,7 +78,7 @@ export function MyAMPLayerGroup({ - - {groupName} + + {getTitle(groupName)} {`${layers?.length} / ${totalNumberOfZones}`} @@ -79,7 +84,7 @@ export function RegulatoryLayerGroup({ groupName, layers }: { groupName: string; Date: Mon, 15 Apr 2024 15:34:16 +0200 Subject: [PATCH 17/19] fixes #1327 --- frontend/src/features/layersSelector/search/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/features/layersSelector/search/index.tsx b/frontend/src/features/layersSelector/search/index.tsx index f24ce4a37e..671e0abe74 100644 --- a/frontend/src/features/layersSelector/search/index.tsx +++ b/frontend/src/features/layersSelector/search/index.tsx @@ -82,7 +82,7 @@ export function LayerSearch() { const ampTypes = useMemo( () => _.chain(amps?.entities) - .map(l => l?.type) + .map(l => l?.type?.trim()) .uniq() .filter(l => !!l) .map(l => ({ label: l, value: l })) @@ -97,13 +97,15 @@ export function LayerSearch() { .filter(l => !!l?.thematique) .map(l => l?.thematique.split(',')) .flatMap(l => l) - .uniq() .filter(l => !!l) + .map(l => l?.trim()) + .uniq() .map(l => ({ label: l, value: l })) .sortBy('label') .value() as Option[], [regulatoryLayers] ) + const allowResetResults = !_.isEmpty(regulatoryLayersSearchResult) || !_.isEmpty(ampsSearchResult) return ( From d86a435f4bc4c5b210b2db176f1fbdc58db777e2 Mon Sep 17 00:00:00 2001 From: Thomas Brosset Date: Mon, 15 Apr 2024 15:56:58 +0200 Subject: [PATCH 18/19] fix ci test --- frontend/cypress/e2e/side_window/reporting/filters.spec.ts | 4 +--- .../features/Reportings/ReportingsList/ReportingsTable.tsx | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts index 0a291fc54a..3857610c61 100644 --- a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts @@ -201,9 +201,7 @@ context('Reportings', () => { cy.wait(500) cy.wait('@getReportings') - // because we have one empty tr at the end of the table - // we add 1 to the expected length (3 results + 1) - cy.get('.Table-SimpleTable tr').should('have.length', 4) + cy.getDataCy('reporting-row').should('have.length', 3) cy.fill('Rechercher une cible', undefined) }) diff --git a/frontend/src/features/Reportings/ReportingsList/ReportingsTable.tsx b/frontend/src/features/Reportings/ReportingsList/ReportingsTable.tsx index c6dd468d25..5591652c35 100644 --- a/frontend/src/features/Reportings/ReportingsList/ReportingsTable.tsx +++ b/frontend/src/features/Reportings/ReportingsList/ReportingsTable.tsx @@ -119,6 +119,7 @@ export function ReportingsTable({ Number(key) === Number(row?.original.id))} + data-cy="reporting-row" > {row?.getVisibleCells().map(cell => ( Date: Mon, 15 Apr 2024 17:03:43 +0200 Subject: [PATCH 19/19] force rerender of checkpickers --- frontend/cypress/e2e/side_window/reporting/filters.spec.ts | 5 +++-- frontend/src/features/Reportings/Filters/Table/index.tsx | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts index 3857610c61..11f98d9546 100644 --- a/frontend/cypress/e2e/side_window/reporting/filters.spec.ts +++ b/frontend/cypress/e2e/side_window/reporting/filters.spec.ts @@ -96,6 +96,7 @@ context('Reportings', () => { }) it('Should filter reportings by themes', () => { + cy.wait(200) cy.fill('Thématiques', ['Arrêté à visa environnemental']) cy.getDataCy('reportings-filter-tags').find('.Component-SingleTag > span').contains('Arrêté à visa environnemental') @@ -232,10 +233,10 @@ context('Reportings', () => { cy.wait(500) cy.wait('@getReportings') - cy.get('*[data-cy="reporting-theme-filter"]').click() + cy.getDataCy('reporting-theme-filter').click() cy.get('#themes-listbox > div').should('have.length', 34) - cy.get('*[data-cy="reporting-sub-theme-filter"]').click() + cy.getDataCy('reporting-sub-theme-filter').click() cy.get('#subThemes-listbox > div').should('have.length', 161) }) }) diff --git a/frontend/src/features/Reportings/Filters/Table/index.tsx b/frontend/src/features/Reportings/Filters/Table/index.tsx index b356fa28f9..7463e37082 100644 --- a/frontend/src/features/Reportings/Filters/Table/index.tsx +++ b/frontend/src/features/Reportings/Filters/Table/index.tsx @@ -200,7 +200,7 @@ export function TableReportingsFiltersWithRef( value={targetTypeFilter} />