feat: simplify regional end user workbench
This commit is contained in:
@@ -147,50 +147,63 @@ export function QualityResultsPanel({
|
||||
const hasActiveQualityFilters = qualityStatusFilter !== 'all' || qualityTypeFilter !== 'all' || qualitySearchQuery.trim().length > 0
|
||||
|
||||
return (
|
||||
<section className="quality-results-panel" data-testid="quality-results-panel">
|
||||
<section className={qualityChecks.length === 0 ? 'quality-results-panel quality-results-panel-empty' : 'quality-results-panel'} data-testid="quality-results-panel">
|
||||
<div className="quality-results-shell">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<h2>QA/QC Results</h2>
|
||||
<p className="muted">Persisted quality checks from dataset, detection and segmentation comparisons.</p>
|
||||
<h2>Kwaliteitsresultaten</h2>
|
||||
<p className="muted">Bewaarde controles van kaartlagen en beeldanalyses.</p>
|
||||
</div>
|
||||
<span className="count-pill">{qualityChecks.length} checks</span>
|
||||
<span className="count-pill">{qualityChecks.length} controles</span>
|
||||
</div>
|
||||
|
||||
<div className="quality-summary-surface" aria-label="QA/QC result summary">
|
||||
<div className="quality-summary-grid">
|
||||
<div>
|
||||
<span>Completed</span>
|
||||
<span>Afgerond</span>
|
||||
<strong>{completedChecks}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Latest score</span>
|
||||
<span>Laatste score</span>
|
||||
<strong>{latestCheck?.score ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Latest status</span>
|
||||
<span>Laatste status</span>
|
||||
<strong>{latestCheck?.status ?? 'waiting'}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{qualityChecks.length === 0 ? (
|
||||
<div className="result-state result-state-empty quality-user-empty-state">
|
||||
<strong>Nog geen kwaliteitscontrole uitgevoerd</strong>
|
||||
<p>Selecteer een gebied op de kaart en vergelijk een bewaard resultaat met een officiële referentielaag.</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{qualityChecks.length > 0 ? (
|
||||
<details className="quality-advanced-disclosure">
|
||||
<summary>
|
||||
<span>Details, kaartbewijs en historiek</span>
|
||||
<strong>{qualityChecks.length} bewaarde controles</strong>
|
||||
</summary>
|
||||
<div className="quality-evidence-surface" aria-label="QA/QC dataset evidence">
|
||||
<div className="quality-handoff-grid" aria-label="QA/QC dataset handoff context">
|
||||
<div>
|
||||
<span>Candidate layers</span>
|
||||
<span>Te controleren lagen</span>
|
||||
<strong>{candidateDatasets.length}</strong>
|
||||
<p className="quality-dataset-name">{candidateDatasets[0]?.name ?? 'No candidate layer selected yet'}</p>
|
||||
<p className="quality-dataset-name">{candidateDatasets[0]?.name ?? 'Nog geen te controleren laag gekozen'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Reference layers</span>
|
||||
<span>Referentielagen</span>
|
||||
<strong>{referenceDatasets.length}</strong>
|
||||
<p className="quality-dataset-name">{referenceDatasets[0]?.name ?? 'No reference layer available yet'}</p>
|
||||
<p className="quality-dataset-name">{referenceDatasets[0]?.name ?? 'Nog geen referentielaag beschikbaar'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Latest comparison</span>
|
||||
<span>Laatste vergelijking</span>
|
||||
<strong>{latestCheck?.status ?? 'waiting'}</strong>
|
||||
<p className="quality-dataset-name">
|
||||
Candidate {latestCandidateName ?? latestCheck?.candidate_dataset_id ?? 'n/a'} / Reference {latestReferenceName ?? latestCheck?.reference_dataset_id ?? 'n/a'}
|
||||
Te controleren: {latestCandidateName ?? latestCheck?.candidate_dataset_id ?? 'n.v.t.'} / referentie: {latestReferenceName ?? latestCheck?.reference_dataset_id ?? 'n.v.t.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -199,8 +212,8 @@ export function QualityResultsPanel({
|
||||
<div className="quality-drilldown-surface" aria-label="QA/QC evidence drilldown">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<h3>QA/QC evidence drilldown</h3>
|
||||
<p className="muted">Inspect the persisted check, metric evidence and dataset handoff context before exporting or sharing results.</p>
|
||||
<h3>Bewijs van de kwaliteitscontrole</h3>
|
||||
<p className="muted">Bekijk de bewaarde vergelijking, scores en kaartlagen voordat je resultaten deelt.</p>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
@@ -208,7 +221,7 @@ export function QualityResultsPanel({
|
||||
onClick={() => setSelectedQualityCheckId(latestCheck?.id ?? null)}
|
||||
disabled={!latestCheck}
|
||||
>
|
||||
Inspect latest check
|
||||
Laatste controle bekijken
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -216,12 +229,12 @@ export function QualityResultsPanel({
|
||||
onClick={() => latestCheck?.id && onOpenEvidenceMap?.(latestCheck.id)}
|
||||
disabled={!latestCheck || evidenceLoading || !onOpenEvidenceMap}
|
||||
>
|
||||
{evidenceLoading ? 'Loading map evidence...' : 'Show latest on map'}
|
||||
{evidenceLoading ? 'Kaartbewijs laden...' : 'Laatste op kaart tonen'}
|
||||
</button>
|
||||
</div>
|
||||
{evidenceError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>QA/QC evidence overlay could not be loaded.</strong>
|
||||
<strong>Het bewijs kon niet op de kaart worden geladen.</strong>
|
||||
<p>{evidenceError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -229,22 +242,22 @@ export function QualityResultsPanel({
|
||||
<>
|
||||
<div className="quality-drilldown-grid">
|
||||
<div>
|
||||
<span>Selected check</span>
|
||||
<span>Geselecteerde controle</span>
|
||||
<strong>{selectedQualityCheck.check_type}</strong>
|
||||
<p>{selectedQualityCheck.id}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Candidate layer</span>
|
||||
<span>Te controleren laag</span>
|
||||
<strong>{selectedCandidateName}</strong>
|
||||
<p>{selectedQualityCheck.candidate_dataset_id ?? 'candidate not stored'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Reference layer</span>
|
||||
<span>Referentielaag</span>
|
||||
<strong>{selectedReferenceName}</strong>
|
||||
<p>{selectedQualityCheck.reference_dataset_id}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Analysis run</span>
|
||||
<span>Analyserun</span>
|
||||
<strong>{selectedQualityCheck.analysis_run_id ?? 'n/a'}</strong>
|
||||
<p>Job: {selectedQualityCheck.job_id ?? 'n/a'}</p>
|
||||
</div>
|
||||
@@ -254,39 +267,39 @@ export function QualityResultsPanel({
|
||||
<p>Score: {selectedQualityCheck.score ?? 'n/a'}</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>Completed</span>
|
||||
<span>Afgerond</span>
|
||||
<strong>{formatQualityTimestamp(selectedQualityCheck.completed_at)}</strong>
|
||||
<p>Created: {formatQualityTimestamp(selectedQualityCheck.created_at)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="quality-evidence-token-grid">
|
||||
<div>
|
||||
<span>False positive evidence</span>
|
||||
<span>Onterecht gevonden</span>
|
||||
<strong>{qualityMetricValue(metricByKey(selectedQualityCheck, 'false_positive_count'))}</strong>
|
||||
<p>Candidate geometries not matched to the reference layer.</p>
|
||||
<p>Gevonden vormen zonder overeenkomst in de referentielaag.</p>
|
||||
</div>
|
||||
<div>
|
||||
<span>False negative evidence</span>
|
||||
<span>Gemiste objecten</span>
|
||||
<strong>{qualityMetricValue(metricByKey(selectedQualityCheck, 'false_negative_count'))}</strong>
|
||||
<p>Reference geometries not matched by the candidate layer.</p>
|
||||
<p>Objecten uit de referentielaag die niet werden gevonden.</p>
|
||||
</div>
|
||||
<div className="quality-evidence-map-handoff">
|
||||
<span>Map evidence handoff</span>
|
||||
<span>Controle op de kaart</span>
|
||||
<strong>{selectedCandidateName} / {selectedReferenceName}</strong>
|
||||
<p>Use the candidate and reference datasets as map layers for spatial review.</p>
|
||||
<p>Toon beide lagen samen op de kaart voor een ruimtelijke controle.</p>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-action"
|
||||
onClick={() => onOpenEvidenceMap?.(selectedQualityCheck.id)}
|
||||
disabled={evidenceLoading || !onOpenEvidenceMap}
|
||||
>
|
||||
{evidenceLoading ? 'Loading overlay...' : 'Show evidence overlay'}
|
||||
{evidenceLoading ? 'Lagen laden...' : 'Bewijs op kaart tonen'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="quality-feature-evidence-grid" aria-label="Feature-level QA/QC evidence">
|
||||
<div>
|
||||
<span>Matched feature ids</span>
|
||||
<span>Overeenkomende object-ID's</span>
|
||||
{selectedMatchEvidence.length > 0 ? (
|
||||
<ul>
|
||||
{selectedMatchEvidence.slice(0, 6).map((item, index) => (
|
||||
@@ -294,11 +307,11 @@ export function QualityResultsPanel({
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No matched feature ids persisted for this check.</p>
|
||||
<p>Voor deze controle zijn geen overeenkomende object-ID's bewaard.</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span>False positive feature ids</span>
|
||||
<span>ID's van onterecht gevonden objecten</span>
|
||||
{selectedFalsePositiveEvidence.length > 0 ? (
|
||||
<ul>
|
||||
{selectedFalsePositiveEvidence.slice(0, 6).map((item, index) => (
|
||||
@@ -306,11 +319,11 @@ export function QualityResultsPanel({
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No false-positive feature ids persisted for this check.</p>
|
||||
<p>Er zijn geen ID's van onterecht gevonden objecten bewaard.</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<span>False negative feature ids</span>
|
||||
<span>ID's van gemiste objecten</span>
|
||||
{selectedFalseNegativeEvidence.length > 0 ? (
|
||||
<ul>
|
||||
{selectedFalseNegativeEvidence.slice(0, 6).map((item, index) => (
|
||||
@@ -318,19 +331,19 @@ export function QualityResultsPanel({
|
||||
))}
|
||||
</ul>
|
||||
) : (
|
||||
<p>No false-negative feature ids persisted for this check.</p>
|
||||
<p>Er zijn geen ID's van gemiste objecten bewaard.</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="quality-provenance-grid">
|
||||
<div>
|
||||
<span>Parameters</span>
|
||||
<span>Instellingen</span>
|
||||
<pre className="quality-provenance-pre">
|
||||
{JSON.stringify(selectedQualityCheck.parameters_json ?? {}, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<span>Findings</span>
|
||||
<span>Bevindingen</span>
|
||||
<pre className="quality-provenance-pre">
|
||||
{JSON.stringify(selectedQualityCheck.findings_json ?? {}, null, 2)}
|
||||
</pre>
|
||||
@@ -339,8 +352,8 @@ export function QualityResultsPanel({
|
||||
</>
|
||||
) : (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No QA/QC check selected.</strong>
|
||||
<p>Run or load a persisted QA/QC check to inspect metric and map evidence.</p>
|
||||
<strong>Geen kwaliteitscontrole geselecteerd.</strong>
|
||||
<p>Kies een bewaarde controle om scores en kaartbewijs te bekijken.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -354,21 +367,21 @@ export function QualityResultsPanel({
|
||||
disabled={!selectedProjectId}
|
||||
data-testid="refresh-quality-results"
|
||||
>
|
||||
Refresh QA/QC results
|
||||
Kwaliteitsresultaten vernieuwen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="quality-result-state-stack">
|
||||
{qualityChecksError ? (
|
||||
<div className="result-state result-state-error">
|
||||
<strong>QA/QC results could not be loaded.</strong>
|
||||
<strong>De kwaliteitsresultaten konden niet worden geladen.</strong>
|
||||
<p>{qualityChecksError}</p>
|
||||
</div>
|
||||
) : null}
|
||||
{qualityChecks.length === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No persisted QA/QC results yet</strong>
|
||||
<p>Run dataset, detection or segmentation QA against a reference layer to populate this workspace.</p>
|
||||
<strong>Nog geen bewaarde kwaliteitsresultaten</strong>
|
||||
<p>Vergelijk een kaartlaag of beeldanalyse met een referentielaag om deze werkruimte te vullen.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -376,7 +389,7 @@ export function QualityResultsPanel({
|
||||
{qualityChecks.length > 0 ? (
|
||||
<div className="quality-history-controls" aria-label="QA/QC result filters">
|
||||
<label>
|
||||
Search QA/QC results
|
||||
Kwaliteitsresultaten zoeken
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Type, id or dataset"
|
||||
@@ -387,7 +400,7 @@ export function QualityResultsPanel({
|
||||
<label>
|
||||
Status
|
||||
<select value={qualityStatusFilter} onChange={(event) => setQualityStatusFilter(event.target.value)}>
|
||||
<option value="all">All statuses</option>
|
||||
<option value="all">Alle statussen</option>
|
||||
{qualityStatuses.map((status) => (
|
||||
<option key={status} value={status}>
|
||||
{status}
|
||||
@@ -396,9 +409,9 @@ export function QualityResultsPanel({
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Check type
|
||||
Type controle
|
||||
<select value={qualityTypeFilter} onChange={(event) => setQualityTypeFilter(event.target.value)}>
|
||||
<option value="all">All check types</option>
|
||||
<option value="all">Alle types</option>
|
||||
{qualityTypes.map((checkType) => (
|
||||
<option key={checkType} value={checkType}>
|
||||
{checkType}
|
||||
@@ -417,33 +430,33 @@ export function QualityResultsPanel({
|
||||
}}
|
||||
disabled={!hasActiveQualityFilters && !showAllQualityChecks}
|
||||
>
|
||||
Reset view
|
||||
Filters wissen
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{filteredQualityChecks.length > 8 ? (
|
||||
<div className="list-limit-banner">
|
||||
<span className="quality-list-count">
|
||||
Showing {visibleQualityChecks.length} of {filteredQualityChecks.length} matching QA/QC results.
|
||||
{hiddenQualityCheckCount > 0 ? ` ${hiddenQualityCheckCount} older checks hidden.` : ' All matching checks shown.'}
|
||||
{visibleQualityChecks.length} van {filteredQualityChecks.length} kwaliteitsresultaten getoond.
|
||||
{hiddenQualityCheckCount > 0 ? ` ${hiddenQualityCheckCount} oudere controles verborgen.` : ' Alle resultaten zijn zichtbaar.'}
|
||||
</span>
|
||||
<button type="button" className="secondary-action" onClick={() => setShowAllQualityChecks((value) => !value)}>
|
||||
{showAllQualityChecks ? 'Show latest 8' : 'Show all QA/QC results'}
|
||||
{showAllQualityChecks ? 'Alleen laatste 8 tonen' : 'Alle kwaliteitsresultaten tonen'}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
{qualityChecks.length > 0 && filteredQualityChecks.length === 0 ? (
|
||||
<div className="result-state result-state-empty">
|
||||
<strong>No QA/QC results match the current filters.</strong>
|
||||
<p>Clear the search, status or check type filter to return to the latest quality results.</p>
|
||||
<strong>Geen kwaliteitsresultaten voldoen aan de filters.</strong>
|
||||
<p>Wis de zoek-, status- of typefilter om de laatste resultaten opnieuw te tonen.</p>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="quality-history-surface" aria-label="QA/QC result history">
|
||||
<div className="panel-title-row">
|
||||
<h3>Result history</h3>
|
||||
<span className="count-pill">{visibleQualityChecks.length} shown</span>
|
||||
<h3>Historiek</h3>
|
||||
<span className="count-pill">{visibleQualityChecks.length} getoond</span>
|
||||
</div>
|
||||
<ul className="quality-check-list">
|
||||
{visibleQualityChecks.map((check) => (
|
||||
@@ -466,7 +479,7 @@ export function QualityResultsPanel({
|
||||
</div>
|
||||
<div className="quality-check-actions">
|
||||
<button type="button" className="secondary-action" onClick={() => setSelectedQualityCheckId(check.id)}>
|
||||
Inspect check
|
||||
Controle bekijken
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -474,7 +487,7 @@ export function QualityResultsPanel({
|
||||
onClick={() => onOpenEvidenceMap?.(check.id)}
|
||||
disabled={evidenceLoading || !onOpenEvidenceMap}
|
||||
>
|
||||
Show evidence on map
|
||||
Bewijs op kaart tonen
|
||||
</button>
|
||||
</div>
|
||||
<div className="quality-score-row">
|
||||
@@ -483,16 +496,16 @@ export function QualityResultsPanel({
|
||||
<strong>{check.score ?? 'n/a'}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Metrics</span>
|
||||
<span>Meetwaarden</span>
|
||||
<strong>{check.metrics.length}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Quality check</span>
|
||||
<span>Controle-ID</span>
|
||||
<strong>{check.id}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div className="quality-metric-section">
|
||||
<span>Metric evidence</span>
|
||||
<span>Gemeten kwaliteit</span>
|
||||
<div className="quality-metric-grid">
|
||||
{CORE_METRIC_ORDER.map((metricKey) => {
|
||||
const metric = check.metrics.find((item) => item.metric_key === metricKey)
|
||||
@@ -506,7 +519,7 @@ export function QualityResultsPanel({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<span className="metric-list-label">Raw metrics</span>
|
||||
<span className="metric-list-label">Alle meetwaarden</span>
|
||||
<ul className="metric-list">
|
||||
{check.metrics.map((metric) => (
|
||||
<li key={metric.id}>
|
||||
@@ -519,6 +532,8 @@ export function QualityResultsPanel({
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user