Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions server/src/graphql/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,9 @@ module.exports = {
})
}
})

return filtered
}
return scanAreas
return scanAreas.filter((parent) => parent.children.length)
}
return []
},
Expand Down
101 changes: 101 additions & 0 deletions src/components/layout/drawer/AreaTile.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from 'react'
import { Grid, MenuItem, Typography, Checkbox } from '@material-ui/core'
import { useMap } from 'react-leaflet'

import Utility from '@services/Utility'
import { useStore } from '@hooks/useStore'

export default function AreaTile({
name,
feature,
childAreas,
scanAreasZoom,
allAreas,
i,
}) {
const { scanAreas } = useStore((s) => s.filters)
const setAreas = useStore((s) => s.setAreas)
const map = useMap()

if (!scanAreas) return null

const hasAll =
childAreas &&
childAreas.every((c) => scanAreas.filter.areas.includes(c.properties.name))
const hasSome =
childAreas &&
childAreas.some((c) => scanAreas.filter.areas.includes(c.properties.name))
const hasManual = childAreas
? childAreas.some((c) => c.properties.manual)
: feature.properties.manual

return (
<Grid
item
xs={
name || (childAreas.length % 2 === 1 && i === childAreas.length - 1)
? 12
: 6
}
style={{
border: `1px solid ${feature?.properties?.color || 'grey'}`,
backgroundColor: feature?.properties?.fillColor || 'none',
}}
>
<MenuItem
onClick={() => {
if (feature?.properties) {
map.flyTo(
feature.properties.center,
feature.properties.zoom || scanAreasZoom,
)
}
}}
>
<Grid container alignItems="center" justifyContent="space-between">
<Grid
item
// eslint-disable-next-line no-nested-ternary
xs={hasManual ? 12 : name ? 11 : 10}
style={{ textAlign: 'center' }}
>
<Typography
variant={name ? 'h6' : 'caption'}
align="center"
style={{ width: '100%', fontWeight: 'bold' }}
>
{name || feature.properties.name ? (
Utility.getProperName(name || feature.properties.name)
) : (
<>&nbsp;</>
)}
</Typography>
</Grid>
{!hasManual && (
<Grid item xs={name ? 1 : 2} style={{ textAlign: 'right' }}>
<Checkbox
size="small"
indeterminate={name ? hasSome && !hasAll : false}
checked={
name
? hasAll
: scanAreas.filter.areas.includes(feature.properties.name)
}
onChange={() =>
setAreas(
name
? childAreas.map((c) => c.properties.name)
: feature.properties.name,
allAreas,
name ? hasSome : false,
)
}
disabled={!childAreas.length}
/>
</Grid>
)}
</Grid>
</MenuItem>
</Grid>
)
}
187 changes: 67 additions & 120 deletions src/components/layout/drawer/Areas.jsx
Original file line number Diff line number Diff line change
@@ -1,136 +1,83 @@
import React from 'react'
import { useQuery } from '@apollo/client'
import { Grid, Paper, MenuItem, Typography, Checkbox } from '@material-ui/core'
import { useMap } from 'react-leaflet'
import { Grid, Button, Paper } from '@material-ui/core'
import { useTranslation } from 'react-i18next'

import Utility from '@services/Utility'
import Query from '@services/Query'
import { useStore } from '@hooks/useStore'
import AreaTile from './AreaTile'

