import { useEffect, useMemo, useState, type Dispatch, type FormEvent, type SetStateAction } from 'react' import type { AreaRead, DatasetCreateResponse } from '../../types' import { getDatasetDisplayName, getDatasetSourceDisplayName } from '../../lib/datasetDisplay' const DATASET_CATALOG_PAGE_SIZE = 10 interface DatasetFormState { datasetType: string source: string datasetRole: string sourceName: string referenceLayerName: string sourceMetadataJson: string provenanceMetadataJson: string areaId: string file: File | null } interface DatasetPanelProps { selectedProjectId: string | null selectedDatasetId: string | null areas: AreaRead[] datasets: DatasetCreateResponse[] datasetForm: DatasetFormState loadingDatasets: boolean onDatasetFormChange: Dispatch> onUploadDataset: (event: FormEvent) => void onLoadDatasetDetails: (projectId: string, dataset: DatasetCreateResponse) => void onOpenInspector: () => void onRefreshMetadata: (datasetId: string) => void onOpenDatasetInMap: (dataset: DatasetCreateResponse) => void onOpenDatasetExport: (dataset: DatasetCreateResponse) => void } function formatBytes(value: number | null | undefined): string { if (!value && value !== 0) { return 'n.v.t.' } const units = ['B', 'KB', 'MB', 'GB'] let size = value let index = 0 while (size >= 1024 && index < units.length - 1) { size /= 1024 index += 1 } return `${size.toFixed(1)} ${units[index]}` } function formatBounds(bounds: Record | null | undefined): string { if (!bounds) { return 'n.v.t.' } const keys = ['min_x', 'min_y', 'max_x', 'max_y'] if (!keys.every((key) => key in bounds)) { return 'n.v.t.' } return `${bounds.min_x?.toFixed(4)}, ${bounds.min_y?.toFixed(4)} -> ${bounds.max_x?.toFixed(4)}, ${bounds.max_y?.toFixed(4)}` } function isVectorDatasetType(datasetType: string): boolean { return datasetType === 'vector' || datasetType === 'geojson' } function normalizeDatasetRole(dataset: DatasetCreateResponse): 'reference' | 'candidate' | 'source' { if (dataset.dataset_role === 'reference') { return 'reference' } if (dataset.dataset_role === 'source') { return 'source' } if (dataset.dataset_role === 'derived' || isVectorDatasetType(dataset.dataset_type)) { return 'candidate' } return 'source' } function datasetRoleLabel(role: string): string { if (role === 'reference') { return 'Officiële referentie' } if (role === 'candidate') { return 'Analyse-resultaat' } return 'Basisbron' } function datasetActionHint(dataset: DatasetCreateResponse, role: string): string { if (role === 'reference') { return 'Officiële laag voor kaartanalyse en kwaliteitscontrole.' } if (role === 'candidate') { return 'Bewaard resultaat dat opnieuw op de kaart, in kwaliteit of als download kan worden gebruikt.' } if (dataset.dataset_type === 'raster') { return 'Luchtbeeld of rasterbron voor kaart- en beeldanalyse.' } return 'Ingeladen gegevensbron voor verdere analyse.' } function formatObservationDate(value: string | null | undefined): string { if (!value) return 'huidige toestand' const date = new Date(value) if (Number.isNaN(date.getTime())) return value return new Intl.DateTimeFormat('nl-BE', { day: 'numeric', month: 'short', year: 'numeric' }).format(date) } function datasetKindLabel(dataset: DatasetCreateResponse): string { if (dataset.dataset_type === 'raster') return 'Luchtbeeld' if (isVectorDatasetType(dataset.dataset_type)) return 'Kaartlaag' return 'Gegevensbron' } export function DatasetPanel({ selectedProjectId, selectedDatasetId, areas, datasets, datasetForm, loadingDatasets, onDatasetFormChange, onUploadDataset, onLoadDatasetDetails, onOpenInspector, onRefreshMetadata, onOpenDatasetInMap, onOpenDatasetExport, }: DatasetPanelProps) { const [historyOpen, setHistoryOpen] = useState(false) const [searchQuery, setSearchQuery] = useState('') const [page, setPage] = useState(1) const selectedDataset = datasets.find((dataset) => dataset.id === selectedDatasetId) const latestDatasetBySeries = new Map() datasets.forEach((dataset) => { if (!dataset.temporal_series_key) return const current = latestDatasetBySeries.get(dataset.temporal_series_key) const currentTimestamp = current?.observed_at ? Date.parse(current.observed_at) : Number.NEGATIVE_INFINITY const candidateTimestamp = dataset.observed_at ? Date.parse(dataset.observed_at) : Number.NEGATIVE_INFINITY if (!current || candidateTimestamp > currentTimestamp) { latestDatasetBySeries.set(dataset.temporal_series_key, dataset) } }) const primaryDatasets = datasets.filter( (dataset) => !dataset.temporal_series_key || latestDatasetBySeries.get(dataset.temporal_series_key)?.id === dataset.id, ) const historicalDatasets = datasets.filter( (dataset) => dataset.temporal_series_key && latestDatasetBySeries.get(dataset.temporal_series_key)?.id !== dataset.id, ) const filteredPrimaryDatasets = useMemo(() => { const query = searchQuery.trim().toLocaleLowerCase('nl-BE') if (!query) return primaryDatasets return primaryDatasets.filter((dataset) => { const searchable = [ getDatasetDisplayName(dataset), getDatasetSourceDisplayName(dataset), dataset.source, dataset.source_name, dataset.reference_layer_name, String(dataset.source_metadata?.['municipality'] ?? ''), datasetRoleLabel(normalizeDatasetRole(dataset)), ] return searchable.some((value) => String(value ?? '').toLocaleLowerCase('nl-BE').includes(query)) }) }, [primaryDatasets, searchQuery]) const pageCount = Math.max(1, Math.ceil(filteredPrimaryDatasets.length / DATASET_CATALOG_PAGE_SIZE)) const visiblePrimaryDatasets = filteredPrimaryDatasets.slice( (page - 1) * DATASET_CATALOG_PAGE_SIZE, page * DATASET_CATALOG_PAGE_SIZE, ) const readyDatasets = primaryDatasets.filter((dataset) => dataset.status === 'ready').length const roleSummaries = [ { key: 'selected', label: 'Geselecteerd', count: selectedDatasetId ? 1 : 0, className: 'dataset-role-selected', }, { key: 'reference', label: 'Referentie', count: primaryDatasets.filter((dataset) => normalizeDatasetRole(dataset) === 'reference').length, className: 'dataset-role-reference', }, { key: 'candidate', label: 'Resultaat', count: primaryDatasets.filter((dataset) => normalizeDatasetRole(dataset) === 'candidate').length, className: 'dataset-role-candidate', }, { key: 'source', label: 'Basisbron', count: primaryDatasets.filter((dataset) => normalizeDatasetRole(dataset) === 'source').length, className: 'dataset-role-source', }, ] useEffect(() => { setPage((current) => Math.min(current, pageCount)) }, [pageCount]) useEffect(() => { setSearchQuery('') setPage(1) setHistoryOpen(false) }, [selectedProjectId]) return (

Ingeladen gegevens

Bronnen

{readyDatasets}/{primaryDatasets.length} bronnen beschikbaar
Actieve bron {selectedDataset ? getDatasetDisplayName(selectedDataset) : 'Geen bron geselecteerd'} {selectedDataset ? `${datasetRoleLabel(normalizeDatasetRole(selectedDataset))} · ${selectedDataset.status === 'ready' ? 'beschikbaar' : selectedDataset.status}` : 'Kies een bron om deze op de kaart te openen.'}
Eigen bronbestand toevoegen
{roleSummaries.map((summary) => (
{summary.count} {summary.label}
))}
{loadingDatasets ?

Bronnen laden...

: null} {!loadingDatasets && datasets.length === 0 ? (
Nog geen bronnen beschikbaar

Voeg een vector-, GeoJSON- of rasterbestand toe aan deze werkruimte.

) : null}

Beschikbare bronnen

{filteredPrimaryDatasets.length} van {primaryDatasets.length} {page} / {pageCount}
    {visiblePrimaryDatasets.map((dataset) => { const datasetRole = normalizeDatasetRole(dataset) const roleLabel = datasetRoleLabel(datasetRole) return (
  • {roleLabel} {datasetKindLabel(dataset)}
    {getDatasetDisplayName(dataset)}
    {(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten {getDatasetSourceDisplayName(dataset)} · {formatObservationDate(dataset.observed_at)}
    {dataset.status === 'ready' ? 'beschikbaar' : dataset.status === 'failed' ? 'mislukt' : 'bezig'}

    {datasetActionHint(dataset, datasetRole)}

    Broninformatie
    Bestand: {dataset.name} Bron: {dataset.source_name ?? dataset.source} {dataset.reference_layer_name ? Laag: {dataset.reference_layer_name} : null} CRS: {dataset.crs ?? dataset.vector_summary?.crs ?? 'onbekend'}
    Grootte: {formatBytes(dataset.size_bytes)} Objecten: {dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 'onbekend'} Begrenzing: {formatBounds(dataset.bounds_json ?? dataset.vector_summary?.bounds_json)}
  • ) })}
{visiblePrimaryDatasets.length === 0 && datasets.length > 0 ? (

Geen bron gevonden voor deze zoekterm.

) : null} {historicalDatasets.length > 0 ? (
setHistoryOpen(event.currentTarget.open)} > Historische meetmomenten {historicalDatasets.length} oudere lagen {historyOpen ?
    {historicalDatasets.map((dataset) => (
  • {getDatasetDisplayName(dataset)} {formatObservationDate(dataset.observed_at)} · {(dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 0).toLocaleString('nl-BE')} objecten
  • ))}
: null}
) : null}
) }