Finish audit UI and live validation closure
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:36:55 +02:00
parent dbc28c8916
commit 75ab8f64ab
6 changed files with 87 additions and 27 deletions
@@ -25,8 +25,29 @@ function detectionQualityInterpretation(f1: number | null | undefined): string {
return 'Onvoldoende betrouwbaar voor operationeel gebruik.'
}
function detectionStatusLabel(status: string): string {
if (status === 'completed') return 'afgerond'
if (status === 'success') return 'geslaagd'
if (status === 'failed') return 'mislukt'
if (status === 'running') return 'bezig'
if (status === 'queued') return 'in wachtrij'
return status.replace(/_/g, ' ')
}
function persistedDetectionModelLabel(modelName: string | null | undefined): string {
if (!modelName) return 'Gebouwdetectie'
if (modelName === 'yolo-configured') return 'Lokaal gebouwmodel'
if (modelName === 'manual-fixture-detector') return 'Testmodel'
if (modelName === 'yolo-placeholder') return 'Niet-geconfigureerd gebouwmodel'
return modelName
}
function detectionClassLabel(className: string): string {
if (className.toLowerCase() === 'building') return 'Gebouw'
return className
}
function formatDetectionRunLabel(run: DetectionRunRead): string {
const status = run.status === 'completed' ? 'afgerond' : run.status
const timestamp = run.finished_at ?? run.created_at
const dateLabel = timestamp
? new Intl.DateTimeFormat('nl-BE', {
@@ -34,7 +55,7 @@ function formatDetectionRunLabel(run: DetectionRunRead): string {
timeStyle: 'short',
}).format(new Date(timestamp))
: 'datum onbekend'
return `${run.model_name || 'Gebouwdetectie'} · ${status} · ${dateLabel}`
return `${persistedDetectionModelLabel(run.model_name)} · ${detectionStatusLabel(run.status)} · ${dateLabel}`
}
interface CalibrationRow {
@@ -753,18 +774,16 @@ export function DetectionLab({
<th>Type</th>
<th>Zekerheid</th>
<th>Model</th>
<th>Beeldtegel</th>
<th>Herkomst</th>
</tr>
</thead>
<tbody>
{visibleDetectionItems.map((detection) => (
<tr key={detection.id}>
<td>{detection.class_name}</td>
<td>{detectionClassLabel(detection.class_name)}</td>
<td>{detection.confidence.toFixed(2)}</td>
<td>{detection.model_name}</td>
<td className="source-tile-cell" title={detection.source_tile_path ?? undefined}>
{formatSourceTilePath(detection.source_tile_path)}
</td>
<td>{persistedDetectionModelLabel(detection.model_name)}</td>
<td className="source-tile-cell">Luchtbeeldtegel</td>
</tr>
))}
</tbody>
@@ -1119,11 +1138,3 @@ function downloadJsonFile(filename: string, payload: unknown): void {
function formatNullableNumber(value: number | null, digits: number): string {
return typeof value === 'number' && Number.isFinite(value) ? value.toFixed(digits) : 'n.v.t.'
}
function formatSourceTilePath(path: string | null | undefined): string {
if (!path) {
return 'n.v.t.'
}
const parts = path.replace(/\\/g, '/').split('/').filter(Boolean)
return parts[parts.length - 1] ?? path
}
@@ -53,8 +53,29 @@ function segmentationRunLabel(run: SegmentationRunRead): string {
const dateLabel = timestamp
? new Intl.DateTimeFormat('nl-BE', { dateStyle: 'short', timeStyle: 'short' }).format(new Date(timestamp))
: 'datum onbekend'
const status = run.status === 'completed' ? 'afgerond' : run.status
return `${run.model_name || 'Segmentatie'} · ${status} · ${dateLabel}`
return `${persistedSegmentationModelLabel(run.model_name)} · ${analysisStatusLabel(run.status)} · ${dateLabel}`
}
function analysisStatusLabel(status: string): string {
if (status === 'completed') return 'afgerond'
if (status === 'success') return 'geslaagd'
if (status === 'failed') return 'mislukt'
if (status === 'running') return 'bezig'
if (status === 'queued') return 'in wachtrij'
return status.replace(/_/g, ' ')
}
function persistedSegmentationModelLabel(modelName: string | null | undefined): string {
if (!modelName) return 'Segmentatie'
if (modelName === 'fixture-segmenter') return 'Testsegmentatie'
if (modelName === 'sam-placeholder') return 'SAM-model niet geconfigureerd'
if (modelName === 'yolo-seg-placeholder') return 'YOLO-segmentatie niet geconfigureerd'
return modelName
}
function segmentationClassLabel(className: string): string {
if (className.toLowerCase() === 'building') return 'Gebouw'
return className
}
export function SegmentationLab({
@@ -371,17 +392,17 @@ export function SegmentationLab({
<th>Zekerheid</th>
<th>Oppervlakte m²</th>
<th>Model</th>
<th>Brontegel</th>
<th>Herkomst</th>
</tr>
</thead>
<tbody>
{segmentationItems.map((segmentation) => (
<tr key={segmentation.id}>
<td>{segmentation.class_name}</td>
<td>{segmentationClassLabel(segmentation.class_name)}</td>
<td>{segmentation.confidence?.toFixed(2) ?? 'n.v.t.'}</td>
<td>{segmentation.area_m2?.toFixed(2) ?? 'n.v.t.'}</td>
<td>{segmentation.model_name}</td>
<td>{segmentation.tile_index ?? 'n.v.t.'}</td>
<td>{persistedSegmentationModelLabel(segmentation.model_name)}</td>
<td>{segmentation.tile_index == null ? 'n.v.t.' : 'Luchtbeeldtegel'}</td>
</tr>
))}
</tbody>