Complete operational map result workflow
This commit is contained in:
@@ -44,6 +44,19 @@ function compactPath(value: string): string {
|
||||
}
|
||||
|
||||
function formatExportType(value: string): string {
|
||||
const labels: Record<string, string> = {
|
||||
vector_selection_geojson: 'Kaartselectie (GeoJSON)',
|
||||
map_analysis_json: 'Gebiedsanalyse (JSON)',
|
||||
map_evolution_json: 'Historische vergelijking (JSON)',
|
||||
dataset_geojson: 'Kaartlaag (GeoJSON)',
|
||||
detection_geojson: 'Gebouwdetecties (GeoJSON)',
|
||||
segmentation_geojson: 'Segmentaties (GeoJSON)',
|
||||
project_metadata_json: 'Werkruimtedata (JSON)',
|
||||
project_report_html: 'Projectrapport (HTML)',
|
||||
}
|
||||
if (labels[value]) {
|
||||
return labels[value]
|
||||
}
|
||||
return value
|
||||
.split('_')
|
||||
.filter(Boolean)
|
||||
@@ -53,9 +66,10 @@ function formatExportType(value: string): string {
|
||||
|
||||
function formatExportCreated(value?: string | null): string {
|
||||
if (!value) {
|
||||
return 'created time unavailable'
|
||||
return 'Tijdstip onbekend'
|
||||
}
|
||||
return value
|
||||
const parsed = new Date(value)
|
||||
return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleString('nl-BE')
|
||||
}
|
||||
|
||||
function getLatestExportByType(exports: ExportRead[], exportType: string): ExportRead | null {
|
||||
@@ -103,6 +117,24 @@ export function ExportCenter({
|
||||
const exportStatuses = useMemo(() => Array.from(new Set(exports.map((item) => item.status))).sort(), [exports])
|
||||
const latestHandoffArtifacts = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: 'map_analysis_json',
|
||||
label: 'Gebiedsanalyse',
|
||||
detail: 'Server-side herberekende metingen van de kaartselectie',
|
||||
item: getLatestExportByType(exports, 'map_analysis_json'),
|
||||
},
|
||||
{
|
||||
key: 'map_evolution_json',
|
||||
label: 'Historische vergelijking',
|
||||
detail: 'Bewaarde evolutie van het geselecteerde gebied',
|
||||
item: getLatestExportByType(exports, 'map_evolution_json'),
|
||||
},
|
||||
{
|
||||
key: 'vector_selection_geojson',
|
||||
label: 'Kaartselectie (GeoJSON)',
|
||||
detail: 'Persistente objecten binnen de geselecteerde rechthoek',
|
||||
item: getLatestExportByType(exports, 'vector_selection_geojson'),
|
||||
},
|
||||
{
|
||||
key: 'project_report_html',
|
||||
label: 'Leesbaar rapport',
|
||||
@@ -182,8 +214,8 @@ export function ExportCenter({
|
||||
<div className="handoff-summary-card">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<h3>Handoff readiness</h3>
|
||||
<p className="muted">Pick the artifact that matches the current review state before sharing data outside the workbench.</p>
|
||||
<h3>Klaar om te delen</h3>
|
||||
<p className="muted">Controleer het juiste bestand voordat je gegevens buiten GeoIntel gebruikt.</p>
|
||||
</div>
|
||||
<span className="status-badge">{handoffStatus}</span>
|
||||
</div>
|
||||
@@ -260,9 +292,9 @@ export function ExportCenter({
|
||||
<div className="export-action-grid">
|
||||
<div className="handoff-action-grid">
|
||||
<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>
|
||||
<span>Vernieuwen</span>
|
||||
<strong>Bewaarde bestanden</strong>
|
||||
<p>Laad de persistente downloads van de geselecteerde werkruimte opnieuw.</p>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-action"
|
||||
@@ -270,7 +302,7 @@ export function ExportCenter({
|
||||
disabled={!selectedProjectId || loadingExports}
|
||||
data-testid="refresh-exports"
|
||||
>
|
||||
Refresh exports
|
||||
Downloads vernieuwen
|
||||
</button>
|
||||
</div>
|
||||
<div className="handoff-action-card handoff-action-card-primary">
|
||||
@@ -304,19 +336,19 @@ export function ExportCenter({
|
||||
</button>
|
||||
</div>
|
||||
{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>
|
||||
<span>Gebouwanalyse</span>
|
||||
<strong>Herkende gebouwen (GeoJSON)</strong>
|
||||
<p>Bewaar de persistente detectiegeometrie van de geselecteerde analyserun.</p>
|
||||
<button type="button" className="secondary-action" onClick={onExportDetectionRun} disabled={!selectedDetectionRunId || exporting}>
|
||||
Export selected detection run GeoJSON
|
||||
Gebouwanalyse bewaren
|
||||
</button>
|
||||
</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>
|
||||
<span>Segmentatieanalyse</span>
|
||||
<strong>Segmentaties (GeoJSON)</strong>
|
||||
<p>Bewaar de persistente polygonen van de geselecteerde segmentatierun.</p>
|
||||
<button type="button" className="secondary-action" onClick={onExportSegmentationRun} disabled={!selectedSegmentationRunId || exporting}>
|
||||
Export selected segmentation run GeoJSON
|
||||
Segmentatie bewaren
|
||||
</button>
|
||||
</div> : null}
|
||||
</div>
|
||||
@@ -358,16 +390,16 @@ export function ExportCenter({
|
||||
</summary>
|
||||
<div className="export-history-body">
|
||||
<div>
|
||||
<h3>Export history</h3>
|
||||
<p className="muted">Review persisted artifacts and open JSON previews where supported.</p>
|
||||
<h3>Bewaarde downloads</h3>
|
||||
<p className="muted">Bekijk persistente bestanden en open beschikbare JSON-voorbeelden.</p>
|
||||
</div>
|
||||
{exports.length > 0 ? (
|
||||
<div className="export-history-controls" aria-label="Export history filters">
|
||||
<label>
|
||||
Search exports
|
||||
Downloads zoeken
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Type, id or path"
|
||||
placeholder="Type, id of pad"
|
||||
value={exportSearchQuery}
|
||||
onChange={(event) => setExportSearchQuery(event.target.value)}
|
||||
/>
|
||||
@@ -375,7 +407,7 @@ export function ExportCenter({
|
||||
<label>
|
||||
Type
|
||||
<select value={exportTypeFilter} onChange={(event) => setExportTypeFilter(event.target.value)}>
|
||||
<option value="all">All types</option>
|
||||
<option value="all">Alle types</option>
|
||||
{exportTypes.map((type) => (
|
||||
<option key={type} value={type}>
|
||||
{type}
|
||||
@@ -386,7 +418,7 @@ export function ExportCenter({
|
||||
<label>
|
||||
Status
|
||||
<select value={exportStatusFilter} onChange={(event) => setExportStatusFilter(event.target.value)}>
|
||||
<option value="all">All statuses</option>
|
||||
<option value="all">Alle statussen</option>
|
||||
{exportStatuses.map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{status}
|
||||
@@ -405,25 +437,25 @@ export function ExportCenter({
|
||||
}}
|
||||
disabled={!hasActiveExportFilters && !showAllExports}
|
||||
>
|
||||
Reset view
|
||||
Filters wissen
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{filteredExports.length > 10 ? (
|
||||
<div className="list-limit-banner">
|
||||
<span>
|
||||
Showing {visibleExports.length} of {filteredExports.length} matching exports.
|
||||
{hiddenExportCount > 0 ? ` ${hiddenExportCount} older artifacts hidden.` : ' All matching artifacts shown.'}
|
||||
{visibleExports.length} van {filteredExports.length} passende downloads zichtbaar.
|
||||
{hiddenExportCount > 0 ? ` ${hiddenExportCount} oudere bestanden verborgen.` : ' Alle passende bestanden zijn zichtbaar.'}
|
||||
</span>
|
||||
<button type="button" className="secondary-action" onClick={() => setShowAllExports((value) => !value)}>
|
||||
{showAllExports ? 'Show latest 10' : 'Show all exports'}
|
||||
{showAllExports ? 'Toon laatste 10' : 'Toon alle downloads'}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{exports.length > 0 && filteredExports.length === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No exports match the current filters.</strong>
|
||||
<p>Clear the search, type or status filter to return to the latest artifacts.</p>
|
||||
<strong>Geen downloads passen bij deze filters.</strong>
|
||||
<p>Wis de zoekopdracht of filters om de recentste bestanden opnieuw te tonen.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="export-list">
|
||||
@@ -433,9 +465,9 @@ export function ExportCenter({
|
||||
<div>
|
||||
<span className="export-type-badge">{formatExportType(item.export_type)}</span>
|
||||
<div className="entity-meta">
|
||||
<span>export id: {item.id}</span>
|
||||
{item.analysis_run_id ? <span>analysis run: {item.analysis_run_id}</span> : null}
|
||||
{item.created_at ? <span>created: {item.created_at}</span> : null}
|
||||
<span>download-id: {item.id}</span>
|
||||
{item.analysis_run_id ? <span>analyserun: {item.analysis_run_id}</span> : null}
|
||||
{item.created_at ? <span>gemaakt: {formatExportCreated(item.created_at)}</span> : null}
|
||||
</div>
|
||||
</div>
|
||||
<span className={item.status === 'ready' ? 'status-badge status-badge-ready' : 'status-badge'}>{item.status}</span>
|
||||
@@ -444,13 +476,13 @@ export function ExportCenter({
|
||||
<div className="button-row">
|
||||
{canPreviewJson(item) ? (
|
||||
<button type="button" className="secondary-action" onClick={() => onPreviewContent(item.id)}>
|
||||
Preview JSON content
|
||||
JSON bekijken
|
||||
</button>
|
||||
) : (
|
||||
<span className="status-badge">download-only HTML artifact</span>
|
||||
<span className="status-badge">HTML alleen downloaden</span>
|
||||
)}
|
||||
<button type="button" className="primary-action" onClick={() => onDownload(item.id)}>
|
||||
Download artifact
|
||||
Downloaden
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user