Close full operational audit findings
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-17 14:19:02 +02:00
parent cc36dabcbe
commit 78a5f0b0b2
81 changed files with 2248 additions and 1467 deletions
@@ -0,0 +1,203 @@
import type {
AreaRead,
DatasetCreateResponse,
ExportRead,
ProjectRead,
QualityCheckRead,
} from '../../types'
import type { useSourceFreshness } from '../../hooks/useSourceFreshness'
import { SourceFreshnessPanel } from '../status/SourceFreshnessPanel'
import { WorkbenchStatusStrip } from '../WorkbenchStatusStrip'
export type WorkspaceKey = 'overview' | 'data' | 'map' | 'assistant' | 'analysis' | 'ai' | 'exports' | 'system'
interface OverviewWorkspaceProps {
selectedProject: ProjectRead | null
selectedProjectId: string | null
areas: AreaRead[]
datasets: DatasetCreateResponse[]
qualityChecks: QualityCheckRead[]
exports: ExportRead[]
activeLayerFeatureCount: number
selectedAreaHasGeometry: boolean
hasAnalysisOutput: boolean
sourceFreshness: ReturnType<typeof useSourceFreshness>
onOpenWorkspace: (target: WorkspaceKey) => void
}
interface WorkflowStep {
step: string
title: string
detail: string
status: string
ready: boolean
target: WorkspaceKey
}
export function OverviewWorkspace({
selectedProject,
selectedProjectId,
areas,
datasets,
qualityChecks,
exports,
activeLayerFeatureCount,
selectedAreaHasGeometry,
hasAnalysisOutput,
sourceFreshness,
onOpenWorkspace,
}: OverviewWorkspaceProps): JSX.Element {
const hasMapContext = activeLayerFeatureCount > 0 || selectedAreaHasGeometry
const workflowComplete =
Boolean(selectedProjectId)
&& datasets.length > 0
&& hasMapContext
&& hasAnalysisOutput
&& exports.length > 0
const recommendedTarget: WorkspaceKey = !selectedProjectId || datasets.length === 0
? 'data'
: !hasMapContext
? 'map'
: !hasAnalysisOutput
? 'analysis'
: 'exports'
const steps: WorkflowStep[] = [
{
step: '1',
title: 'Werkruimte en gebied',
detail: selectedProjectId
? `${areas.length} ${areas.length === 1 ? 'gebied' : 'gebieden'} beschikbaar`
: 'Laad of maak een werkruimte',
status: selectedProjectId ? 'gereed' : 'volgende stap',
ready: Boolean(selectedProjectId),
target: 'data',
},
{
step: '2',
title: 'Databronnen',
detail: datasets.length > 0
? `${datasets.length} ${datasets.length === 1 ? 'databron' : 'databronnen'} ingeladen`
: 'Voeg bron- en referentiegegevens toe',
status: datasets.length > 0 ? 'gereed' : 'wachten',
ready: datasets.length > 0,
target: 'data',
},
{
step: '3',
title: 'Kaart',
detail: hasMapContext
? activeLayerFeatureCount > 0
? `${activeLayerFeatureCount.toLocaleString('nl-BE')} objecten op de kaart`
: 'Werkgebied ingeladen'
: 'Bekijk het werkgebied en kies een kaartlaag',
status: hasMapContext ? 'gereed' : 'wachten',
ready: hasMapContext,
target: 'map',
},
{
step: '4',
title: 'Controle en analyse',
detail: hasAnalysisOutput
? 'Er is een bewaard analyse- of kwaliteitsresultaat'
: 'Voer een controle uit zodra de gegevens klaarstaan',
status: hasAnalysisOutput ? 'gereed' : 'wachten',
ready: hasAnalysisOutput,
target: 'analysis',
},
{
step: '5',
title: 'Downloads',
detail: exports.length > 0
? `${exports.length} ${exports.length === 1 ? 'resultaat' : 'resultaten'} bewaard`
: 'Bewaar een gecontroleerd resultaat',
status: exports.length > 0 ? 'gereed' : 'wachten',
ready: exports.length > 0,
target: 'exports',
},
]
return (
<div className="workspace-stack">
<WorkbenchStatusStrip
selectedProject={selectedProject}
areas={areas}
datasets={datasets}
qualityChecks={qualityChecks}
exports={exports}
activeLayerFeatureCount={activeLayerFeatureCount}
selectedAreaHasGeometry={selectedAreaHasGeometry}
/>
<SourceFreshnessPanel
report={sourceFreshness.report}
loading={sourceFreshness.loading}
error={sourceFreshness.error}
onRefresh={() => { void sourceFreshness.refresh() }}
catalogReport={sourceFreshness.catalogReport}
catalogLoading={sourceFreshness.catalogLoading}
catalogError={sourceFreshness.catalogError}
onProbeCatalogs={(force) => { void sourceFreshness.probeCatalogs(force) }}
grbRefreshPlan={sourceFreshness.grbRefreshPlan}
grbRefreshPlanLoading={sourceFreshness.grbRefreshPlanLoading}
grbRefreshPlanError={sourceFreshness.grbRefreshPlanError}
/>
<details className="status-details-disclosure">
<summary>
<span>Volledige workflowstatus</span>
<strong>{workflowComplete ? 'voltooid' : 'stappen open'}</strong>
</summary>
<section className="workflow-guidance-panel" aria-label="Voortgang van de werkstroom">
<div className="panel-title-row">
<div>
<p className="eyebrow">Van bron tot resultaat</p>
<h2>Voortgang van de werkstroom</h2>
</div>
<span className="status-badge">
{workflowComplete ? 'Klaar om te delen' : 'Ga verder met de gemarkeerde stap'}
</span>
</div>
<div className="workflow-guidance-steps">
{steps.map((step) => (
<button
key={`${step.step}-${step.title}`}
type="button"
className={
step.target === recommendedTarget && !step.ready
? 'workflow-guidance-step workflow-guidance-step-active'
: step.ready
? 'workflow-guidance-step workflow-guidance-step-ready'
: 'workflow-guidance-step'
}
onClick={() => onOpenWorkspace(step.target)}
aria-label={`Open stap ${step.title}`}
>
<span className="workflow-step-status">{step.status}</span>
<strong>{step.step}. {step.title}</strong>
<small>{step.detail}</small>
</button>
))}
</div>
</section>
<section className="overview-actions">
<div className="overview-action-copy">
<p className="eyebrow">Snel verder</p>
<h2>Kies je volgende actie</h2>
</div>
<div className="quick-action-grid overview-quick-actions" aria-label="Aanbevolen acties">
<button type="button" className="quick-action-button" onClick={() => onOpenWorkspace('data')}>
Gegevens beheren
</button>
<button type="button" className="quick-action-button" onClick={() => onOpenWorkspace('map')}>
Kaart openen
</button>
<button type="button" className="quick-action-button" onClick={() => onOpenWorkspace('analysis')}>
Kwaliteit bekijken
</button>
<button type="button" className="quick-action-button" onClick={() => onOpenWorkspace('exports')}>
Downloads beheren
</button>
</div>
</section>
</details>
</div>
)
}