import { useMemo, useState } from 'react' import type { DatasetCreateResponse, MetricRead, QualityCheckRead } from '../../types' const CORE_METRIC_ORDER = [ 'precision', 'recall', 'f1', 'mean_iou', 'false_positive_count', 'false_negative_count', ] interface QualityResultsPanelProps { selectedProjectId: string | null qualityChecks: QualityCheckRead[] qualityChecksError: string | null candidateDatasets: DatasetCreateResponse[] referenceDatasets: DatasetCreateResponse[] onRefresh: () => void onOpenEvidenceMap?: (qualityCheckId: string) => void evidenceLoading?: boolean evidenceError?: string | null } function qualityMetricLabel(metricKey: string): string { const labels: Record = { precision: 'Precision', recall: 'Recall', f1: 'F1', mean_iou: 'Mean IoU', false_positive_count: 'False positives', false_negative_count: 'False negatives', } return labels[metricKey] ?? metricKey.replaceAll('_', ' ') } function qualityMetricValue(metric: MetricRead | undefined): string { if (!metric || metric.metric_value === null || metric.metric_value === undefined) { return 'n/a' } const value = Number(metric.metric_value) if (!Number.isFinite(value)) { return String(metric.metric_value) } return Number.isInteger(value) ? String(value) : value.toFixed(3) } function metricByKey(check: QualityCheckRead | null, metricKey: string): MetricRead | undefined { return check?.metrics.find((metric) => metric.metric_key === metricKey) } function findingEvidenceList(check: QualityCheckRead | null, key: string): Record[] { const value = check?.findings_json?.[key] return Array.isArray(value) ? value.filter((item): item is Record => Boolean(item) && typeof item === 'object' && !Array.isArray(item)) : [] } function evidenceLabel(item: Record): string { const candidate = item['candidate_feature_id'] const reference = item['reference_feature_id'] const iou = item['iou'] const pairLabel = [candidate ? `Candidate ${candidate}` : null, reference ? `Reference ${reference}` : null].filter(Boolean).join(' / ') const iouValue = Number(iou) return iou === null || iou === undefined || !Number.isFinite(iouValue) ? pairLabel || JSON.stringify(item) : `${pairLabel || 'Match'} / IoU ${iouValue.toFixed(3)}` } function formatQualityTimestamp(value?: string | null): string { return value || 'n/a' } function qualityMatchesSearch(check: QualityCheckRead, query: string, datasetNameById: Map): boolean { const normalizedQuery = query.trim().toLowerCase() if (!normalizedQuery) { return true } const candidateName = check.candidate_dataset_id ? datasetNameById.get(check.candidate_dataset_id) : null const referenceName = datasetNameById.get(check.reference_dataset_id) const searchable = [ check.id, check.check_type, check.status, check.candidate_dataset_id, check.reference_dataset_id, check.analysis_run_id, check.job_id, candidateName, referenceName, ] .filter(Boolean) .join(' ') .toLowerCase() return searchable.includes(normalizedQuery) } export function QualityResultsPanel({ selectedProjectId, qualityChecks, qualityChecksError, candidateDatasets, referenceDatasets, onRefresh, onOpenEvidenceMap, evidenceLoading = false, evidenceError = null, }: QualityResultsPanelProps): JSX.Element { const [showAllQualityChecks, setShowAllQualityChecks] = useState(false) const [qualityStatusFilter, setQualityStatusFilter] = useState('all') const [qualityTypeFilter, setQualityTypeFilter] = useState('all') const [qualitySearchQuery, setQualitySearchQuery] = useState('') const [selectedQualityCheckId, setSelectedQualityCheckId] = useState(null) const completedChecks = qualityChecks.filter((check) => check.status === 'ok' || check.status === 'completed').length const latestCheck = qualityChecks[0] ?? null const datasetNameById = useMemo(() => { const lookup = new Map() for (const dataset of [...candidateDatasets, ...referenceDatasets]) { lookup.set(dataset.id, dataset.name) } return lookup }, [candidateDatasets, referenceDatasets]) const latestCandidateName = latestCheck?.candidate_dataset_id ? datasetNameById.get(latestCheck.candidate_dataset_id) : null const latestReferenceName = latestCheck?.reference_dataset_id ? datasetNameById.get(latestCheck.reference_dataset_id) : null const selectedQualityCheck = useMemo( () => qualityChecks.find((check) => check.id === selectedQualityCheckId) ?? latestCheck, [latestCheck, qualityChecks, selectedQualityCheckId], ) const selectedCandidateName = selectedQualityCheck?.candidate_dataset_id ? datasetNameById.get(selectedQualityCheck.candidate_dataset_id) ?? selectedQualityCheck.candidate_dataset_id : 'n/a' const selectedReferenceName = selectedQualityCheck ? datasetNameById.get(selectedQualityCheck.reference_dataset_id) ?? selectedQualityCheck.reference_dataset_id : 'n/a' const selectedMatchEvidence = findingEvidenceList(selectedQualityCheck, 'match_evidence') const selectedFalsePositiveEvidence = findingEvidenceList(selectedQualityCheck, 'false_positive_evidence') const selectedFalseNegativeEvidence = findingEvidenceList(selectedQualityCheck, 'false_negative_evidence') const qualityStatuses = useMemo(() => Array.from(new Set(qualityChecks.map((check) => check.status))).sort(), [qualityChecks]) const qualityTypes = useMemo(() => Array.from(new Set(qualityChecks.map((check) => check.check_type))).sort(), [qualityChecks]) const filteredQualityChecks = useMemo( () => qualityChecks.filter((check) => { const statusMatches = qualityStatusFilter === 'all' || check.status === qualityStatusFilter const typeMatches = qualityTypeFilter === 'all' || check.check_type === qualityTypeFilter return statusMatches && typeMatches && qualityMatchesSearch(check, qualitySearchQuery, datasetNameById) }), [qualityChecks, qualityStatusFilter, qualityTypeFilter, qualitySearchQuery, datasetNameById], ) const visibleQualityChecks = showAllQualityChecks ? filteredQualityChecks : filteredQualityChecks.slice(0, 8) const hiddenQualityCheckCount = Math.max(filteredQualityChecks.length - visibleQualityChecks.length, 0) const hasActiveQualityFilters = qualityStatusFilter !== 'all' || qualityTypeFilter !== 'all' || qualitySearchQuery.trim().length > 0 return (

QA/QC Results

Persisted quality checks from dataset, detection and segmentation comparisons.

{qualityChecks.length} checks
Completed {completedChecks}
Latest score {latestCheck?.score ?? 'n/a'}
Latest status {latestCheck?.status ?? 'waiting'}
Candidate layers {candidateDatasets.length}

{candidateDatasets[0]?.name ?? 'No candidate layer selected yet'}

Reference layers {referenceDatasets.length}

{referenceDatasets[0]?.name ?? 'No reference layer available yet'}

Latest comparison {latestCheck?.status ?? 'waiting'}

Candidate {latestCandidateName ?? latestCheck?.candidate_dataset_id ?? 'n/a'} / Reference {latestReferenceName ?? latestCheck?.reference_dataset_id ?? 'n/a'}

QA/QC evidence drilldown

Inspect the persisted check, metric evidence and dataset handoff context before exporting or sharing results.

{evidenceError ? (
QA/QC evidence overlay could not be loaded.

{evidenceError}

) : null} {selectedQualityCheck ? ( <>
Selected check {selectedQualityCheck.check_type}

{selectedQualityCheck.id}

Candidate layer {selectedCandidateName}

{selectedQualityCheck.candidate_dataset_id ?? 'candidate not stored'}

Reference layer {selectedReferenceName}

{selectedQualityCheck.reference_dataset_id}

Analysis run {selectedQualityCheck.analysis_run_id ?? 'n/a'}

Job: {selectedQualityCheck.job_id ?? 'n/a'}

Status {selectedQualityCheck.status}

Score: {selectedQualityCheck.score ?? 'n/a'}

Completed {formatQualityTimestamp(selectedQualityCheck.completed_at)}

Created: {formatQualityTimestamp(selectedQualityCheck.created_at)}

False positive evidence {qualityMetricValue(metricByKey(selectedQualityCheck, 'false_positive_count'))}

Candidate geometries not matched to the reference layer.

False negative evidence {qualityMetricValue(metricByKey(selectedQualityCheck, 'false_negative_count'))}

Reference geometries not matched by the candidate layer.

Map evidence handoff {selectedCandidateName} / {selectedReferenceName}

Use the candidate and reference datasets as map layers for spatial review.

Matched feature ids {selectedMatchEvidence.length > 0 ? (
    {selectedMatchEvidence.slice(0, 6).map((item, index) => (
  • {evidenceLabel(item)}
  • ))}
) : (

No matched feature ids persisted for this check.

)}
False positive feature ids {selectedFalsePositiveEvidence.length > 0 ? (
    {selectedFalsePositiveEvidence.slice(0, 6).map((item, index) => (
  • {evidenceLabel(item)}
  • ))}
) : (

No false-positive feature ids persisted for this check.

)}
False negative feature ids {selectedFalseNegativeEvidence.length > 0 ? (
    {selectedFalseNegativeEvidence.slice(0, 6).map((item, index) => (
  • {evidenceLabel(item)}
  • ))}
) : (

No false-negative feature ids persisted for this check.

)}
Parameters
                    {JSON.stringify(selectedQualityCheck.parameters_json ?? {}, null, 2)}
                  
Findings
                    {JSON.stringify(selectedQualityCheck.findings_json ?? {}, null, 2)}
                  
) : (
No QA/QC check selected.

Run or load a persisted QA/QC check to inspect metric and map evidence.

)}
{qualityChecksError ? (
QA/QC results could not be loaded.

{qualityChecksError}

) : null} {qualityChecks.length === 0 ? (
No persisted QA/QC results yet

Run dataset, detection or segmentation QA against a reference layer to populate this workspace.

) : null}
{qualityChecks.length > 0 ? (
) : null} {filteredQualityChecks.length > 8 ? (
Showing {visibleQualityChecks.length} of {filteredQualityChecks.length} matching QA/QC results. {hiddenQualityCheckCount > 0 ? ` ${hiddenQualityCheckCount} older checks hidden.` : ' All matching checks shown.'}
) : null} {qualityChecks.length > 0 && filteredQualityChecks.length === 0 ? (
No QA/QC results match the current filters.

Clear the search, status or check type filter to return to the latest quality results.

) : null}

Result history

{visibleQualityChecks.length} shown
    {visibleQualityChecks.map((check) => (
  • {check.check_type}
    candidate: {check.candidate_dataset_id ? datasetNameById.get(check.candidate_dataset_id) ?? check.candidate_dataset_id : 'n/a'} reference: {datasetNameById.get(check.reference_dataset_id) ?? check.reference_dataset_id}
    {check.status}
    Score {check.score ?? 'n/a'}
    Metrics {check.metrics.length}
    Quality check {check.id}
    Metric evidence
    {CORE_METRIC_ORDER.map((metricKey) => { const metric = check.metrics.find((item) => item.metric_key === metricKey) const isCriticalCount = metricKey === 'false_positive_count' || metricKey === 'false_negative_count' return (
    {qualityMetricLabel(metricKey)} {qualityMetricValue(metric)}
    ) })}
    Raw metrics
      {check.metrics.map((metric) => (
    • {qualityMetricLabel(metric.metric_key)} {qualityMetricValue(metric)}
    • ))}
  • ))}
) }