Scale regional data catalogs
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import { useEffect, useMemo, useState, type FormEvent } from 'react'
|
||||
import type { AreaRead, ProjectRead } from '../../types'
|
||||
|
||||
const AREA_CATALOG_PAGE_SIZE = 12
|
||||
|
||||
function projectDisplayName(project: ProjectRead | null): string {
|
||||
if (project?.name === 'Kempen Regional Workbench') {
|
||||
return 'Kempen · volledige regio'
|
||||
@@ -40,7 +42,30 @@ export function AreaPanel({
|
||||
onUpdateAreaForm,
|
||||
onSelectMapArea,
|
||||
}: AreaPanelProps): JSX.Element {
|
||||
const [catalogOpen, setCatalogOpen] = useState(false)
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const selectedArea = areas.find((area) => area.id === selectedMapAreaId)
|
||||
const filteredAreas = useMemo(() => {
|
||||
const query = searchQuery.trim().toLocaleLowerCase('nl-BE')
|
||||
if (!query) return areas
|
||||
return areas.filter((area) => area.name.toLocaleLowerCase('nl-BE').includes(query))
|
||||
}, [areas, searchQuery])
|
||||
const pageCount = Math.max(1, Math.ceil(filteredAreas.length / AREA_CATALOG_PAGE_SIZE))
|
||||
const visibleAreas = filteredAreas.slice(
|
||||
(page - 1) * AREA_CATALOG_PAGE_SIZE,
|
||||
page * AREA_CATALOG_PAGE_SIZE,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setPage((current) => Math.min(current, pageCount))
|
||||
}, [pageCount])
|
||||
|
||||
useEffect(() => {
|
||||
setSearchQuery('')
|
||||
setPage(1)
|
||||
setCatalogOpen(false)
|
||||
}, [selectedProjectId])
|
||||
|
||||
return (
|
||||
<section data-testid="area-panel">
|
||||
@@ -94,28 +119,73 @@ export function AreaPanel({
|
||||
|
||||
{loadingAreas ? <p>Gebieden laden...</p> : null}
|
||||
{areas.length === 0 ? <p>Nog geen gebieden beschikbaar.</p> : null}
|
||||
<details className="data-panel-list-block area-catalog-disclosure">
|
||||
<details
|
||||
className="data-panel-list-block area-catalog-disclosure"
|
||||
onToggle={(event) => setCatalogOpen(event.currentTarget.open)}
|
||||
>
|
||||
<summary>{areas.length} beschikbare gemeenten en regiogrenzen</summary>
|
||||
<ul className="entity-list">
|
||||
{areas.map((area) => (
|
||||
<li className={selectedMapAreaId === area.id ? 'entity-card entity-card-active' : 'entity-card'} key={area.id}>
|
||||
<strong>{area.name}</strong>
|
||||
<div className="entity-meta">
|
||||
<span>{area.area_m2 ? `${(area.area_m2 / 1_000_000).toLocaleString('nl-BE', { maximumFractionDigits: 2 })} km2` : 'Oppervlakte onbekend'}</span>
|
||||
<span>{area.geometry?.type ?? area.geometry_type ?? 'Geometrie onbekend'}</span>
|
||||
{catalogOpen ? (
|
||||
<>
|
||||
<div className="catalog-browser-toolbar">
|
||||
<label className="catalog-search-field">
|
||||
Zoek gemeente of regio
|
||||
<input
|
||||
type="search"
|
||||
value={searchQuery}
|
||||
onChange={(event) => {
|
||||
setSearchQuery(event.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
placeholder="Bijvoorbeeld Mol"
|
||||
/>
|
||||
</label>
|
||||
<div className="catalog-pagination" aria-label="Paginering van gebieden">
|
||||
<span>{filteredAreas.length} gevonden</span>
|
||||
<button
|
||||
className="secondary-action"
|
||||
type="button"
|
||||
disabled={page === 1}
|
||||
onClick={() => setPage((current) => Math.max(1, current - 1))}
|
||||
>
|
||||
Vorige
|
||||
</button>
|
||||
<strong>{page} / {pageCount}</strong>
|
||||
<button
|
||||
className="secondary-action"
|
||||
type="button"
|
||||
disabled={page === pageCount}
|
||||
onClick={() => setPage((current) => Math.min(pageCount, current + 1))}
|
||||
>
|
||||
Volgende
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="secondary-action"
|
||||
type="button"
|
||||
onClick={() => onSelectMapArea(area.id)}
|
||||
disabled={!area.geometry}
|
||||
data-testid={`area-show-${area.id}`}
|
||||
>
|
||||
{selectedMapAreaId === area.id ? 'Op kaart' : 'Toon op kaart'}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{visibleAreas.length > 0 ? (
|
||||
<ul className="entity-list">
|
||||
{visibleAreas.map((area) => (
|
||||
<li className={selectedMapAreaId === area.id ? 'entity-card entity-card-active' : 'entity-card'} key={area.id}>
|
||||
<strong>{area.name}</strong>
|
||||
<div className="entity-meta">
|
||||
<span>{area.area_m2 ? `${(area.area_m2 / 1_000_000).toLocaleString('nl-BE', { maximumFractionDigits: 2 })} km2` : 'Oppervlakte onbekend'}</span>
|
||||
<span>{area.geometry?.type ?? area.geometry_type ?? 'Geometrie onbekend'}</span>
|
||||
</div>
|
||||
<button
|
||||
className="secondary-action"
|
||||
type="button"
|
||||
onClick={() => onSelectMapArea(area.id)}
|
||||
disabled={!area.geometry}
|
||||
data-testid={`area-show-${area.id}`}
|
||||
>
|
||||
{selectedMapAreaId === area.id ? 'Op kaart' : 'Toon op kaart'}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p className="muted">Geen gebied gevonden voor deze zoekterm.</p>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</details>
|
||||
</section>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user