feat: simplify regional end user workbench
This commit is contained in:
@@ -36,28 +36,28 @@ export function AreaPanel({
|
||||
<section data-testid="area-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Spatial scope</p>
|
||||
<h2>AOI manager</h2>
|
||||
<p className="eyebrow">Geografisch bereik</p>
|
||||
<h2>Gebieden</h2>
|
||||
</div>
|
||||
<span className="count-pill">{areas.length}</span>
|
||||
</div>
|
||||
<p className="muted">{selectedProject ? `Project: ${selectedProject.name}` : 'Select a project first'}</p>
|
||||
<p className="muted">{selectedProject ? selectedProject.name : 'Geen werkruimte actief'}</p>
|
||||
|
||||
<div className="data-selection-summary data-selection-summary-area">
|
||||
<span>Shown on map</span>
|
||||
<strong>{selectedArea?.name ?? 'No AOI selected'}</strong>
|
||||
<small>{selectedArea?.area_m2 ? `${selectedArea.area_m2.toFixed(2)} m2` : 'Select an AOI with geometry for spatial review.'}</small>
|
||||
<span>Getoond op de kaart</span>
|
||||
<strong>{selectedArea?.name ?? 'Geen gebied geselecteerd'}</strong>
|
||||
<small>{selectedArea?.area_m2 ? `${(selectedArea.area_m2 / 1_000_000).toLocaleString('nl-BE', { maximumFractionDigits: 2 })} km2` : 'Kies een gemeente of de volledige regio op de kaart.'}</small>
|
||||
</div>
|
||||
|
||||
<details className="data-panel-form-block">
|
||||
<summary>Create spatial scope</summary>
|
||||
<summary>Eigen gebied toevoegen (GeoJSON)</summary>
|
||||
<form className="compact-form" onSubmit={onCreateArea}>
|
||||
<label>
|
||||
AOI name
|
||||
Naam
|
||||
<input
|
||||
value={areaForm.name}
|
||||
onChange={(event) => onUpdateAreaForm({ ...areaForm, name: event.target.value })}
|
||||
placeholder="AOI name"
|
||||
placeholder="Naam van het gebied"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
@@ -69,7 +69,7 @@ export function AreaPanel({
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
GeoJSON geometry
|
||||
GeoJSON-geometrie
|
||||
<textarea
|
||||
value={areaForm.geometry}
|
||||
onChange={(event) => onUpdateAreaForm({ ...areaForm, geometry: event.target.value })}
|
||||
@@ -77,22 +77,22 @@ export function AreaPanel({
|
||||
/>
|
||||
</label>
|
||||
<button className="primary-action" type="submit" disabled={!selectedProjectId}>
|
||||
Create area
|
||||
Gebied toevoegen
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
{loadingAreas ? <p>Loading areas...</p> : null}
|
||||
{areas.length === 0 ? <p>No areas yet</p> : null}
|
||||
<div className="data-panel-list-block">
|
||||
<p className="data-section-label">Saved AOIs</p>
|
||||
{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">
|
||||
<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.toFixed(2)} m2` : 'n/a'}</span>
|
||||
<span>{area.geometry?.type ?? area.geometry_type ?? 'n/a'}</span>
|
||||
<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"
|
||||
@@ -101,12 +101,12 @@ export function AreaPanel({
|
||||
disabled={!area.geometry}
|
||||
data-testid={`area-show-${area.id}`}
|
||||
>
|
||||
{selectedMapAreaId === area.id ? 'Shown on map' : 'Show on map'}
|
||||
{selectedMapAreaId === area.id ? 'Op kaart' : 'Toon op kaart'}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { FormEvent } from 'react'
|
||||
import type { ProjectCreate, ProjectRead } from '../../types'
|
||||
|
||||
const TECHNICAL_PROJECT_PATTERN = /^(GeoIntel Detection Quality Matrix|GeoIntel hard-negative|GeoIntel training|Mol Building QA)/i
|
||||
|
||||
function isTechnicalProject(project: ProjectRead): boolean {
|
||||
return TECHNICAL_PROJECT_PATTERN.test(project.name)
|
||||
}
|
||||
|
||||
interface ProjectPanelProps {
|
||||
projects: ProjectRead[]
|
||||
selectedProjectId: string | null
|
||||
@@ -27,68 +33,33 @@ export function ProjectPanel({
|
||||
onLoadDemoWorkflow,
|
||||
}: ProjectPanelProps): JSX.Element {
|
||||
const selectedProject = projects.find((project) => project.id === selectedProjectId)
|
||||
const regularProjects = projects.filter((project) => !isTechnicalProject(project))
|
||||
const technicalProjects = projects.filter(isTechnicalProject)
|
||||
const visibleProjects = selectedProject && isTechnicalProject(selectedProject)
|
||||
? [selectedProject, ...regularProjects]
|
||||
: regularProjects
|
||||
|
||||
return (
|
||||
<section className="workspace-panel" data-testid="project-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Context</p>
|
||||
<h2>Projects</h2>
|
||||
<p className="eyebrow">Gegevenscontext</p>
|
||||
<h2>Werkruimtes</h2>
|
||||
</div>
|
||||
<span className="count-pill">{projects.length}</span>
|
||||
<span className="count-pill">{regularProjects.length} beschikbaar</span>
|
||||
</div>
|
||||
{loadingProjects ? <p>Loading projects...</p> : null}
|
||||
{loadingProjects ? <p>Werkruimtes laden...</p> : null}
|
||||
|
||||
<div className="data-selection-summary data-selection-summary-project">
|
||||
<span>Selected project</span>
|
||||
<strong>{selectedProject?.name ?? 'No active project'}</strong>
|
||||
<small>{selectedProject?.region ?? 'Create or load a project to start the workbench.'}</small>
|
||||
</div>
|
||||
|
||||
<details className="data-panel-form-block">
|
||||
<summary>Create project</summary>
|
||||
<form className="compact-form" onSubmit={onCreateProject}>
|
||||
<label>
|
||||
Name
|
||||
<input
|
||||
value={projectForm.name}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, name: event.target.value })}
|
||||
placeholder="Project name"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Description
|
||||
<input
|
||||
value={projectForm.description ?? ''}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, description: event.target.value })}
|
||||
placeholder="Description"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Region
|
||||
<input
|
||||
value={projectForm.region ?? 'Kempen'}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, region: event.target.value })}
|
||||
placeholder="Region"
|
||||
/>
|
||||
</label>
|
||||
<button className="primary-action" type="submit">
|
||||
Create project
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<div className="demo-actions">
|
||||
<button className="secondary-action" type="button" onClick={onLoadDemoWorkflow} disabled={loadingDemoWorkflow} data-testid="load-demo-workflow">
|
||||
{loadingDemoWorkflow ? 'Loading demo...' : 'Load demo workflow'}
|
||||
</button>
|
||||
{demoWorkflowMessage ? <p>{demoWorkflowMessage}</p> : null}
|
||||
<span>Actieve werkruimte</span>
|
||||
<strong>{selectedProject?.name ?? 'Geen werkruimte actief'}</strong>
|
||||
<small>{selectedProject?.region ?? 'De regionale werkruimte wordt automatisch gekozen.'}</small>
|
||||
</div>
|
||||
|
||||
<div className="data-panel-list-block">
|
||||
<p className="data-section-label">Saved projects</p>
|
||||
<p className="data-section-label">Beschikbare werkruimtes</p>
|
||||
<ul className="entity-list">
|
||||
{projects.map((project) => (
|
||||
{visibleProjects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<button
|
||||
className={project.id === selectedProjectId ? 'entity-card entity-card-active' : 'entity-card'}
|
||||
@@ -98,18 +69,78 @@ export function ProjectPanel({
|
||||
data-testid={`project-select-${project.id}`}
|
||||
>
|
||||
<strong>{project.name}</strong>
|
||||
<span>{project.region || 'No region'}</span>
|
||||
<span>{project.region || 'Regio niet opgegeven'}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{projects.length === 0 ? (
|
||||
{regularProjects.length === 0 ? (
|
||||
<li className="empty-state">
|
||||
<strong>No projects yet</strong>
|
||||
<p>Create a project or load the offline demo workflow to populate the workbench.</p>
|
||||
<strong>Nog geen werkruimte beschikbaar</strong>
|
||||
<p>Maak een werkruimte aan of laad de demonstratiegegevens.</p>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<details className="data-panel-form-block technical-management-block">
|
||||
<summary>Geavanceerd projectbeheer</summary>
|
||||
<div className="technical-management-body">
|
||||
<form className="compact-form" onSubmit={onCreateProject}>
|
||||
<label>
|
||||
Naam
|
||||
<input
|
||||
value={projectForm.name}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, name: event.target.value })}
|
||||
placeholder="Naam van de werkruimte"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Beschrijving
|
||||
<input
|
||||
value={projectForm.description ?? ''}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, description: event.target.value })}
|
||||
placeholder="Korte beschrijving"
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Regio
|
||||
<input
|
||||
value={projectForm.region ?? 'Kempen'}
|
||||
onChange={(event) => onUpdateProjectForm({ ...projectForm, region: event.target.value })}
|
||||
placeholder="Regio"
|
||||
/>
|
||||
</label>
|
||||
<button className="primary-action" type="submit">Nieuwe werkruimte</button>
|
||||
</form>
|
||||
<div className="demo-actions">
|
||||
<button className="secondary-action" type="button" onClick={onLoadDemoWorkflow} disabled={loadingDemoWorkflow} data-testid="load-demo-workflow">
|
||||
{loadingDemoWorkflow ? 'Demo laden...' : 'Demonstratiegegevens laden'}
|
||||
</button>
|
||||
{demoWorkflowMessage ? <p>{demoWorkflowMessage}</p> : null}
|
||||
</div>
|
||||
{technicalProjects.length > 0 ? (
|
||||
<details className="technical-run-list">
|
||||
<summary>{technicalProjects.length} technische test- en benchmarkruns</summary>
|
||||
<ul className="entity-list">
|
||||
{technicalProjects.map((project) => (
|
||||
<li key={project.id}>
|
||||
<button
|
||||
className={project.id === selectedProjectId ? 'entity-card entity-card-active' : 'entity-card'}
|
||||
type="button"
|
||||
onClick={() => onSelectProject(project.id)}
|
||||
aria-pressed={project.id === selectedProjectId}
|
||||
data-testid={`project-select-${project.id}`}
|
||||
>
|
||||
<strong>{project.name}</strong>
|
||||
<span>{project.region || 'Technische run'}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
) : null}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user