import { useEffect, useMemo, useRef, useState } from 'react' import { CircleAlert, LogOut, ShieldCheck, UserRound } from 'lucide-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' import './styles/atlas-premium-v2.css' import './styles/professionalization.css' import { LandingPage } from './components/auth/LandingPage' import { ChangeDetectionPanel } from './components/analysis/ChangeDetectionPanel' import { GeoAssistantPanel } from './components/assistant/GeoAssistantPanel' import { DatasetPanel } from './components/datasets/DatasetPanel' import { SourceCatalogPanel } from './components/datasets/SourceCatalogPanel' import { DetectionLab } from './components/detection/DetectionLab' import { ExportCenter } from './components/exports/ExportCenter' import { ExportPreview } from './components/exports/ExportPreview' import { WorkbenchInspector } from './components/inspector/WorkbenchInspector' import { MapWorkspace } from './components/map/MapWorkspace' import { OverviewWorkspace, type WorkspaceKey } from './components/overview/OverviewWorkspace' import { AreaPanel } from './components/project/AreaPanel' import { ProjectPanel } from './components/project/ProjectPanel' import { QualityResultsPanel } from './components/quality/QualityResultsPanel' import type { DatasetCreateResponse } from './types' import { ProviderPanel } from './components/providers/ProviderPanel' import { SegmentationLab } from './components/segmentation/SegmentationLab' import { WorkbenchNavigation, type WorkspaceNavigationGroup, type WorkspaceNavigationItem, } from './components/shell/WorkbenchNavigation' import { WorkspaceSignal } from './components/shell/WorkspaceSignal' import { useChangeDetectionWorkflow } from './hooks/useChangeDetectionWorkflow' import { useDemoWorkflow } from './hooks/useDemoWorkflow' import { useCoverageResolver } from './hooks/useCoverageResolver' import { useDetectionWorkflow } from './hooks/useDetectionWorkflow' import { useDatasetWorkflow } from './hooks/useDatasetWorkflow' import { useExportWorkflow } from './hooks/useExportWorkflow' import { useMapSelectionDataset } from './hooks/useMapSelectionDataset' import { useMapSelectionQa } from './hooks/useMapSelectionQa' import { useMapWorkspaceState } from './hooks/useMapWorkspaceState' import { useOperatorSession } from './hooks/useOperatorSession' import { useMapSelectionExtract } from './hooks/useMapSelectionExtract' import { useMapOrthophotoAnalysis } from './hooks/useMapOrthophotoAnalysis' import { isDetectionImageryDataset, isMapRasterDataset } from './lib/datasetCapabilities' import { useProviderCapabilities } from './hooks/useProviderCapabilities' import { useProjectWorkspace } from './hooks/useProjectWorkspace' import { useQualityWorkflow } from './hooks/useQualityWorkflow' import { useSegmentationWorkflow } from './hooks/useSegmentationWorkflow' import { useSourceFreshness } from './hooks/useSourceFreshness' import { useWorkbenchBootstrap } from './hooks/useWorkbenchBootstrap' import { useViewportVectorLayer } from './hooks/useViewportVectorLayer' import { getDatasetDisplayName } from './lib/datasetDisplay' import { FLANDERS_WORKSPACE_LABEL, FLANDERS_WORKSPACE_PROJECT_NAME, NATIONAL_WORKSPACE_LABEL, NATIONAL_WORKSPACE_PROJECT_NAME, REGIONAL_WORKSPACE_LABEL, REGIONAL_WORKSPACE_PROJECT_NAME, } from './config/primaryFocus' function isVectorDatasetType(datasetType: string): boolean { return datasetType === 'vector' || datasetType === 'geojson' } const workspaceNavItems: WorkspaceNavigationItem[] = [ { key: 'overview', label: 'Status', description: 'Beschikbaarheid en aandachtspunten' }, { key: 'data', label: 'Bronnen', description: 'Gebieden en ingeladen gegevens' }, { key: 'map', label: 'Kaart', description: 'Selecteren, uitlezen en vergelijken' }, { key: 'assistant', label: 'AI-vragen', description: 'Vraag de lokale assistent over het actieve gebied' }, { key: 'analysis', label: 'Kwaliteit', description: 'Resultaten controleren' }, { key: 'ai', label: 'Beeldanalyse', description: 'Gebouwen herkennen op luchtbeelden' }, { key: 'exports', label: 'Downloads', description: 'Resultaten bewaren en delen' }, { key: 'system', label: 'Systeem', description: 'Bronkoppelingen en operationele status' }, ] const workspaceNavGroups: WorkspaceNavigationGroup[] = [ { label: 'Verkennen', keys: ['map', 'data'] }, { label: 'Vragen', keys: ['assistant'] }, { label: 'Analyseren', keys: ['analysis', 'ai'] }, { label: 'Afronden', keys: ['exports'] }, { label: 'Beheer', keys: ['overview', 'system'] }, ] const guestWorkspaceKeys = new Set(['map', 'analysis']) const guestWorkspaceGroups: WorkspaceNavigationGroup[] = [ { label: 'Demowerkruimte', keys: ['map', 'analysis'] }, ] interface WorkbenchAppProps { username: string | null accessMode: 'open' | 'operator' | 'guest' loggingOut: boolean onLogout: () => void } function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchAppProps): JSX.Element { const isGuest = accessMode === 'guest' const [activeWorkspace, setActiveWorkspace] = useState('map') const [inspectorOpen, setInspectorOpen] = useState(false) const [guestDemoReady, setGuestDemoReady] = useState(!isGuest) const guestDemoStartedRef = useRef(false) const visibleWorkspaceItems = useMemo( () => isGuest ? workspaceNavItems.filter((item) => guestWorkspaceKeys.has(item.key)) : workspaceNavItems, [isGuest], ) const visibleWorkspaceGroups = isGuest ? guestWorkspaceGroups : workspaceNavGroups const [mapContentMode, setMapContentMode] = useState<'dataset' | 'analysis'>('dataset') const [mapContextSourceLabel, setMapContextSourceLabel] = useState(null) const [mapContextLayerLabel, setMapContextLayerLabel] = useState(null) const workbenchMainRef = useRef(null) const previousWorkspaceRef = useRef(activeWorkspace) useEffect(() => { if (isGuest && !guestWorkspaceKeys.has(activeWorkspace)) { setActiveWorkspace('map') } }, [activeWorkspace, isGuest]) useEffect(() => { workbenchMainRef.current?.scrollTo({ top: 0, left: 0 }) if (previousWorkspaceRef.current !== activeWorkspace) { workbenchMainRef.current?.focus({ preventScroll: true }) } previousWorkspaceRef.current = activeWorkspace setInspectorOpen(false) }, [activeWorkspace]) const { projects, selectedProject, selectedProjectId, areas, datasets, loadingProjects, loadingAreas, loadingDatasets, archivingProjectId, errorMessage, projectForm, areaForm, loadProjects, loadProjectData, createProject, createArea, activateMunicipality, archiveProject, resetProjectData, setSelectedProjectId, setErrorMessage, setProjectForm, setAreaForm, } = useProjectWorkspace() const sourceFreshness = useSourceFreshness(selectedProjectId) const selectProject = (projectId: string) => { setMapContentMode('dataset') setSelectedProjectId(projectId) } const { selectedDatasetId, selectedDataset, selectedDatasetSummary, selectedRasterMetadata, selectedRasterStats, datasetContent, jobs, rasterPreview, latestRasterTileManifestPath, latestRasterTileManifest, selectedIntersectTargetId, selectedClipAreaId, rasterTileSize, rasterTileOverlap, rasterTileOutputName, rasterReprojectCrs, rasterReprojectResampling, ndviNirBand, ndviRedBand, ndwiGreenBand, ndwiNirBand, ndbiSwirBand, ndbiNirBand, loadingDatasetDetails, datasetDetailError, datasetForm, isRasterTileInputValid, loadDatasetJobs, loadDatasetDetails, uploadDataset, runVectorClip, runVectorBuffer, runVectorIntersect, runRasterInspect, runRasterPreview, runRasterStats, runRasterReproject, runRasterClip, runRasterTile, runRasterNdvi, runRasterNdwi, runRasterNdbi, pickDerivedDataset, refreshMetadata, resetDatasetForProject, setSelectedDatasetId, setSelectedIntersectTargetId, setSelectedClipAreaId, setRasterTileSize, setRasterTileOverlap, setRasterTileOutputName, setRasterReprojectCrs, setRasterReprojectResampling, setNdviNirBand, setNdviRedBand, setNdwiGreenBand, setNdwiNirBand, setNdbiSwirBand, setNdbiNirBand, setDatasetForm, } = useDatasetWorkflow({ selectedProjectId, areas, datasets, loadProjectData, setErrorMessage, isVectorDatasetType, }) const rasterUnavailableMessage = useMemo(() => { if (!selectedDataset || selectedDataset.dataset_type !== 'raster' || !selectedDataset.metadata_json) { return null } const metadata = selectedDataset.metadata_json as Record const processingCode = metadata['processing_code'] if (processingCode === 'RASTER_PROCESSING_UNAVAILABLE') { return String(metadata['processing_error'] ?? 'Raster processing unavailable.') } return null }, [selectedDataset]) const availableVectorTargets = useMemo( () => datasets.filter((item) => item.id !== selectedDatasetId && isVectorDatasetType(item.dataset_type)), [datasets, selectedDatasetId], ) const availableVectorDatasets = useMemo(() => datasets.filter((item) => isVectorDatasetType(item.dataset_type)), [datasets]) const availableMapDatasets = useMemo( () => { const vectors = datasets.filter((dataset) => isVectorDatasetType(dataset.dataset_type) && dataset.status === 'ready') const rasters = datasets.filter(isMapRasterDataset) return [...vectors, ...rasters] }, [datasets], ) const referenceDatasets = useMemo( () => availableVectorDatasets.filter((item) => item.dataset_role === 'reference'), [availableVectorDatasets], ) const candidateDatasets = useMemo( () => availableVectorDatasets.filter((item) => item.dataset_role !== 'reference'), [availableVectorDatasets], ) const rasterDatasets = useMemo(() => datasets.filter((item) => item.dataset_type === 'raster'), [datasets]) const detectionRasterDatasets = useMemo( () => rasterDatasets.filter(isDetectionImageryDataset), [rasterDatasets], ) const { providers, loadingCapabilities, capabilitiesError, loadCapabilities, } = useProviderCapabilities() const { changeSourceDatasetId, changeTargetDatasetId, changeIouThreshold, changeIncludeUnchanged, runningChangeDetection, changeDetectionResult, changeDetectionError, runChangeDetection, setChangeSourceDatasetId, setChangeTargetDatasetId, setChangeIouThreshold, setChangeIncludeUnchanged, } = useChangeDetectionWorkflow({ selectedProjectId, availableVectorDatasets, loadDatasetJobs, }) const { qaCandidateDatasetId, qaReferenceDatasetId, qaAreaId, qaIouThreshold, qaRunning, qaResult, qaError, qualityChecks, qualityChecksError, qualityEvidenceGeoJson, qualityEvidenceLoading, qualityEvidenceError, loadQualityChecks, loadQualityEvidenceGeoJson, clearQualityEvidenceGeoJson, runQaComparison, setQaCandidateDatasetId, setQaReferenceDatasetId, setQaAreaId, setQaIouThreshold, } = useQualityWorkflow({ selectedProjectId, loadProjectData, }) const { detectionModels, modelAssets, loadingDetectionModels, detectionModelError, modelAssetError, selectedDetectionDatasetId, selectedDetectionModelId, selectedModelAssetId, detectionTileManifestPath, detectionConfidenceThreshold, runningDetection, detectionRunResult, detectionRunError, detectionRuns, selectedDetectionRunId, detectionItems, detectionGeoJson, detectionClassFilter, detectionMinConfidenceFilter, loadingDetectionResults, detectionReferenceDatasetId, detectionQaResult, detectionQaError, runningDetectionQa, yoloPreflight, loadingYoloPreflight, yoloPreflightError, calibrationThresholdText, runningDetectionCalibration, detectionCalibrationRows, detectionCalibrationError, detectionWorkflowStage, loadDetectionModels, loadYoloPreflight, loadDetectionRuns, loadDetectionResults, runDetection, uploadDetectionRaster, prepareAndRunDetection, compareDetectionRunWithReference, runDetectionQa, runDetectionCalibration, applyDetectionOperatorProfile, resetDetectionForProject, setSelectedDetectionDatasetId, setSelectedDetectionModelId, setSelectedModelAssetId, setDetectionTileManifestPath, setDetectionConfidenceThreshold, setSelectedDetectionRunId, setDetectionClassFilter, setDetectionMinConfidenceFilter, setDetectionReferenceDatasetId, setCalibrationThresholdText, } = useDetectionWorkflow({ selectedProjectId, rasterDatasets: detectionRasterDatasets, qaIouThreshold, loadProjectData, loadQualityChecks, }) const useRasterTileManifestForDetection = () => { const manifestPath = latestRasterTileManifestPath.trim() if (!manifestPath || !selectedDataset || !isDetectionImageryDataset(selectedDataset)) { return } setDetectionTileManifestPath(manifestPath) setSelectedDetectionDatasetId(selectedDataset.id) setSelectedDetectionModelId('yolo-configured') setDetectionConfidenceThreshold(0.25) loadYoloPreflight(manifestPath).catch(() => null) setActiveWorkspace('ai') } const { segmentationModels, loadingSegmentationModels, segmentationModelError, selectedSegmentationDatasetId, selectedSegmentationModelId, selectedSegmentationModel, segmentationTileManifestPath, segmentationConfidenceThreshold, runningSegmentation, segmentationRunResult, segmentationRunError, segmentationRuns, selectedSegmentationRunId, segmentationItems, segmentationGeoJson, segmentationClassFilter, segmentationMinConfidenceFilter, loadingSegmentationResults, segmentationReferenceDatasetId, segmentationQaResult, segmentationQaError, runningSegmentationQa, loadSegmentationModels, loadSegmentationRuns, loadSegmentationResults, runSegmentation, runSegmentationQa, resetSegmentationForProject, setSelectedSegmentationDatasetId, setSelectedSegmentationModelId, setSegmentationTileManifestPath, setSegmentationConfidenceThreshold, setSelectedSegmentationRunId, setSegmentationClassFilter, setSegmentationMinConfidenceFilter, setSegmentationReferenceDatasetId, } = useSegmentationWorkflow({ selectedProjectId, rasterDatasets, qaIouThreshold, loadProjectData, loadQualityChecks, }) const viewportVectorLayer = useViewportVectorLayer({ selectedProjectId, selectedDataset, featureCount: selectedDatasetSummary?.feature_count ?? selectedDataset?.feature_count ?? null, isVectorDatasetType, }) const analysisMapLayerAvailable = Boolean(changeDetectionResult?.geojson || segmentationGeoJson || detectionGeoJson) const analysisMapLayerActive = mapContentMode === 'analysis' && analysisMapLayerAvailable const viewportVectorLayerActive = viewportVectorLayer.enabled && !analysisMapLayerActive const datasetMapContent = viewportVectorLayer.enabled ? viewportVectorLayer.data : datasetContent const useRasterTileManifestForSegmentation = () => { const manifestPath = latestRasterTileManifestPath.trim() if (!manifestPath || !selectedDataset || selectedDataset.dataset_type !== 'raster') { return } setSegmentationTileManifestPath(manifestPath) setSelectedSegmentationDatasetId(selectedDataset.id) setActiveWorkspace('ai') } const { exports, latestExport, exportError, loadingExports, exporting, selectionExporting, selectionExportError, latestSelectionExport, exportPreview, loadExports, exportSelectedDatasetGeoJson, exportSelectedDetectionRunGeoJson, exportSelectedSegmentationRunGeoJson, exportMapSelectionGeoJson, persistMapResult, exportProjectMetadata, exportProjectReport, previewExportContent, downloadExportArtifact, resetExportsForProject, } = useExportWorkflow({ selectedProjectId, selectedDataset, selectedDetectionRunId, selectedSegmentationRunId, isVectorDatasetType, }) const { mapLayerVisible, mapLayerOpacity, selectedMapAreaId, areaLayerVisible, areaLayerOpacity, selectedMapFeature, areaFeatureCollection, mapFeatureCollection, mapLayerLabel, mapFeatureCount, areaFeatureCount, setMapLayerVisible, setMapLayerOpacity, setSelectedMapAreaId, setAreaLayerVisible, setAreaLayerOpacity, setSelectedMapFeature, } = useMapWorkspaceState({ areas, changeDetectionGeoJson: analysisMapLayerActive ? changeDetectionResult?.geojson ?? null : null, segmentationGeoJson: analysisMapLayerActive ? segmentationGeoJson : null, detectionGeoJson: analysisMapLayerActive ? detectionGeoJson : null, datasetContent: datasetMapContent, selectedDataset, datasetLayerActive: Boolean(selectedDataset && isVectorDatasetType(selectedDataset.dataset_type)), }) const { mapSelectionBbox, mapSelectionResult, mapSelectionLoading, mapSelectionError, runMapSelectionExtract, resetMapSelectionExtract, setMapSelectionBbox, } = useMapSelectionExtract({ selectedProjectId, selectedDataset, isVectorDatasetType, }) const { coverage: mapCoverage, loadingCoverage: mapCoverageLoading, coverageError: mapCoverageError, coverageDurationMs: mapCoverageDurationMs, coverageBudgetExceeded: mapCoverageBudgetExceeded, } = useCoverageResolver({ projectId: selectedProjectId, bbox: mapSelectionBbox, }) const { selectionDatasetSaving, selectionDatasetError, latestSelectionDataset, deriveMapSelectionDataset, } = useMapSelectionDataset({ selectedProjectId, selectedDataset, isVectorDatasetType, loadProjectData, loadDatasetDetails, setMapLayerVisible, }) const { selectedMapQaReferenceDatasetId, mapSelectionQaRunning, mapSelectionQaError, mapSelectionQaResult, latestMapSelectionQualityCheckId, runMapSelectionQa, setSelectedMapQaReferenceDatasetId, } = useMapSelectionQa({ selectedProjectId, latestSelectionDataset, loadQualityChecks, loadProjectData, }) const mapOrthophotoAnalysis = useMapOrthophotoAnalysis({ selectedProjectId, selectedAreaId: areas.find((area) => /vervoerregio|operationele grens/i.test(area.name))?.id ?? selectedMapAreaId, selectionBbox: mapSelectionBbox, datasets, loadProjectData, prepareAndRunDetection: (datasetId) => prepareAndRunDetection(datasetId, 'yolo-configured'), compareDetectionRunWithReference: (analysisRunId, referenceDatasetId, iouThreshold) => compareDetectionRunWithReference(analysisRunId, referenceDatasetId, false, iouThreshold), onAnalysisReady: () => { setMapContentMode('analysis') setMapLayerVisible(true) setActiveWorkspace('map') }, }) const openMapSelectionQualityEvidence = () => { setActiveWorkspace('analysis') } const openQualityEvidenceOnMap = async (qualityCheckId: string) => { const result = await loadQualityEvidenceGeoJson(qualityCheckId) if (result) { setMapLayerVisible(true) setActiveWorkspace('map') } } const { loadingDemoWorkflow, demoWorkflowMessage, loadDemoWorkflow, } = useDemoWorkflow({ restrictedMode: isGuest, loadProjects, loadProjectData, loadDatasetDetails, loadDetectionRuns, loadSegmentationRuns, loadQualityChecks, loadExports, setSelectedProjectId: selectProject, setSelectedDatasetId, setSelectedMapAreaId, setQaCandidateDatasetId, setQaReferenceDatasetId, setQaAreaId, setSelectedDetectionDatasetId, setDetectionReferenceDatasetId, setSelectedSegmentationDatasetId, setSegmentationReferenceDatasetId, setErrorMessage, }) useEffect(() => { if (!isGuest || guestDemoStartedRef.current) return guestDemoStartedRef.current = true setGuestDemoReady(false) void loadDemoWorkflow().finally(() => setGuestDemoReady(true)) }, [isGuest, loadDemoWorkflow]) useWorkbenchBootstrap({ restrictedMode: isGuest, selectedProjectId, selectedDetectionRunId, detectionClassFilter, detectionMinConfidenceFilter, selectedSegmentationRunId, segmentationClassFilter, segmentationMinConfidenceFilter, loadProjects, loadCapabilities, loadDetectionModels, loadSegmentationModels, loadProjectData, loadDetectionRuns, loadSegmentationRuns, loadQualityChecks, loadExports, loadDetectionResults, loadSegmentationResults, resetProjectData, resetDatasetForProject, resetDetectionForProject, resetSegmentationForProject, resetExportsForProject, }) const selectedArea = areas.find((area) => area.id === selectedMapAreaId) ?? null const activeWorkspaceItem = visibleWorkspaceItems.find((item) => item.key === activeWorkspace) ?? visibleWorkspaceItems[0] const mapLayerSourceLabel = useMemo(() => { if (analysisMapLayerActive && changeDetectionResult?.geojson) { return 'Veranderingsanalyse' } if (analysisMapLayerActive && segmentationGeoJson) { return 'Segmentatierun' } if (analysisMapLayerActive && detectionGeoJson) { return 'Detectierun' } if ((datasetMapContent || viewportVectorLayer.enabled) && selectedDataset) { return selectedDataset.dataset_type === 'raster' ? 'Rasterdatabron' : 'Vectordatabron' } return 'Geen actieve gegevens- of analyselaag' }, [analysisMapLayerActive, changeDetectionResult?.geojson, datasetMapContent, detectionGeoJson, segmentationGeoJson, selectedDataset, viewportVectorLayer.enabled]) const mapLayerProvenance = useMemo(() => { if (analysisMapLayerActive && changeDetectionResult?.geojson) { return 'Vergelijking van twee bewaarde kaartlagen' } if (analysisMapLayerActive && segmentationGeoJson) { return selectedSegmentationRunId ? 'Bewaarde segmentatieronde' : 'Segmentatieresultaten geladen' } if (analysisMapLayerActive && detectionGeoJson) { return selectedDetectionRunId ? 'Bewaarde detectieronde' : 'Detectieresultaten geladen' } if ((datasetMapContent || viewportVectorLayer.enabled) && selectedDataset) { return `Bewaarde ${selectedDataset.dataset_role === 'reference' ? 'referentielaag' : 'databron'}` } return 'Open een databron, detectieronde, segmentatieronde of veranderingsresultaat om het hier te tekenen.' }, [ analysisMapLayerActive, changeDetectionResult?.geojson, changeSourceDatasetId, changeTargetDatasetId, datasetMapContent, detectionGeoJson, segmentationGeoJson, selectedDataset, selectedDetectionRunId, selectedSegmentationRunId, viewportVectorLayer.enabled, ]) const openDatasetInMap = (dataset: DatasetCreateResponse) => { if (selectedProjectId) { loadDatasetDetails(selectedProjectId, dataset) } setMapContentMode('dataset') setMapLayerVisible(true) setActiveWorkspace('map') } const runGuidedDetection = async () => { const completed = await prepareAndRunDetection() if (completed) { setMapContentMode('analysis') setMapLayerVisible(true) setActiveWorkspace('map') } } const openDetectionResultsOnMap = () => { if (!detectionGeoJson) { return } setMapContentMode('analysis') setMapLayerVisible(true) setActiveWorkspace('map') } const openDatasetExport = (dataset: DatasetCreateResponse) => { if (selectedProjectId) { loadDatasetDetails(selectedProjectId, dataset) } setActiveWorkspace('exports') } const openWorkflowGuidanceStep = (target: WorkspaceKey) => { if (isGuest && !guestWorkspaceKeys.has(target)) { setActiveWorkspace(target === 'exports' ? 'analysis' : 'map') return } if (target === 'map' && availableMapDatasets.length > 0 && !mapFeatureCollection) { openDatasetInMap(availableMapDatasets[0]) return } if (target === 'exports' && availableMapDatasets.length > 0 && !selectedDataset) { openDatasetExport(availableMapDatasets[0]) return } setActiveWorkspace(target) } const hasAnalysisOutput = qualityChecks.length > 0 || Boolean(changeDetectionResult) || detectionItems.length > 0 || segmentationItems.length > 0 const projectContextLabel = selectedProject?.name === NATIONAL_WORKSPACE_PROJECT_NAME ? NATIONAL_WORKSPACE_LABEL : selectedProject?.name === REGIONAL_WORKSPACE_PROJECT_NAME ? REGIONAL_WORKSPACE_LABEL : selectedProject?.name === FLANDERS_WORKSPACE_PROJECT_NAME ? FLANDERS_WORKSPACE_LABEL : selectedProject?.name ?? 'Geen werkruimte' const areaContextLabel = selectedArea?.name ?? (areas.length > 0 ? 'Kies een gebied' : 'Geen gebied') const bathymetryProfileContextActive = Boolean( activeWorkspace === 'map' && selectedDataset?.source_name === 'vmm_vha_bathymetry_profiles', ) const bathymetryRasterContextActive = Boolean( activeWorkspace === 'map' && selectedDataset?.source_name === 'spw_bathymetry', ) const bathymetryContextActive = bathymetryProfileContextActive || bathymetryRasterContextActive const regionalBathymetryContextActive = Boolean( bathymetryProfileContextActive && selectedDataset?.source_metadata?.regional_partitions_complete === true && selectedArea && !/^Gemeente\s/i.test(selectedArea.name), ) const regionalBathymetryManifest = String( selectedDataset?.source_metadata?.partition_manifest_sha256 ?? '', ) const regionalBathymetryPartitions = regionalBathymetryContextActive ? availableMapDatasets.filter( (dataset) => dataset.source_name === 'vmm_vha_bathymetry_profiles' && dataset.source_metadata?.regional_partitions_complete === true && String(dataset.source_metadata?.partition_manifest_sha256 ?? '') === regionalBathymetryManifest, ) : [] const regionalBathymetryProfileCount = regionalBathymetryPartitions.reduce( (total, dataset) => total + (dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0), 0, ) const bathymetryProfileCount = regionalBathymetryContextActive ? regionalBathymetryProfileCount : selectedDataset?.feature_count ?? selectedDataset?.vector_summary?.feature_count ?? 0 const workspaceDataLoading = loadingProjects || loadingAreas || loadingDatasets const workspaceStatusMessage = errorMessage ? `Werkruimtefout: ${errorMessage}` : workspaceDataLoading ? 'Werkruimte en databronnen worden geladen.' : selectedProject ? `${projectContextLabel} is geladen met ${datasets.length} databronnen en ${areas.length} gebieden.` : 'Geen werkruimte geselecteerd.' const datasetContextLabel = activeWorkspace === 'map' && mapContextSourceLabel ? mapContextSourceLabel : analysisMapLayerActive && mapFeatureCollection ? `${mapLayerLabel} · controle vereist` : selectedDataset ? getDatasetDisplayName(selectedDataset) : workspaceDataLoading ? 'Bronnen worden geladen' : datasets.length > 0 ? 'Kies een bron' : 'Geen bron' const layerContextLabel = activeWorkspace === 'map' && mapContextLayerLabel ? mapContextLayerLabel : bathymetryContextActive ? bathymetryRasterContextActive ? `${Number(selectedDataset?.source_metadata?.analysis_resolution_m ?? 0.5).toLocaleString('nl-BE')} m raster · mDNG · ${String(selectedDataset?.source_metadata?.survey_period ?? '2019-2022')}` : regionalBathymetryContextActive ? `${bathymetryProfileCount.toLocaleString('nl-BE')} profielen · ${regionalBathymetryPartitions.length} gemeenten` : `${bathymetryProfileCount.toLocaleString('nl-BE')} profielen` : mapFeatureCollection ? `${mapFeatureCount.toLocaleString('nl-BE')} objecten` : workspaceDataLoading ? 'Kaart wordt voorbereid' : viewportVectorLayerActive ? 'Kaartlaag gekozen' : activeWorkspace === 'map' && selectedDataset?.dataset_type === 'raster' ? 'Rasterlaag actief' : 'Geen actieve laag' return (
{ window.requestAnimationFrame(() => workbenchMainRef.current?.focus({ preventScroll: true })) }} > Ga naar de werkruimte

{workspaceStatusMessage}

{errorMessage && activeWorkspace !== 'map' ? (
) : null}
GeoIntel
Werkruimte {projectContextLabel}
Gebied {areaContextLabel}
Bron {datasetContextLabel}
Kaartlaag {layerContextLabel}
{username || isGuest ? (
{isGuest ?
) : null}
{isGuest ? (
) : null} {isGuest && (!guestDemoReady || loadingDemoWorkflow) ? (
) : null}
{activeWorkspace !== 'map' ?

{selectedProject?.region ?? 'Belgie en Belgische Noordzee'}

{activeWorkspaceItem.label}

{activeWorkspaceItem.description}

{!isGuest && activeWorkspace !== 'overview' ? ( ) : null}
: null} {activeWorkspace === 'overview' ? ( ) : null} {activeWorkspace === 'data' ? (
setInspectorOpen(true)} onRefreshMetadata={refreshMetadata} onOpenDatasetInMap={openDatasetInMap} onOpenDatasetExport={openDatasetExport} />
) : null} {activeWorkspace === 'analysis' ? (
loadQualityChecks()} onOpenEvidenceMap={openQualityEvidenceOnMap} evidenceLoading={qualityEvidenceLoading} evidenceError={qualityEvidenceError} onOpenMapWorkspace={() => setActiveWorkspace('map')} onOpenAnalysisWorkspace={() => setActiveWorkspace(isGuest ? 'map' : 'ai')} /> {!isGuest ? (
Historische vectorlagen vergelijken Geavanceerd
) : (
)}
) : null} {activeWorkspace === 'assistant' ? (
) : null} {activeWorkspace === 'ai' ? (
loadYoloPreflight()} onSelectDataset={setSelectedDetectionDatasetId} onSelectModel={setSelectedDetectionModelId} onSelectModelAsset={setSelectedModelAssetId} onSetConfidenceThreshold={setDetectionConfidenceThreshold} onSetTileManifestPath={setDetectionTileManifestPath} onRunDetection={runDetection} onUploadRaster={uploadDetectionRaster} onPrepareAndRunDetection={runGuidedDetection} onOpenResultsOnMap={openDetectionResultsOnMap} onLoadRuns={() => loadDetectionRuns()} onSelectRun={setSelectedDetectionRunId} onSetClassFilter={setDetectionClassFilter} onSetMinConfidenceFilter={setDetectionMinConfidenceFilter} onLoadResults={() => loadDetectionResults()} onSelectReferenceDataset={setDetectionReferenceDatasetId} onRunQa={runDetectionQa} onSetCalibrationThresholdText={setCalibrationThresholdText} onRunCalibration={runDetectionCalibration} onOpenCalibrationEvidence={openQualityEvidenceOnMap} onApplyOperatorProfile={applyDetectionOperatorProfile} />
Segmentatie van beeldvlakken Nog niet geconfigureerd loadSegmentationRuns()} onSelectRun={setSelectedSegmentationRunId} onSetClassFilter={setSegmentationClassFilter} onSetMinConfidenceFilter={setSegmentationMinConfidenceFilter} onLoadResults={() => loadSegmentationResults()} onSelectReferenceDataset={setSegmentationReferenceDatasetId} onRunQa={runSegmentationQa} />
) : null} {activeWorkspace === 'exports' ? (
loadExports()} onExportDataset={exportSelectedDatasetGeoJson} onExportDetectionRun={exportSelectedDetectionRunGeoJson} onExportSegmentationRun={exportSelectedSegmentationRunGeoJson} onExportProjectMetadata={exportProjectMetadata} onExportProjectReport={exportProjectReport} onPreviewContent={previewExportContent} onDownload={downloadExportArtifact} />
) : null} {activeWorkspace === 'system' ? ( setActiveWorkspace('data')} onOpenStatus={() => setActiveWorkspace('overview')} onOpenMap={() => setActiveWorkspace('map')} /> ) : null}
{inspectorOpen && !isGuest ? ( ) : null}
) } function App(): JSX.Element { const { session, sessionError, loggingOut, handleAuthenticated, handleLogout } = useOperatorSession() if (session === null) { return (
) } if (!session.authenticated) { return ( ) } return ( ) } export default App