diff --git a/frontend/src/pages/map/drawer/MapDrawer.test.tsx b/frontend/src/pages/map/drawer/MapDrawer.test.tsx
index bf600f43..ee7d78a4 100644
--- a/frontend/src/pages/map/drawer/MapDrawer.test.tsx
+++ b/frontend/src/pages/map/drawer/MapDrawer.test.tsx
@@ -228,7 +228,7 @@ describe('Test suite for MapDrawer', () => {
await waitFor(() => expect(div).toHaveClass('map-bottom-drawer--expanded'))
expect(state.bottomDrawerHeight).toBe(MAP_BOTTOM_DRAWER_HEIGHT_SMALL)
expect(state.activeTool).toBe(ActiveToolEnum.pointSearch)
- screen.getByText('Point Search')
+ screen.getByText('Radius Search')
screen.getByRole('slider', { name: 'Search radius' })
// Swipe up to make full height
diff --git a/frontend/src/pages/map/layers/PointSearchLayer.tsx b/frontend/src/pages/map/layers/PointSearchLayer.tsx
index bc7abc18..80b2c7ca 100644
--- a/frontend/src/pages/map/layers/PointSearchLayer.tsx
+++ b/frontend/src/pages/map/layers/PointSearchLayer.tsx
@@ -8,6 +8,7 @@ import {
setPointFilterCenter,
usePointFilterCenter,
usePointFilterRadius,
+ usePointFilterFinished,
} from '@/features/omrr/omrr-slice'
import { useMapCrosshairsCursor } from '../hooks/useMapCrosshairsCursor'
import { CrosshairsTooltipMarker } from './CrosshairsTooltipMarker'
@@ -24,22 +25,27 @@ function CircleLayer() {
const dispatch = useDispatch()
const center = usePointFilterCenter()
const radius = usePointFilterRadius()
+ const finished = usePointFilterFinished()
const map = useMap()
useMapCrosshairsCursor(map)
useMapEvents({
click: (ev: LeafletMouseEvent) => {
- const newCenter: LatLngTuple = [ev.latlng.lat, ev.latlng.lng]
- dispatch(setPointFilterCenter(newCenter))
+ if (!finished) {
+ const newCenter: LatLngTuple = [ev.latlng.lat, ev.latlng.lng]
+ dispatch(setPointFilterCenter(newCenter))
+ }
},
})
const drawCircle = center && radius > 0
return (
<>
-
- Click to place center point
-
+ {!finished && (
+
+ Click to place center point
+
+ )}
{drawCircle && (
)}
>
diff --git a/frontend/src/pages/map/search/AutocompleteItem.tsx b/frontend/src/pages/map/search/AutocompleteItem.tsx
index f07de78b..ea3ca08f 100644
--- a/frontend/src/pages/map/search/AutocompleteItem.tsx
+++ b/frontend/src/pages/map/search/AutocompleteItem.tsx
@@ -41,7 +41,7 @@ function getLabel(matchType: MatchType, item: OmrrData | undefined): ReactNode {
const { 'Authorization Number': number = 0 } = item
return (
- Authorization #: {number}
+ Authorization #: {number}
)
}
diff --git a/frontend/src/pages/map/search/MapSearch.test.tsx b/frontend/src/pages/map/search/MapSearch.test.tsx
index 8cde9240..33a089cd 100644
--- a/frontend/src/pages/map/search/MapSearch.test.tsx
+++ b/frontend/src/pages/map/search/MapSearch.test.tsx
@@ -14,7 +14,7 @@ describe('Test suite for MapSearch', () => {
screen.getByRole('button', { name: 'Find Me' })
screen.getByRole('button', { name: 'Layers' })
screen.getByRole('button', { name: 'Polygon Search' })
- screen.getByRole('button', { name: 'Point Search' })
+ screen.getByRole('button', { name: 'Radius Search' })
screen.getByText('Status')
screen.getByRole('button', { name: 'Filter' })
@@ -37,7 +37,7 @@ describe('Test suite for MapSearch', () => {
screen.queryByRole('button', { name: 'Layers' }),
).not.toBeInTheDocument()
screen.getByRole('button', { name: 'Polygon Search' })
- screen.getByRole('button', { name: 'Point Search' })
+ screen.getByRole('button', { name: 'Radius Search' })
screen.getByText('Status')
screen.getByRole('button', { name: 'Filter' })
})
diff --git a/frontend/src/pages/map/search/PointSearch.test.tsx b/frontend/src/pages/map/search/PointSearch.test.tsx
index 31e05283..c4431f83 100644
--- a/frontend/src/pages/map/search/PointSearch.test.tsx
+++ b/frontend/src/pages/map/search/PointSearch.test.tsx
@@ -55,7 +55,7 @@ describe('Test suite for PointSearch', () => {
const setRadiusBtn = screen.getByRole('button', { name: 'Set Radius' })
await user.click(setRadiusBtn)
- screen.getByText(`${MIN_CIRCLE_RADIUS} m`)
+ screen.getByText(`${MIN_CIRCLE_RADIUS / 1000} km`)
const slider = screen.getByRole('slider', { name: 'Search radius' })
fireEvent.change(slider, { target: { value: 1000 } })
expect(state.pointFilterCenter).toBeUndefined()
@@ -68,7 +68,7 @@ describe('Test suite for PointSearch', () => {
expect(screen.queryByText('Cancel')).not.toBeInTheDocument()
screen.getByText('Set Radius:')
const slider = screen.getByRole('slider', { name: 'Search radius' })
- screen.getByText(`${MIN_CIRCLE_RADIUS} m`)
+ screen.getByText(`${MIN_CIRCLE_RADIUS / 1000} km`)
fireEvent.change(slider, { target: { value: 2000 } })
expect(state.pointFilterCenter).toBeUndefined()
expect(state.pointFilterRadius).toBe(2000)
diff --git a/frontend/src/pages/map/search/PointSearch.tsx b/frontend/src/pages/map/search/PointSearch.tsx
index a065eda6..b47b4c2a 100644
--- a/frontend/src/pages/map/search/PointSearch.tsx
+++ b/frontend/src/pages/map/search/PointSearch.tsx
@@ -1,31 +1,132 @@
import { useDispatch } from 'react-redux'
-import { Button, Slider, Typography } from '@mui/material'
+import { Button, Slider, Typography, TextField } from '@mui/material'
import clsx from 'clsx'
+import { useEffect } from 'react'
import DropdownButton from '@/components/DropdownButton'
import { MIN_CIRCLE_RADIUS } from '@/constants/constants'
-import { clearActiveTool } from '@/features/map/map-slice'
+import {
+ clearActiveTool,
+ toggleActiveTool,
+ useActiveTool,
+ setRadiusActive,
+} from '@/features/map/map-slice'
import {
resetPointFilter,
setPointFilterRadius,
usePointFilterRadius,
+ usePointFilterActive,
+ usePointFilterFinished,
+ setPointFilterFinished,
+ setPointFilterUnfinished,
} from '@/features/omrr/omrr-slice'
import { formatDistance } from '@/utils/utils'
+import { ActiveToolEnum } from '@/constants/constants'
import CloseIcon from '@/assets/svgs/fa-close.svg?react'
+import CheckIcon from '@/assets/svgs/fa-check.svg?react'
interface Props {
isSmall?: boolean
className?: string
+ showControls?: boolean
}
-export function PointSearch({ isSmall = false, className }: Readonly
) {
+const styles = {
+ container: {
+ width: '100%',
+ display: 'flex',
+ justifyContent: 'center',
+ },
+ pointSearchContent: {
+ width: '100%',
+ maxWidth: '800px',
+ padding: '0 20px',
+ },
+ sliderContainer: ({ isSmall }: { isSmall: boolean }) => ({
+ display: 'flex',
+ flexDirection: isSmall ? ('column' as const) : ('row' as const),
+ gap: '20px',
+ alignItems: 'center',
+ }),
+ controlsRow: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '20px',
+ width: '100%',
+ justifyContent: 'space-between',
+ },
+ controlsRight: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '20px',
+ },
+ desktopContainer: {
+ display: 'flex',
+ alignItems: 'center',
+ gap: '20px',
+ },
+ sliderWrapper: {
+ width: '300px',
+ margin: '0 auto',
+ },
+ labelContainer: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ width: '100%',
+ },
+ textField: {
+ marginTop: '-10px',
+ '& input[type=number]': {
+ MozAppearance: 'textfield',
+ },
+ '& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button':
+ {
+ WebkitAppearance: 'none',
+ },
+ },
+ okButton: { marginTop: '-10px' },
+ input: {
+ textAlign: 'right',
+ paddingRight: '2px',
+ paddingLeft: '2px',
+ paddingTop: '2px',
+ paddingBottom: '2px',
+ },
+} as const
+
+export function PointSearch({
+ isSmall = false,
+ className,
+ showControls = true,
+}: Readonly) {
const dispatch = useDispatch()
const radius = usePointFilterRadius()
+ const activeTool = useActiveTool()
+ const isDrawing = activeTool === ActiveToolEnum.pointSearch
+ const isFilterActive = usePointFilterActive()
+ const finished = usePointFilterFinished()
+
+ useEffect(() => {
+ dispatch(setRadiusActive(true))
+ return () => {
+ dispatch(setRadiusActive(false))
+ }
+ }, [dispatch])
const onCancel = () => {
dispatch(resetPointFilter())
dispatch(clearActiveTool())
+ dispatch(setPointFilterUnfinished())
+ dispatch(setRadiusActive(false))
+ }
+
+ const onFinish = () => {
+ const mapContainer = document.querySelector('.map-container')
+ if (mapContainer) {
+ mapContainer.classList.remove('crosshairs-cursor')
+ }
+ dispatch(setPointFilterFinished())
}
const onRadiusChange = (_ev: any, value: number | number[]) => {
@@ -36,57 +137,162 @@ export function PointSearch({ isSmall = false, className }: Readonly) {
dispatch(setPointFilterRadius(newRadius))
}
+ const onInputChange = (event: React.ChangeEvent) => {
+ const value =
+ event.target.value === '' ? 1 : parseInt(event.target.value, 10)
+ if (!isNaN(value)) {
+ const newRadius = Math.min(
+ Math.max(value * 1000, MIN_CIRCLE_RADIUS),
+ 400000,
+ )
+ dispatch(setPointFilterRadius(newRadius))
+ }
+ }
+
const sliderBox = (
- {isSmall && (
-
- Set Radius:
-
+ {isSmall ? (
+
+
+
+ Set Radius:
+
+
+ km,
+ inputProps: {
+ min: 1,
+ max: 400,
+ inputMode: 'numeric',
+ style: styles.input,
+ },
+ }}
+ />
+ }
+ sx={{ ml: 1, ...styles.okButton }}
+ >
+ OK
+
+
+
+
+
+ ) : (
+
+
+
km,
+ inputProps: {
+ min: 1,
+ max: 400,
+ inputMode: 'numeric',
+ style: styles.input,
+ },
+ }}
+ />
+
)}
-
-
- {formatDistance(radius, 1)}
-
)
return isSmall ? (
- sliderBox
+
) : (
- }
- >
- Cancel
-
-
- Set Radius
-
+ {showControls && (
+ <>
+ }
+ >
+ Cancel
+
+
+ Set Radius
+
+ }
+ >
+ OK
+
+ >
+ )}
)
}
diff --git a/frontend/src/pages/map/search/PointSearchButton.tsx b/frontend/src/pages/map/search/PointSearchButton.tsx
index f691add6..f9507535 100644
--- a/frontend/src/pages/map/search/PointSearchButton.tsx
+++ b/frontend/src/pages/map/search/PointSearchButton.tsx
@@ -6,6 +6,9 @@ import { ActiveToolEnum } from '@/constants/constants'
import {
resetPointFilter,
resetPolygonFilter,
+ usePointFilterActive,
+ setPointFilterUnfinished,
+ usePointFilterFinished,
} from '@/features/omrr/omrr-slice'
import { toggleActiveTool } from '@/features/map/map-slice'
@@ -17,15 +20,17 @@ interface Props {
export function PointSearchButton({ isActive }: Readonly) {
const dispatch = useDispatch()
+ const isFilterActive = usePointFilterActive()
+ const isFinished = usePointFilterFinished()
const onClick = () => {
- if (isActive) {
- // Turn off point search
- dispatch(resetPointFilter())
- } else {
- // starting point search - make sure the polygon filter is turned off
+ dispatch(resetPointFilter())
+ dispatch(setPointFilterUnfinished())
+
+ if (!isActive) {
dispatch(resetPolygonFilter())
}
+
dispatch(toggleActiveTool(ActiveToolEnum.pointSearch))
}
@@ -42,10 +47,26 @@ export function PointSearchButton({ isActive }: Readonly) {
)}
onClick={onClick}
startIcon={
-
+
+
+ {isFilterActive && (
+
+ )}
+
}
>
- Point Search
+ Radius Search
)
}
diff --git a/frontend/src/pages/map/search/PolygonSearchButton.tsx b/frontend/src/pages/map/search/PolygonSearchButton.tsx
index 59110654..ceb435be 100644
--- a/frontend/src/pages/map/search/PolygonSearchButton.tsx
+++ b/frontend/src/pages/map/search/PolygonSearchButton.tsx
@@ -7,6 +7,7 @@ import { toggleActiveTool } from '@/features/map/map-slice'
import {
resetPointFilter,
resetPolygonFilter,
+ usePolygonFilterPositions,
} from '@/features/omrr/omrr-slice'
import PolygonIcon from '@/assets/svgs/fa-polygon.svg?react'
@@ -17,6 +18,8 @@ interface Props {
export function PolygonSearchButton({ isActive }: Readonly) {
const dispatch = useDispatch()
+ const positions = usePolygonFilterPositions()
+ const isFilterActive = positions.length > 0
const onClick = () => {
if (isActive) {
@@ -39,10 +42,26 @@ export function PolygonSearchButton({ isActive }: Readonly) {
isActive && 'map-button--active',
)}
startIcon={
-
+
+
+ {isFilterActive && (
+
+ )}
+
}
onClick={onClick}
>