Skip to content

Commit

Permalink
feat: add a button to erase pending geom to draw modal
Browse files Browse the repository at this point in the history
  • Loading branch information
maximeperrault authored and maximeperraultdev committed Nov 28, 2024
1 parent d01bf0b commit 04c69be
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 36 deletions.
2 changes: 1 addition & 1 deletion frontend/src/domain/entities/layers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function addGeometryToMultiGeometryGeoJSON(

export function keepOnlyInitialGeometriesOfMultiGeometry(
multiGeometry: GeoJSONType.Geometry,
initialFeatureNumber: number
initialFeatureNumber?: number
): GeoJSONType.Geometry | undefined {
const nextGeometry = new GeoJSON({
featureProjection: OPENLAYERS_PROJECTION
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/domain/shared_slices/Draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ const drawReducerSlice = createSlice({
state.initialGeometry = undefined
},

setGeometry(state, action: PayloadAction<GeoJSON.Geometry>) {
setGeometry(state, action: PayloadAction<GeoJSON.Geometry | undefined>) {
state.geometry = action.payload
state.isGeometryValid = isGeometryValid(action.payload)
},
/**
* Save the initial geometry value from form if user want to reinitialize the geometry
*/
setInitialGeometry(state, action: PayloadAction<GeoJSON.Geometry>) {
setInitialGeometry(state, action: PayloadAction<GeoJSON.Geometry | undefined>) {
state.initialGeometry = action.payload
},

Expand Down
22 changes: 13 additions & 9 deletions frontend/src/domain/use_cases/draw/eraseDrawedGeometries.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { keepOnlyInitialGeometriesOfMultiGeometry } from '../../entities/layers'
import { setGeometry } from '../../shared_slices/Draw'

export const eraseDrawedGeometries = initialFeatureNumber => (dispatch, getState) => {
const { geometry } = getState().draw
if (!geometry) {
return
}
import type { HomeAppThunk } from '@store/index'

export const eraseDrawedGeometries =
(initialFeatureNumber?: number): HomeAppThunk =>
(dispatch, getState) => {
const { geometry } = getState().draw
if (!geometry) {
return
}

const nextGeometry = keepOnlyInitialGeometriesOfMultiGeometry(geometry, initialFeatureNumber)
if (nextGeometry) {
dispatch(setGeometry(nextGeometry))
const nextGeometry = keepOnlyInitialGeometriesOfMultiGeometry(geometry, initialFeatureNumber)
if (nextGeometry) {
dispatch(setGeometry(nextGeometry))
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { dashboardActions } from '@features/Dashboard/slice'
import { useAppDispatch } from '@hooks/useAppDispatch'
import { useListenForDrawedGeometry } from '@hooks/useListenForDrawing'
import { Accent, Icon, IconButton, OPENLAYERS_PROJECTION, THEME, WSG84_PROJECTION } from '@mtes-mct/monitor-ui'
Expand All @@ -13,10 +14,11 @@ import type { GeoJSON } from 'domain/types/GeoJSON'
import type { Coordinate } from 'ol/coordinate'

type EditAreaProps = {
dashboardKey: string
geometry: GeoJSON.Geometry | undefined
onValidate: (geometry: GeoJSON.Geometry) => void
}
export function EditArea({ geometry, onValidate }: EditAreaProps) {
export function EditArea({ dashboardKey, geometry, onValidate }: EditAreaProps) {
const [geometryToSave, setGeometryToSave] = useState<GeoJSON.Geometry | undefined>(undefined)
const { geometry: geometryInProgress, interactionType } = useListenForDrawedGeometry(
InteractionListener.DASHBOARD_ZONE
Expand All @@ -30,18 +32,20 @@ export function EditArea({ geometry, onValidate }: EditAreaProps) {
}

const handleEditArea = useCallback(() => {
dispatch(drawCircle(undefined, InteractionListener.DASHBOARD_ZONE))
}, [dispatch])
dispatch(drawCircle(geometry, InteractionListener.DASHBOARD_ZONE))
}, [dispatch, geometry])

useEffect(() => {
if (interactionType) {
setGeometryToSave(geometryInProgress)
dispatch(dashboardActions.setDisplayGeometry({ key: dashboardKey, visible: false }))
}
}, [dispatch, geometryInProgress, interactionType])
}, [dashboardKey, dispatch, geometryInProgress, interactionType])

useEffect(() => {
if (geometryToSave && (geometryToSave as GeoJSON.MultiPolygon).coordinates.length > 0 && !interactionType) {
onValidate(geometryToSave)
dispatch(dashboardActions.setDisplayGeometry({ key: dashboardKey, visible: true }))
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [interactionType, geometryToSave])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const Toolbar = forwardRef<HTMLDivElement, ToolbarProps>(
return (
<Wrapper ref={ref}>
<EditArea
dashboardKey={key}
geometry={geometry}
onValidate={geometryToSave => {
dispatch(editDashboardArea(geometryToSave, key))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export function ActiveDashboardLayer({ map }: BaseMapChildrenProps) {
const displayDashboardLayer = useAppSelector(state => state.global.displayDashboardLayer)

const activeDashboardId = useAppSelector(state => state.dashboard.activeDashboardId)
const displayGeometry = useAppSelector(state =>
activeDashboardId ? state.dashboard.dashboards?.[activeDashboardId]?.displayGeometry : false
)

const dashboard = useAppSelector(state => getDashboardById(state.dashboard, activeDashboardId))
const { data: reportings } = useGetReportingsByIdsQuery(dashboard?.dashboard.reportingIds ?? [], {
Expand Down Expand Up @@ -168,7 +171,7 @@ export function ActiveDashboardLayer({ map }: BaseMapChildrenProps) {
}
}

if (dashboard?.dashboard.geom) {
if (dashboard?.dashboard.geom && displayGeometry) {
const dashboardAreaFeature = getFeature(dashboard.dashboard.geom)
if (!dashboardAreaFeature) {
return
Expand All @@ -192,7 +195,8 @@ export function ActiveDashboardLayer({ map }: BaseMapChildrenProps) {
vigilanceAreas?.entities,
reportings,
dashboard?.dashboard.geom,
drawBorder
drawBorder,
displayGeometry
])

useEffect(() => {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/features/Dashboard/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const initialDashboard: DashboardType = {
vigilanceAreaIds: []
},
defaultName: '',
displayGeometry: true,
extractedArea: undefined,
isCancelEditModalOpen: false,
isEditingTabName: false,
Expand All @@ -43,6 +44,7 @@ export type DashboardType = {
ampIdsToDisplay: number[]
dashboard: Dashboard.Dashboard
defaultName: string | undefined
displayGeometry: boolean
extractedArea?: Dashboard.ExtractedArea
isCancelEditModalOpen: boolean
isEditingTabName: boolean
Expand Down Expand Up @@ -290,6 +292,15 @@ export const dashboardSlice = createSlice({
state.dashboards[id].openPanel = action.payload
}
},
setDisplayGeometry(state, action: PayloadAction<{ key: string; visible: boolean }>) {
const id = action.payload.key

if (!id || !state.dashboards[id]) {
return
}

state.dashboards[id].displayGeometry = action.payload.visible
},
setGeometry(state, action: PayloadAction<GeoJSON.Geometry | undefined>) {
state.geometry = action.payload
state.isGeometryValid = action.payload ? isGeometryValid(action.payload) : true
Expand Down
17 changes: 13 additions & 4 deletions frontend/src/features/commonComponents/Modals/MapInteraction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type MapInteractionProps = {
customTools?: React.ReactNode
isValidatedButtonDisabled?: boolean
onCancel?: () => void
onDelete?: () => void
onReset: () => void
onValidate: () => void
resetButtonText?: string
Expand All @@ -17,6 +18,7 @@ export function MapInteraction({
customTools = undefined,
isValidatedButtonDisabled = false,
onCancel,
onDelete,
onReset,
onValidate,
resetButtonText = undefined,
Expand All @@ -36,6 +38,11 @@ export function MapInteraction({
<ButtonRow $withCustomTools={!!customTools}>
{customTools}
<ButtonGroup>
{!!onDelete && (
<DeleteButton accent={Accent.SECONDARY} Icon={Icon.Delete} onClick={onDelete}>
Tout effacer
</DeleteButton>
)}
<Button accent={Accent.SECONDARY} onClick={onReset}>
{resetButtonText ?? 'Réinitialiser'}
</Button>
Expand Down Expand Up @@ -78,10 +85,8 @@ const Title = styled.h1`
`

const ButtonGroup = styled.div`
display: inline-block;
& > :not(:last-child) {
margin-right: 16px;
}
display: flex;
gap: 16px;
`

const ValidateButton = styled(Button)`
Expand All @@ -101,3 +106,7 @@ const Body = styled.div`
padding: 24px;
background-color: ${p => p.theme.color.white};
`

const DeleteButton = styled(Button)`
color: ${p => p.theme.color.maximumRed};
`
9 changes: 8 additions & 1 deletion frontend/src/features/map/draw/DrawModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type Coordinates, CoordinatesInput, IconButton, Icon, usePrevious } from '@mtes-mct/monitor-ui'
import { type Coordinates, CoordinatesInput, Icon, IconButton, usePrevious } from '@mtes-mct/monitor-ui'
import { getFeature } from '@utils/getFeature'
import Feature from 'ol/Feature'
import Point from 'ol/geom/Point'
Expand Down Expand Up @@ -53,6 +53,7 @@ export function DrawModal() {
const initialGeometry = useAppSelector(state => state.draw.initialGeometry)
const interactionType = useAppSelector(state => state.draw.interactionType)
const isGeometryValid = useAppSelector(state => state.draw.isGeometryValid)

const listener = useAppSelector(state => state.draw.listener)

const global = useAppSelector(state => state.global)
Expand Down Expand Up @@ -143,6 +144,11 @@ export function DrawModal() {
dispatch(setGeometry(initialGeometry))
}

const handleDelete = () => {
dispatch(eraseDrawedGeometries(initialFeatureNumberRef.current))
dispatch(setGeometry(undefined))
}

const handleValidate = () => {
dispatch(updateMapInteractionListeners(MapInteractionListenerEnum.NONE))
}
Expand Down Expand Up @@ -209,6 +215,7 @@ export function DrawModal() {
}
isValidatedButtonDisabled={!isGeometryValid}
onCancel={handleCancel}
onDelete={listener === InteractionListener.DASHBOARD_ZONE ? handleDelete : undefined}
onReset={handleReset}
onValidate={handleValidate}
title={`${listener && titlePlaceholder[listener]}`}
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/features/map/layers/DrawLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ function UnmemoizedDrawLayer({ map }: BaseMapChildrenProps) {
)

useEffect(() => {
vectorSourceRef.current.clear(true)
drawVectorSourceRef.current.clear(true)
if (isEmpty(feature) || !interactionType) {
return undefined
}

resetModifyInteractions(map)
vectorSourceRef.current.clear(true)
drawVectorSourceRef.current.clear(true)

vectorSourceRef.current.addFeature(feature)
const modify = new Modify({
source: vectorSourceRef.current
Expand Down
8 changes: 1 addition & 7 deletions frontend/src/hooks/useDrawVectorLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ type VectorLayerWithName = VectorLayer & { name?: string }

export function useDrawVectorLayer(geometry: any, layerName: string) {
// Memoize the feature creation
const feature = useMemo(() => {
if (!geometry) {
return undefined
}

return getFeature(geometry)
}, [geometry])
const feature = useMemo(() => getFeature(geometry), [geometry])

// Create the vector sources and layer references
const vectorSourceRef = useRef(
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/utils/geometryValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,18 @@ function isPolygonValid(polygon: Coordinate[][]) {
return true
}

function arePolygonsValid(polygons: Coordinate[][][]) {
function arePolygonsValid(polygons: Coordinate[][][] | undefined) {
return reduce(polygons, (result, polygon) => result && isPolygonValid(polygon), true)
}

export function isGeometryValid(geometry: GeoJSON.Geometry) {
if (geometry && geometry.type === OLGeometryType.MULTIPOLYGON) {
export function isGeometryValid(geometry: GeoJSON.Geometry | undefined) {
if (!geometry) {
return false
}
if (geometry.type === OLGeometryType.MULTIPOLYGON) {
return !!geometry.coordinates.length && arePolygonsValid(geometry.coordinates as Coordinate[][][])
}
if (geometry && geometry.type === OLGeometryType.POLYGON) {
if (geometry.type === OLGeometryType.POLYGON) {
return isPolygonValid(geometry.coordinates as Coordinate[][])
}

Expand Down

0 comments on commit 04c69be

Please sign in to comment.