Skip to content

Commit

Permalink
[Dashboard] display regulatory areas in dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
claire2212 committed Sep 20, 2024
1 parent f17004f commit 1d22b1f
Show file tree
Hide file tree
Showing 16 changed files with 349 additions and 94 deletions.
8 changes: 4 additions & 4 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ type AccordionProps = {

export function Accordion({ children, headerButton, isExpanded, setExpandedAccordion, title }: AccordionProps) {
return (
<AccordionContainer $withCursor={!headerButton} onClick={!headerButton ? setExpandedAccordion : undefined}>
<AccordionHeader>
<AccordionContainer $withCursor={!headerButton}>
<AccordionHeader onClick={!headerButton ? setExpandedAccordion : undefined}>
<TitleContainer>
<Title>{title}</Title>
{headerButton}
Expand All @@ -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)')};
Expand Down Expand Up @@ -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;
`
Original file line number Diff line number Diff line change
@@ -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<HTMLSpanElement>()

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 (
<LayerSelector.Layer
ref={ref}
// $metadataIsShown={metadataIsShown}
onClick={toggleZoneMetadata}
>
<LayerLegend
layerType={MonitorEnvLayers.REGULATORY_ENV}
legendKey={layer?.entity_name ?? 'aucun'}
type={layer?.thematique ?? 'aucun'}
/>
<LayerSelector.Name $withLargeWidth onClick={fitToRegulatoryLayer} title={layer?.entity_name}>
{layer?.entity_name ?? 'AUCUN NOM'}
</LayerSelector.Name>

<LayerSelector.IconGroup>
<IconButton
accent={Accent.TERTIARY}
aria-label="Sélectionner la zone"
color={isZoneSelected ? THEME.color.blueGray : THEME.color.slateGray}
data-cy="regulatory-zone-check"
Icon={isZoneSelected ? Icon.PinFilled : Icon.Pin}
onClick={handleSelectZone}
/>
</LayerSelector.IconGroup>
</LayerSelector.Layer>
)
}
Original file line number Diff line number Diff line change
@@ -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 (
<>
<LayerSelector.GroupWrapper $isOpen={forceZonesAreOpen || zonesAreOpen} onClick={clickOnGroupZones}>
<LayerSelector.GroupName data-cy="result-group" title={groupName}>
{getTitle(groupName) ?? ''}
</LayerSelector.GroupName>
<LayerSelector.IconGroup>
<LayerSelector.ZonesNumber>{`${layerIds.length}/${totalNumberOfZones}`}</LayerSelector.ZonesNumber>

<IconButton
accent={Accent.TERTIARY}
aria-label="Sélectionner la/les zone(s)"
color={allTopicZonesAreChecked ? THEME.color.blueGray : THEME.color.slateGray}
Icon={allTopicZonesAreChecked ? Icon.PinFilled : Icon.Pin}
onClick={handleCheckAllZones}
title="Sélectionner la/les zone(s)"
/>
</LayerSelector.IconGroup>
</LayerSelector.GroupWrapper>
<LayerSelector.SubGroup isOpen={forceZonesAreOpen || zonesAreOpen} length={layerIds?.length}>
{layerIds?.map(layerId => (
<Layer key={layerId} layerId={layerId} />
))}
</LayerSelector.SubGroup>
</>
)
}
Original file line number Diff line number Diff line change
@@ -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 (
<Accordion isExpanded={isExpanded} setExpandedAccordion={setExpandedAccordion} title="Zones règlementaires">
<StyledLayerList
$baseLayersLength={Object.values(regulatoryAreasByLayerName).length}
$maxHeight={100}
$showBaseLayers={isExpanded}
>
{Object.entries(regulatoryAreasByLayerName).map(([layerGroupName, layerIdsInGroup]) => (
<ListLayerGroup key={layerGroupName} groupName={layerGroupName} layerIds={layerIdsInGroup} />
))}
</StyledLayerList>
</Accordion>
)
}

const StyledLayerList = styled(LayerSelector.LayerList)`
height: auto;
`
Loading

0 comments on commit 1d22b1f

Please sign in to comment.