export default function AreaDropDown({ scanAreaMenuHeight, scanAreasZoom }) {
const { data, loading, error } = useQuery(Query.scanAreasMenu())
const map = useMap()
const { scanAreas } = useStore((s) => s.filters)
const { t } = useTranslation()
const setAreas = useStore((s) => s.setAreas)

const allAreas = React.useMemo(() => {
if (data?.scanAreasMenu) {
return data.scanAreasMenu.flatMap((parent) =>
parent.children.map((child) => child.properties.name),
)
}
return []
}, [data])

if (loading || error) return null

return (
<Paper
style={{
minHeight: 50,
maxHeight: scanAreaMenuHeight || 400,
width: '100%',
overflow: 'auto',
backgroundColor: '#212121',
}}
>
{data?.scanAreasMenu?.map(({ name, details, children }) => (
<Grid
key={name || ''}
container
alignItems="center"
justifyContent="center"
<>
<Grid
item
xs={t('drawer_grid_advanced_width')}
style={{ textAlign: 'center' }}
>
<Button
onClick={() => setAreas()}
variant="contained"
color="primary"
style={{ minWidth: '80%' }}
>
{Boolean(name) && (
<Grid
item
xs={12}
onClick={
details
? () => {
if (details?.properties) {
map.flyTo(
details.properties.center,
details.properties.zoom || scanAreasZoom,
)
}
}
: undefined
}
style={{
border: `1px solid ${details?.properties?.color || 'grey'}`,
backgroundColor: details?.properties?.fillColor || 'none',
}}
>
<MenuItem disabled={!details}>
<Typography
variant="h6"
align="center"
style={{ width: '100%' }}
>
{Utility.getProperName(name)}
</Typography>
</MenuItem>
</Grid>
)}
{children.map((feat, i) => (
<Grid
key={feat.properties.name}
item
xs={
children.length % 2 === 1 && i === children.length - 1 ? 12 : 6
}
onClick={() => {
if (feat.properties?.center) {
map.flyTo(
feat.properties.center,
feat.properties.zoom || scanAreasZoom,
)
}
}}
style={{
border: `1px solid ${feat.properties.color || 'grey'}`,
backgroundColor: feat.properties.fillColor || 'none',
}}
>
<MenuItem disabled={!feat.properties.name}>
<Grid
container
alignItems="center"
justifyContent="space-between"
>
<Grid item xs={10} style={{ textAlign: 'center' }}>
<Typography
variant="caption"
align="center"
style={{ width: '100%', fontWeight: 'bold' }}
onClick={() => {
if (feat.properties?.center) {
map.flyTo(
feat.properties.center,
feat.properties.zoom || scanAreasZoom,
)
}
}}
>
{feat.properties.name ? (
Utility.getProperName(feat.properties.name)
) : (
<>&nbsp;</>
)}
</Typography>
</Grid>
<Grid item xs={2} style={{ textAlign: 'right' }}>
<Checkbox
size="small"
checked={scanAreas.filter.areas.includes(
feat.properties.name,
)}
style={{
color: feat.properties.name ? 'none' : '#212121',
display: feat.properties.manual ? 'none' : 'block',
}}
onChange={() => setAreas(feat.properties.name)}
disabled={!feat.properties.name || feat.properties.manual}
/>
</Grid>
</Grid>
</MenuItem>
</Grid>
))}
</Grid>
))}
</Paper>
{t('reset')}
</Button>
</Grid>
<Paper
style={{
minHeight: 50,
maxHeight: scanAreaMenuHeight || 400,
width: '100%',
overflow: 'auto',
backgroundColor: '#212121',
}}
>
{data?.scanAreasMenu?.map(({ name, details, children }) => (
<Grid
key={name || ''}
container
alignItems="center"
justifyContent="center"
>
{name && (
<AreaTile
key={name}
name={name}
feature={details}
allAreas={allAreas}
childAreas={children}
scanAreasZoom={scanAreasZoom}
/>
)}
{children.map((feature, i) => (
<AreaTile
key={feature?.properties?.name || i}
feature={feature}
allAreas={allAreas}
childAreas={children}
scanAreasZoom={scanAreasZoom}
i={i}
/>
))}
</Grid>
))}
</Paper>
</>
)
}
19 changes: 15 additions & 4 deletions src/hooks/useStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,19 @@ export const useStore = create(
setZoom: (zoom) => set({ zoom }),
filters: undefined,
setFilters: (filters) => set({ filters }),
setAreas: (area) => {
setAreas: (areas = [], validAreas = [], unselectAll = false) => {
const { filters } = get()
const incoming = new Set(Array.isArray(areas) ? areas : [areas])
const existing = new Set(filters?.scanAreas?.filter?.areas || [])

incoming.forEach((area) => {
if (existing.has(area) || unselectAll) {
existing.delete(area)
} else {
existing.add(area)
}
})

if (filters?.scanAreas?.filter?.areas) {
set({
filters: {
Expand All @@ -20,9 +31,9 @@ export const useStore = create(
...filters.scanAreas,
filter: {
...filters.scanAreas.filter,
areas: filters.scanAreas.filter.areas.includes(area)
? filters.scanAreas.filter.areas.filter((a) => a !== area)
: [...filters.scanAreas.filter.areas, area],
areas: [...existing].filter((area) =>
validAreas.includes(area),
),
},
},
},
Expand Down