diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d34fef9e0..b3cb9e67c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "AGPL-3.0", "dependencies": { - "@mtes-mct/monitor-ui": "22.1.0", + "@mtes-mct/monitor-ui": "23.0.0", "@reduxjs/toolkit": "2.0.0", "@rsuite/responsive-nav": "5.0.2", "@sentry/browser": "7.73.0", @@ -3659,9 +3659,9 @@ "integrity": "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA==" }, "node_modules/@mtes-mct/monitor-ui": { - "version": "22.1.0", - "resolved": "https://registry.npmjs.org/@mtes-mct/monitor-ui/-/monitor-ui-22.1.0.tgz", - "integrity": "sha512-ox/BzwALdDH0L/TcvJ8N/d27kC5jZ5vdr69zhhyiFsIE1t2H6bn/W1+QjaIkKRcYqa/NNEHBHQccxmC4C8/M/g==", + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/@mtes-mct/monitor-ui/-/monitor-ui-23.0.0.tgz", + "integrity": "sha512-RXN9j6/ggZNVlUcRMUgLTYT/tOGfmTjZZj4mgthbNszODbQsAkDBp+A855rZ+kBFPVnol9mdmzNov7ThSb3U5A==", "dependencies": { "@babel/runtime": "7.25.6", "@tanstack/react-table": "8.20.5", diff --git a/frontend/package.json b/frontend/package.json index d6ebad758..b869fc055 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ "test:unit:watch": "npm run test:unit -- --watch" }, "dependencies": { - "@mtes-mct/monitor-ui": "22.1.0", + "@mtes-mct/monitor-ui": "23.0.0", "@reduxjs/toolkit": "2.0.0", "@rsuite/responsive-nav": "5.0.2", "@sentry/browser": "7.73.0", diff --git a/frontend/src/features/Dashboard/components/DashboardForm/Accordion.tsx b/frontend/src/features/Dashboard/components/DashboardForm/Accordion.tsx index b42ecc63b..5c19b07cc 100644 --- a/frontend/src/features/Dashboard/components/DashboardForm/Accordion.tsx +++ b/frontend/src/features/Dashboard/components/DashboardForm/Accordion.tsx @@ -11,8 +11,8 @@ type AccordionProps = { export function Accordion({ children, headerButton, isExpanded, setExpandedAccordion, title }: AccordionProps) { return ( - - + + {title} {headerButton} @@ -34,6 +34,7 @@ export function Accordion({ children, headerButton, isExpanded, setExpandedAccor const AccordionContainer = styled.div<{ $withCursor: boolean }>` box-shadow: 0px 3px 6px #70778540; cursor: ${({ $withCursor }) => ($withCursor ? 'pointer' : 'default')}; + width: 571px; ` const StyledIconButton = styled(IconButton)<{ $isExpanded: boolean }>` transform: ${({ $isExpanded }) => ($isExpanded ? 'rotate(180deg)' : 'rotate(0deg)')}; @@ -61,7 +62,7 @@ const HeaderSeparator = styled.div` const AccordionContent = styled.div<{ $isExpanded: boolean }>` display: flex; flex-direction: column; - max-height: ${({ $isExpanded }) => ($isExpanded ? '535px' : '0px')}; - overflow: hidden; + max-height: ${({ $isExpanded }) => ($isExpanded ? '100vh' : '0px')}; + overflow-x: hidden; transition: 0.5s max-height; ` diff --git a/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/Layer.tsx b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/Layer.tsx new file mode 100644 index 000000000..353b77992 --- /dev/null +++ b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/Layer.tsx @@ -0,0 +1,89 @@ +import { LayerLegend } from '@features/layersSelector/utils/LayerLegend.style' +import { LayerSelector } from '@features/layersSelector/utils/LayerSelector.style' +import { Accent, Icon, IconButton, THEME } from '@mtes-mct/monitor-ui' +import { transformExtent } from 'ol/proj' +import Projection from 'ol/proj/Projection' +import { createRef } from 'react' + +import { useGetRegulatoryLayersQuery } from '../../../../../api/regulatoryLayersAPI' +import { MonitorEnvLayers } from '../../../../../domain/entities/layers/constants' +import { OPENLAYERS_PROJECTION, WSG84_PROJECTION } from '../../../../../domain/entities/map/constants' +import { setFitToExtent } from '../../../../../domain/shared_slices/Map' +import { + addRegulatoryZonesToMyLayers, + removeRegulatoryZonesFromMyLayers +} from '../../../../../domain/shared_slices/Regulatory' +import { useAppDispatch } from '../../../../../hooks/useAppDispatch' +import { useAppSelector } from '../../../../../hooks/useAppSelector' + +type RegulatoryLayerProps = { + layerId: number +} + +export function Layer({ layerId }: RegulatoryLayerProps) { + const dispatch = useAppDispatch() + const ref = createRef() + + const selectedRegulatoryLayerIds = useAppSelector(state => state.regulatory.selectedRegulatoryLayerIds) + + const { layer } = useGetRegulatoryLayersQuery(undefined, { + selectFromResult: result => ({ + layer: result?.currentData?.entities[layerId] + }) + }) + // const regulatoryMetadataLayerId = useAppSelector(state => getDisplayedMetadataRegulatoryLayerId(state)) + + const isZoneSelected = selectedRegulatoryLayerIds.includes(layerId) + // const metadataIsShown = useAppSelector(state => getMetadataIsOpenForRegulatoryLayerId(state, layerId)) + + const handleSelectZone = e => { + e.stopPropagation() + if (isZoneSelected) { + dispatch(removeRegulatoryZonesFromMyLayers([layerId])) + } else { + dispatch(addRegulatoryZonesToMyLayers([layerId])) + } + } + + const toggleZoneMetadata = () => {} + + const fitToRegulatoryLayer = () => { + if (!layer?.bbox) { + return + } + const extent = transformExtent( + layer?.bbox, + new Projection({ code: WSG84_PROJECTION }), + new Projection({ code: OPENLAYERS_PROJECTION }) + ) + dispatch(setFitToExtent(extent)) + } + + return ( + + + + {layer?.entity_name ?? 'AUCUN NOM'} + + + + + + + ) +} diff --git a/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/ListLayerGroup.tsx b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/ListLayerGroup.tsx new file mode 100644 index 000000000..cd1d9fd18 --- /dev/null +++ b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/ListLayerGroup.tsx @@ -0,0 +1,72 @@ +import { + getExtentOfRegulatoryLayersGroupByGroupName, + getNumberOfRegulatoryLayerZonesByGroupName +} from '@api/regulatoryLayersAPI' +import { getDisplayedMetadataRegulatoryLayerId } from '@features/layersSelector/metadataPanel/slice' +import { LayerSelector } from '@features/layersSelector/utils/LayerSelector.style' +import { useAppDispatch } from '@hooks/useAppDispatch' +import { useAppSelector } from '@hooks/useAppSelector' +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 { includes, intersection } from 'lodash' +import { useState } from 'react' + +import { Layer } from './Layer' + +type ResultListLayerGroupProps = { + groupName: string + layerIds: number[] +} +export function ListLayerGroup({ groupName, layerIds }: ResultListLayerGroupProps) { + const dispatch = useAppDispatch() + const [zonesAreOpen, setZonesAreOpen] = useState(false) + + const layerIdToDisplay = useAppSelector(state => getDisplayedMetadataRegulatoryLayerId(state)) + const totalNumberOfZones = useAppSelector(state => getNumberOfRegulatoryLayerZonesByGroupName(state, groupName)) + const groupExtent = useAppSelector(state => getExtentOfRegulatoryLayersGroupByGroupName(state, groupName)) + + const selectedLayerIds = [] + const zonesSelected = intersection(selectedLayerIds, layerIds) + const allTopicZonesAreChecked = zonesSelected?.length === layerIds?.length + const forceZonesAreOpen = includes(layerIds, layerIdToDisplay) + + const handleCheckAllZones = e => { + e.stopPropagation() + } + + const clickOnGroupZones = () => { + setZonesAreOpen(!zonesAreOpen) + + if (!zonesAreOpen) { + dispatch(setFitToExtent(groupExtent)) + } + } + + return ( + <> + + + {getTitle(groupName) ?? ''} + + + {`${layerIds.length}/${totalNumberOfZones}`} + + + + + + {layerIds?.map(layerId => ( + + ))} + + + ) +} diff --git a/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/index.tsx b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/index.tsx new file mode 100644 index 000000000..988fc76a8 --- /dev/null +++ b/frontend/src/features/Dashboard/components/DashboardForm/RegulatoryAreas/index.tsx @@ -0,0 +1,38 @@ +import { useGetRegulatoryLayersQuery } from '@api/regulatoryLayersAPI' +import { LayerSelector } from '@features/layersSelector/utils/LayerSelector.style' +import { groupBy } from 'lodash' +import styled from 'styled-components' + +import { ListLayerGroup } from './ListLayerGroup' +import { Accordion } from '../Accordion' + +type RegulatoriesAreasProps = { + isExpanded: boolean + setExpandedAccordion: () => void +} +export function RegulatoryAreas({ isExpanded, setExpandedAccordion }: RegulatoriesAreasProps) { + const { data: regulatoryLayers } = useGetRegulatoryLayersQuery() + + const regulatoryAreasByLayerName = groupBy( + Object.values(regulatoryLayers?.ids ?? {}), + r => regulatoryLayers?.entities[r]?.layer_name + ) + + return ( + + + {Object.entries(regulatoryAreasByLayerName).map(([layerGroupName, layerIdsInGroup]) => ( + + ))} + + + ) +} + +const StyledLayerList = styled(LayerSelector.LayerList)` + height: auto; +` diff --git a/frontend/src/features/Dashboard/components/DashboardForm/index.tsx b/frontend/src/features/Dashboard/components/DashboardForm/index.tsx index d68faf86f..d186c87dc 100644 --- a/frontend/src/features/Dashboard/components/DashboardForm/index.tsx +++ b/frontend/src/features/Dashboard/components/DashboardForm/index.tsx @@ -1,7 +1,10 @@ +import { SideWindowContent } from '@features/SideWindow/style' import { Accent, Icon, IconButton } from '@mtes-mct/monitor-ui' import { useState } from 'react' +import styled from 'styled-components' import { Accordion } from './Accordion' +import { RegulatoryAreas } from './RegulatoryAreas' export function DashboardForm() { const [expandedAccordion, setExpandedAccordion] = useState(0) @@ -18,58 +21,110 @@ export function DashboardForm() { const clickOnEye = () => {} return ( -
- } - isExpanded={expandedAccordion === 1} - setExpandedAccordion={() => handleAccordionClick(1)} - title="Zones réglementaires" - > -
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
- handleAccordionClick(2)} - title="Zones AMP" - > -
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
TEST
-
- handleAccordionClick(3)} - title="Zones de vigilance" - > -
TEST
-
TEST
-
TEST
-
TEST
-
-
+ + + handleAccordionClick(1)} /> + handleAccordionClick(3)} + title="Zones AMP" + > +
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
+ handleAccordionClick(4)} + title="Zones de vigilance" + > +
TEST
+
TEST
+
TEST
+
TEST
+
+
+ + handleAccordionClick(5)} + title="Pression territoriale des contrôles et surveillances" + > +
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
+ } + isExpanded={expandedAccordion === 6} + setExpandedAccordion={() => handleAccordionClick(6)} + title="Signalements" + > +
TEST
+
TEST
+
TEST
+
TEST
+
+
+ + handleAccordionClick(7)} + title="Unités" + > +
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
TEST
+
+ handleAccordionClick(8)} + title="Commentaires" + > +
TEST
+
TEST
+
TEST
+
TEST
+
+
+
) } + +const Container = styled(SideWindowContent)` + overflow-y: hidden; + flex-direction: row; +` +const Column = styled.div` + height: 100vh; + overflow-y: auto; + padding: 12px; + padding-bottom: 100px; +` diff --git a/frontend/src/features/Dashboard/components/DashboardsList/index.tsx b/frontend/src/features/Dashboard/components/DashboardsList/index.tsx index 2744465f5..400d29744 100644 --- a/frontend/src/features/Dashboard/components/DashboardsList/index.tsx +++ b/frontend/src/features/Dashboard/components/DashboardsList/index.tsx @@ -1,13 +1,10 @@ import { SideWindowContent } from '@features/SideWindow/style' import styled from 'styled-components' -import { DashboardForm } from '../DashboardForm' - export function DashboardsList() { return ( Tableaux de bord - ) } diff --git a/frontend/src/features/Dashboard/components/DashboardsNavBar.tsx b/frontend/src/features/Dashboard/components/DashboardsNavBar.tsx index f66a665b2..480b061d4 100644 --- a/frontend/src/features/Dashboard/components/DashboardsNavBar.tsx +++ b/frontend/src/features/Dashboard/components/DashboardsNavBar.tsx @@ -9,7 +9,6 @@ import { generatePath } from 'react-router' export function DashboardsNavBar() { const dispatch = useAppDispatch() - const tabs = useMemo(() => { const dashboardsList = { icon: , diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddAMPs/AMPItem.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddAMPs/AMPItem.tsx index d8f21be05..651f68b3f 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddAMPs/AMPItem.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddAMPs/AMPItem.tsx @@ -164,6 +164,9 @@ const AMPName = styled.div` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + > span { + margin-left: 8px; + } ` const ButtonsContainer = styled.div` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddRegulatoryAreas/RegulatoryAreaItem.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddRegulatoryAreas/RegulatoryAreaItem.tsx index 5bf4c2780..25fb56d2b 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddRegulatoryAreas/RegulatoryAreaItem.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/AddRegulatoryAreas/RegulatoryAreaItem.tsx @@ -177,6 +177,9 @@ const RegulatoryAreaName = styled.div` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + > span { + margin-left: 8px; + } ` const ButtonsContainer = styled.div` diff --git a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx index dd4379f14..77af8adf6 100644 --- a/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx +++ b/frontend/src/features/VigilanceArea/components/VigilanceAreaForm/index.tsx @@ -160,6 +160,7 @@ const Title = styled.span<{ $isDraft: boolean; $isNew: boolean }>` font-size: 15px; color: ${p => p.theme.color.gunMetal}; overflow: hidden; + margin-left: 8px; max-width: ${p => { if (p.$isNew) { return '100%' diff --git a/frontend/src/features/layersSelector/index.tsx b/frontend/src/features/layersSelector/index.tsx index e41f57b91..a45065df0 100644 --- a/frontend/src/features/layersSelector/index.tsx +++ b/frontend/src/features/layersSelector/index.tsx @@ -166,6 +166,7 @@ const MetadataPanelShifter = styled.div<{ opacity: ${props => (props.metadataPanelIsOpen ? 1 : 0)}; background: ${p => p.theme.color.gainsboro}; transition: 0.5s all; + z-index: -1; ` const VigilanceAreaPanelShifter = styled.div<{ @@ -188,6 +189,7 @@ const VigilanceAreaPanelShifter = styled.div<{ opacity: ${props => (props.isVigilanceAreaFormOpen ? 1 : 0)}; background: ${p => p.theme.color.gainsboro}; transition: 0.5s all; + z-index: -1; ` const Sidebar = styled.div<{ isLayersSidebarVisible: boolean; isVisible: boolean }>` @@ -198,7 +200,6 @@ const Sidebar = styled.div<{ isLayersSidebarVisible: boolean; isVisible: boolean z-index: 1; border-radius: 2px; position: absolute; - display: inline-block; transition: 0.5s all; ` diff --git a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx index fcdd2abe1..b47af63a9 100644 --- a/frontend/src/features/layersSelector/overlays/OverlayContent.tsx +++ b/frontend/src/features/layersSelector/overlays/OverlayContent.tsx @@ -186,6 +186,7 @@ const GroupName = styled.span` white-space: nowrap; font: normal normal bold 13px/18px Marianne; min-width: ${p => (p.title?.length && p.title.length * 7 > 220 ? 200 : 0)}px; + margin-left: 8px; ` const Name = styled.span` diff --git a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx index e9f7a9df8..cb86d6a7f 100644 --- a/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx +++ b/frontend/src/features/layersSelector/utils/LayerLegend.style.tsx @@ -45,6 +45,5 @@ const Rectangle = styled.div<{ $size: Size; $vectorLayerColor?: string }>` background: ${p => p.$vectorLayerColor ?? p.theme.color.gainsboro}; border: 1px solid ${p => p.theme.color.slateGray}; display: inline-block; - margin-right: 10px; flex-shrink: 0; ` diff --git a/frontend/src/features/layersSelector/utils/LayerSelector.style.ts b/frontend/src/features/layersSelector/utils/LayerSelector.style.ts index 17155b63d..9e0672019 100644 --- a/frontend/src/features/layersSelector/utils/LayerSelector.style.ts +++ b/frontend/src/features/layersSelector/utils/LayerSelector.style.ts @@ -11,34 +11,29 @@ const IconGroup = styled.span` ` const Layer = styled.span<{ $metadataIsShown?: boolean; $withBorderBottom?: boolean }>` - user-select: none; - display: flex; - text-align: left; - font-size: 13px; - padding-left: 20px; + align-items: center; background: ${p => (p.$metadataIsShown ? p.theme.color.blueYonder25 : 'transparent')}; + border-bottom: ${p => (p.$withBorderBottom ? 1 : 0)}px solid ${p => p.theme.color.lightGray}; color: ${p => p.theme.color.gunMetal}; + display: flex; + font-size: 13px; height: ${LAYER_SELECTOR_ROW_HEIGHT}px; - align-items: center; - border-bottom: ${p => (p.$withBorderBottom ? 1 : 0)}px solid ${p => p.theme.color.lightGray}; - + padding-left: 20px; + text-align: left; + user-select: none; :hover { background: ${p => p.theme.color.blueYonder25}; } ` -const Name = styled.span` - width: 280px; +const Name = styled.span<{ $withLargeWidth?: boolean }>` display: block; + margin-left: 8px; + overflow: hidden; + text-align: left; text-overflow: ellipsis; white-space: nowrap; - overflow-x: hidden !important; - font-size: inherit; - text-align: left; - span { - text-overflow: ellipsis; - white-space: nowrap; - } + width: ${p => (p.$withLargeWidth ? '500px' : '280px')}; ` const ZonesNumber = styled.span` @@ -53,19 +48,20 @@ const ZonesNumber = styled.span` ` const GroupWrapper = styled.li<{ $isOpen: boolean; $isPadded?: boolean }>` - display: flex; align-items: center; - user-select: none; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden !important; - height: ${LAYER_SELECTOR_ROW_HEIGHT}px; + border-bottom: ${p => (p.$isOpen ? 0 : 1)}px solid ${p => p.theme.color.lightGray}; + color: ${p => p.theme.color.gunMetal}; + display: flex; font-size: 13px; + font-weight: 700; + height: ${LAYER_SELECTOR_ROW_HEIGHT}px; + justify-content: space-between; + overflow: hidden !important; padding-left: 18px; padding-right: ${p => (p.$isPadded ? '8px' : '0')}; - font-weight: 700; - color: ${p => p.theme.color.gunMetal}; - border-bottom: ${p => (p.$isOpen ? 0 : 1)}px solid ${p => p.theme.color.lightGray}; + text-overflow: ellipsis; + user-select: none; + white-space: nowrap; :hover { background: ${p => p.theme.color.blueYonder25};