feat: simplify regional end user workbench
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-14 21:40:32 +02:00
parent 1693e4ca1f
commit b5bcd9b041
52 changed files with 1183 additions and 617 deletions
@@ -98,44 +98,45 @@ export function ExportCenter({
const canExportDataset = Boolean(selectedDataset && isVectorDatasetType(selectedDataset.dataset_type))
const geojsonExports = exports.filter((item) => item.export_type.includes('geojson')).length
const reportExports = exports.filter((item) => item.export_type === 'project_report_html').length
const handoffStatus = selectedProjectId ? 'project selected' : 'select project'
const handoffStatus = selectedProjectId ? 'werkruimte gekozen' : 'kies een werkruimte'
const exportTypes = useMemo(() => Array.from(new Set(exports.map((item) => item.export_type))).sort(), [exports])
const exportStatuses = useMemo(() => Array.from(new Set(exports.map((item) => item.status))).sort(), [exports])
const latestHandoffArtifacts = useMemo(
() => [
{
key: 'project_report_html',
label: 'Project report',
detail: 'HTML report for human review',
label: 'Leesbaar rapport',
detail: 'HTML-rapport om te bekijken en te delen',
item: getLatestExportByType(exports, 'project_report_html'),
},
{
key: 'project_metadata_json',
label: 'Project metadata',
detail: 'Machine-readable project inventory',
label: 'Werkruimtedata',
detail: 'Technische inventaris in JSON-formaat',
item: getLatestExportByType(exports, 'project_metadata_json'),
},
{
key: 'dataset_geojson',
label: 'Dataset GeoJSON',
detail: 'Selected vector/reference layer',
label: 'Kaartlaag (GeoJSON)',
detail: 'De geselecteerde vector- of referentielaag',
item: getLatestExportByType(exports, 'dataset_geojson'),
},
{
key: 'detection_geojson',
label: 'Detection GeoJSON',
detail: 'Persisted detection geometries',
label: 'Herkende gebouwen (GeoJSON)',
detail: 'Bewaarde geometrieën uit beeldanalyse',
item: getLatestExportByType(exports, 'detection_geojson'),
},
{
key: 'segmentation_geojson',
label: 'Segmentation GeoJSON',
detail: 'Persisted segmentation polygons',
label: 'Segmentaties (GeoJSON)',
detail: 'Bewaarde segmentatiepolygonen',
item: getLatestExportByType(exports, 'segmentation_geojson'),
},
],
[exports],
)
const availableLatestArtifacts = latestHandoffArtifacts.filter((artifact) => artifact.item)
const filteredExports = useMemo(
() =>
exports.filter((item) => {
@@ -154,10 +155,10 @@ export function ExportCenter({
<div className="export-center-shell">
<div className="panel-title-row">
<div>
<h2>Export Center</h2>
<p className="muted">Create handoff artifacts from persisted datasets, AI runs and project metadata.</p>
<h2>Downloads en rapporten</h2>
<p className="muted">Bewaar de geselecteerde kaartlaag, analyseresultaten of een leesbaar projectrapport.</p>
</div>
<span className="count-pill">{exports.length} exports</span>
<span className="count-pill">{exports.length} bestanden</span>
</div>
<div className="export-summary-surface" aria-label="Export summary">
@@ -167,12 +168,12 @@ export function ExportCenter({
<strong>{geojsonExports}</strong>
</div>
<div>
<span>Reports</span>
<span>Rapporten</span>
<strong>{reportExports}</strong>
</div>
<div>
<span>Selected dataset</span>
<strong>{selectedDataset?.name ?? 'none'}</strong>
<span>Geselecteerde kaartlaag</span>
<strong>{selectedDataset?.name ?? 'geen'}</strong>
</div>
</div>
</div>
@@ -188,20 +189,20 @@ export function ExportCenter({
</div>
<div className="handoff-readiness-grid">
<div>
<span>Vector dataset</span>
<strong>{canExportDataset ? selectedDataset?.name : 'none ready'}</strong>
<span>Vectorkaartlaag</span>
<strong>{canExportDataset ? selectedDataset?.name : 'geen beschikbaar'}</strong>
</div>
<div>
<span>Detection run</span>
<strong>{selectedDetectionRunId || 'none'}</strong>
<span>Gebouwanalyse</span>
<strong>{selectedDetectionRunId || 'geen'}</strong>
</div>
<div>
<span>Segmentation run</span>
<strong>{selectedSegmentationRunId || 'none'}</strong>
<span>Segmentatieanalyse</span>
<strong>{selectedSegmentationRunId || 'geen'}</strong>
</div>
<div>
<span>Latest handoff artifact</span>
<strong>{latestExport ? formatExportType(latestExport.export_type) : 'none'}</strong>
<span>Laatste bestand</span>
<strong>{latestExport ? formatExportType(latestExport.export_type) : 'geen'}</strong>
</div>
</div>
</div>
@@ -209,16 +210,16 @@ export function ExportCenter({
<div className="latest-artifact-surface" aria-label="Latest handoff artifacts">
<div>
<h3>Latest handoff artifacts</h3>
<p className="muted">Open the newest artifact of each handoff type without searching the full export history.</p>
<h3>Laatste downloads</h3>
<p className="muted">Open de recentste bestanden zonder in de volledige geschiedenis te zoeken.</p>
</div>
<div className="latest-artifact-grid">
{latestHandoffArtifacts.map((artifact) => {
{availableLatestArtifacts.map((artifact) => {
const item = artifact.item
return (
<div className={item ? 'latest-artifact-card' : 'latest-artifact-card latest-artifact-card-empty'} key={artifact.key}>
<span>{artifact.label}</span>
<strong>{item ? item.status : 'No artifact yet'}</strong>
<strong>{item ? (item.status === 'ready' ? 'Beschikbaar' : item.status) : 'Nog niet gemaakt'}</strong>
<p>{item ? compactPath(item.storage_path) : artifact.detail}</p>
{item ? (
<>
@@ -226,13 +227,13 @@ export function ExportCenter({
<div className="latest-artifact-actions">
{canPreviewJson(item) ? (
<button type="button" className="secondary-action" onClick={() => onPreviewContent(item.id)}>
Preview
Bekijken
</button>
) : (
<span className="status-badge">download-only</span>
<span className="status-badge">alleen downloaden</span>
)}
<button type="button" className="primary-action" onClick={() => onDownload(item.id)}>
Download
Downloaden
</button>
</div>
</>
@@ -240,19 +241,25 @@ export function ExportCenter({
</div>
)
})}
{availableLatestArtifacts.length === 0 ? (
<div className="result-state result-state-empty latest-download-empty">
<strong>Nog geen downloads gemaakt</strong>
<p>Kies hieronder een rapport of de actieve kaartlaag.</p>
</div>
) : null}
</div>
</div>
<div className="export-actions-surface" aria-label="Export artifact actions">
<div className="panel-title-row">
<div>
<h3>Artifact actions</h3>
<p className="muted">Create review artifacts from the current project, dataset or selected AI analysis run.</p>
<h3>Wat wil je bewaren?</h3>
<p className="muted">Maak een leesbaar rapport of download gegevens voor verder gebruik.</p>
</div>
</div>
<div className="export-action-grid">
<div className="handoff-action-grid">
<div className="handoff-action-card">
<div className="handoff-action-card handoff-action-card-technical">
<span>Refresh</span>
<strong>Export registry</strong>
<p>Reload the persisted artifact list for the selected project.</p>
@@ -267,17 +274,17 @@ export function ExportCenter({
</button>
</div>
<div className="handoff-action-card handoff-action-card-primary">
<span>Project report</span>
<strong>HTML summary</strong>
<p>Generate the human-readable V1 handoff report from persisted project state.</p>
<span>Rapport</span>
<strong>Leesbaar projectoverzicht</strong>
<p>Maak een HTML-rapport met de beschikbare bronnen, controles en resultaten.</p>
<button type="button" className="primary-action" onClick={onExportProjectReport} disabled={!selectedProjectId || exporting}>
Export project report HTML
Projectrapport maken
</button>
</div>
<div className="handoff-action-card">
<span>Metadata</span>
<strong>Project JSON</strong>
<p>Export project, dataset, QA and artifact metadata for machine-readable review.</p>
<span>Technische gegevens</span>
<strong>Projectgegevens (JSON)</strong>
<p>Download de volledige projectinventaris voor geautomatiseerde verwerking.</p>
<button
type="button"
className="secondary-action"
@@ -285,33 +292,33 @@ export function ExportCenter({
disabled={!selectedProjectId || exporting}
data-testid="export-project-metadata"
>
Export project metadata JSON
Projectgegevens downloaden
</button>
</div>
<div className="handoff-action-card">
<span>Vector dataset</span>
<strong>Selected GeoJSON</strong>
<p>Export the active vector/reference dataset when it is selected and ready.</p>
<span>Kaartlaag</span>
<strong>Geselecteerde bron (GeoJSON)</strong>
<p>Download de actieve vector- of referentielaag.</p>
<button type="button" className="secondary-action" onClick={onExportDataset} disabled={!canExportDataset || exporting}>
Export selected vector GeoJSON
Kaartlaag downloaden
</button>
</div>
<div className="handoff-action-card">
{selectedDetectionRunId ? <div className="handoff-action-card">
<span>Detection run</span>
<strong>Detections GeoJSON</strong>
<p>Export persisted detection geometries for the selected analysis run.</p>
<button type="button" className="secondary-action" onClick={onExportDetectionRun} disabled={!selectedDetectionRunId || exporting}>
Export selected detection run GeoJSON
</button>
</div>
<div className="handoff-action-card">
</div> : null}
{selectedSegmentationRunId ? <div className="handoff-action-card">
<span>Segmentation run</span>
<strong>Segmentations GeoJSON</strong>
<p>Export persisted segmentation polygons for the selected analysis run.</p>
<button type="button" className="secondary-action" onClick={onExportSegmentationRun} disabled={!selectedSegmentationRunId || exporting}>
Export selected segmentation run GeoJSON
</button>
</div>
</div> : null}
</div>
</div>
</div>
@@ -344,7 +351,12 @@ export function ExportCenter({
) : null}
</div>
<div className="export-history-surface" aria-label="Export history">
<details className="export-history-surface export-history-disclosure" aria-label="Export history">
<summary>
<span>Downloadgeschiedenis</span>
<strong>{exports.length} bestanden</strong>
</summary>
<div className="export-history-body">
<div>
<h3>Export history</h3>
<p className="muted">Review persisted artifacts and open JSON previews where supported.</p>
@@ -444,7 +456,8 @@ export function ExportCenter({
</li>
))}
</ul>
</div>
</div>
</details>
</div>
</section>
)