feat(ui): align workbench with Stitch screens
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-19 16:38:17 +02:00
parent 87345f1a8c
commit 1b9848a5f4
10 changed files with 883 additions and 195 deletions
+10
View File
@@ -127,6 +127,16 @@ The visual contract, Stitch project and screen references are documented in
hierarchy only; API, persistence and GIS behavior remain owned by the existing
hooks and services.
All eight user-facing destinations now use the Stitch Atlas composition
directly. Map is a flush theme/canvas/insight surface; Sources is a
workspace/catalog/dataset master-detail view; AI Questions keeps context beside
the conversation; Quality keeps controls, results and evidence in three stable
panes; Image Analysis keeps run configuration, persisted results and QA aligned;
Downloads separates artifact actions, history and preview; Status and
Administration use compact operational rows. Public Sans and Manrope are
bundled with the frontend, so typography remains identical without a remote
font dependency.
## Current component boundaries
`App.tsx` remains the shared workspace orchestrator, while focused surfaces
+20
View File
@@ -8,6 +8,8 @@
"name": "geointel-frontend",
"version": "1.0.0",
"dependencies": {
"@fontsource/manrope": "^5.3.0",
"@fontsource/public-sans": "^5.3.0",
"lucide-react": "^1.25.0",
"maplibre-gl": "^4.7.1",
"react": "^18.2.0",
@@ -898,6 +900,24 @@
"node": ">=18"
}
},
"node_modules/@fontsource/manrope": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@fontsource/manrope/-/manrope-5.3.0.tgz",
"integrity": "sha512-obJ1Dv3+uCA6HlHgW8u4BGYxJR9In2HW7gjJhlflEvkrj1X1iSEwu0fToL+JYGC/FEKFfIz1sBuPduvcL2gIAA==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@fontsource/public-sans": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@fontsource/public-sans/-/public-sans-5.3.0.tgz",
"integrity": "sha512-kjODI0S3zdv0mBYCIQ8TbBayaiqszpc2UbhJiO3bjIqVVXzcWfHSt2o3WBCLOY3juaGaQoy4MoWCcgmfI5hCuA==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+2
View File
@@ -16,6 +16,8 @@
"node": "^20.19.0 || >=22.12.0"
},
"dependencies": {
"@fontsource/manrope": "^5.3.0",
"@fontsource/public-sans": "^5.3.0",
"lucide-react": "^1.25.0",
"maplibre-gl": "^4.7.1",
"react": "^18.2.0",
+6
View File
@@ -1,4 +1,10 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import '@fontsource/manrope/latin-500.css'
import '@fontsource/manrope/latin-600.css'
import '@fontsource/manrope/latin-700.css'
import '@fontsource/public-sans/latin-400.css'
import '@fontsource/public-sans/latin-500.css'
import '@fontsource/public-sans/latin-600.css'
import './styles/app.css'
import './styles/premium.css'
import './styles/atlas-workbench.css'
+8 -1
View File
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState, type KeyboardEvent } from 'react'
import { BoxSelect, MapPinned, SlidersHorizontal, Trash2 } from 'lucide-react'
import GeoMap from '../GeoMap'
import type { AreaRead, CoverageResolveResponse, CoverageStatus, DatasetCreateResponse, DetectionQaResult, MapResultExportRequest, MapViewportState, OrthophotoAcquisitionResult, OrthophotoProductRead, ProjectRead, QaComparisonResult, VectorSelectionBBox, VectorSelectionMetric, VectorSelectionResponse } from '../../types'
import { useMapThemeSelectionInsights, type MapThemeAcquisition, type MapThemeQuery } from '../../hooks/useMapThemeSelectionInsights'
@@ -1905,8 +1906,11 @@ export function MapWorkspace({
onClick={() => setAdvancedMode(true)}
aria-expanded={advancedMode}
aria-controls="geo-advanced-workbench"
aria-label="Geavanceerde werkbank"
title="Geavanceerde werkbank"
>
Geavanceerde werkbank
<SlidersHorizontal aria-hidden="true" />
<span>Geavanceerde werkbank</span>
</button>
</header>
@@ -2210,6 +2214,7 @@ export function MapWorkspace({
type="button"
onClick={startBboxSelection}
>
<BoxSelect aria-hidden="true" />
{bboxSelectionMode ? 'Teken op de kaart…' : 'Teken rechthoek'}
</button>
<button
@@ -2223,9 +2228,11 @@ export function MapWorkspace({
}
onClick={() => selectedAreaBbox && void analyzeSelection(selectedAreaBbox, selectedMapArea?.id)}
>
<MapPinned aria-hidden="true" />
{regionalRasterThemeActive || regionalOnDemandThemeActive ? 'Selecteer een deelgebied' : 'Volledig werkgebied'}
</button>
<button className="secondary-action" disabled={!mapSelectionBbox} type="button" onClick={clearAreaSelection}>
<Trash2 aria-hidden="true" />
Wis selectie
</button>
</div>
@@ -208,14 +208,48 @@ export function QualityResultsPanel({
</div>
{qualityChecks.length === 0 ? (
<div className="result-state result-state-empty quality-user-empty-state">
<strong>Nog geen kwaliteitscontrole uitgevoerd</strong>
<p>Selecteer een gebied op de kaart en vergelijk een bewaard resultaat met een officiële referentielaag.</p>
<div className="quality-empty-workbench" aria-label="Lege kwaliteitswerkruimte">
<aside className="quality-empty-context">
<span className="eyebrow">Controles</span>
<strong>Nog geen resultaten</strong>
<p>Kwaliteitscontroles verschijnen hier nadat een resultaat met een officiële referentielaag is vergeleken.</p>
<div className="quality-empty-context-stat">
<span>Te controleren lagen</span>
<strong>{candidateDatasets.length}</strong>
</div>
<div className="quality-empty-context-stat">
<span>Referentielagen</span>
<strong>{referenceDatasets.length}</strong>
</div>
<button type="button" className="secondary-action" onClick={onRefresh} disabled={!selectedProjectId}>
Resultaten vernieuwen
</button>
</aside>
<div className="quality-empty-results">
<div className="quality-empty-column-heading">
<strong>Resultaten</strong>
<span>0 controles</span>
</div>
<div className="result-state result-state-empty quality-user-empty-state">
<strong>Nog geen kwaliteitscontrole uitgevoerd</strong>
<p>Selecteer een gebied op de kaart en vergelijk een bewaard resultaat met een officiële referentielaag.</p>
</div>
</div>
<aside className="quality-empty-inspector">
<div className="quality-empty-column-heading">
<strong>Controle-inspector</strong>
<span>Geen selectie</span>
</div>
<div className="result-state result-state-empty">
<strong>Geen bewijs geselecteerd</strong>
<p>Scores, databronnen en kaartbewijs worden hier samen getoond zodra een controle beschikbaar is.</p>
</div>
</aside>
</div>
) : null}
{qualityChecks.length > 0 ? (
<details className="quality-advanced-disclosure">
<details className="quality-advanced-disclosure" open>
<summary>
<span>Details, kaartbewijs en historiek</span>
<strong>{qualityChecks.length} bewaarde controles</strong>
File diff suppressed because it is too large Load Diff