Add map selection QA shortcut
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 109 Map selection QA shortcut (2026-06-25)
|
||||||
|
|
||||||
|
- Added a Map workspace QA/QC shortcut for saved derived selection datasets.
|
||||||
|
- The shortcut reuses the existing QA comparison workflow and persists `QualityCheck`/`Metric` rows through the existing backend route.
|
||||||
|
- Added reference dataset selection, loading/error state and compact precision/recall/F1/status feedback beside the saved map selection.
|
||||||
|
- Kept QA orchestration in a dedicated frontend hook so `App.tsx` remains an orchestrator and API calls stay out of the shell component.
|
||||||
|
- No backend API contracts, migrations, provider fetching, AI behavior or new product domains were introduced.
|
||||||
|
|
||||||
## Sprint 108 Map selection derived datasets (2026-06-25)
|
## Sprint 108 Map selection derived datasets (2026-06-25)
|
||||||
|
|
||||||
- Added `POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/select/derive` to persist a map bbox selection as a reusable derived vector dataset.
|
- Added `POST /api/v1/projects/{project_id}/datasets/{dataset_id}/vector/select/derive` to persist a map bbox selection as a reusable derived vector dataset.
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None:
|
||||||
|
hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts"
|
||||||
|
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
|
||||||
|
map_workspace = (ROOT / "frontend" / "src" / "components" / "map" / "MapWorkspace.tsx").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert hook_path.exists()
|
||||||
|
hook = hook_path.read_text(encoding="utf-8")
|
||||||
|
assert "qaApi.runQa" in hook
|
||||||
|
assert "loadQualityChecks" in hook
|
||||||
|
assert "candidate_dataset_id: latestSelectionDataset.id" in hook
|
||||||
|
assert "reference_dataset_id: selectedMapQaReferenceDatasetId" in hook
|
||||||
|
assert "setMapQaResult" in hook
|
||||||
|
assert "useMapSelectionQa" in app
|
||||||
|
assert "mapQaReferenceDatasets={referenceDatasets}" in app
|
||||||
|
assert "onRunMapSelectionQa={runMapSelectionQa}" in app
|
||||||
|
assert "Run QA on saved dataset" in map_workspace
|
||||||
|
assert "mapSelectionQaError" in map_workspace
|
||||||
|
assert "mapSelectionQaResult" in map_workspace
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_keeps_qa_api_calls_out_of_orchestration() -> None:
|
||||||
|
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "qaApi" not in app
|
||||||
|
assert "from './services/api'" not in app
|
||||||
@@ -1,3 +1,28 @@
|
|||||||
|
## Sprint 109 Map selection QA shortcut (2026-06-25)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Added `useMapSelectionQa` to keep Map workspace QA/QC orchestration out of `App.tsx`.
|
||||||
|
- Added a Map workspace QA/QC shortcut after `Save as dataset`, allowing the latest derived map selection dataset to be compared against a selected reference dataset.
|
||||||
|
- The shortcut reuses the existing `qaApi.runQa` flow and refreshes persisted quality checks/project data after completion.
|
||||||
|
- Added inline precision, recall, F1 and quality-check status feedback in the Map workspace.
|
||||||
|
- Added compact styling for the Map selection QA surface.
|
||||||
|
- Updated `frontend/README.md`, `CHANGELOG.md` and `docs/TODO.md`.
|
||||||
|
- Added regression coverage in `backend/tests/test_sprint109_map_selection_qa_shortcut.py`.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- RED: `python -m pytest backend\tests\test_sprint109_map_selection_qa_shortcut.py -q` failed before implementation because the hook and wiring were absent.
|
||||||
|
- `python -m pytest backend\tests\test_sprint109_map_selection_qa_shortcut.py -q` passed: 2 tests.
|
||||||
|
- `cd frontend && npm run typecheck` passed.
|
||||||
|
- `cd backend && python -m pytest -q` passed: 347 tests.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
- The Map shortcut currently uses the existing QA comparison defaults with IoU threshold `0.5`.
|
||||||
|
- QA results are summarized inline; detailed false-positive/false-negative evidence remains in the QA/QC workspace.
|
||||||
|
- No backend API contract, migration, provider fetching, AI dependency, real model behavior or new product domain was added.
|
||||||
|
|
||||||
|
Next recommended pass:
|
||||||
|
- Add a QA result drilldown/handoff from the Map workspace so operators can inspect persisted false-positive and false-negative evidence directly after running selection QA/QC.
|
||||||
|
|
||||||
## Sprint 108 Map selection derived datasets (2026-06-25)
|
## Sprint 108 Map selection derived datasets (2026-06-25)
|
||||||
|
|
||||||
Changed:
|
Changed:
|
||||||
|
|||||||
@@ -375,3 +375,4 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Add persisted vector area selection from the Map workspace with bbox extract and GeoJSON download.
|
- [x] Add persisted vector area selection from the Map workspace with bbox extract and GeoJSON download.
|
||||||
- [x] Persist map area selections as Export Center handoff artifacts.
|
- [x] Persist map area selections as Export Center handoff artifacts.
|
||||||
- [x] Persist map area selections as reusable derived vector datasets indexed into `vector_features`.
|
- [x] Persist map area selections as reusable derived vector datasets indexed into `vector_features`.
|
||||||
|
- [x] Add Map workspace QA/QC shortcut for saved derived selection datasets.
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ AI Lab run controls explicitly explain when no raster dataset is available, inst
|
|||||||
- The Map workspace can extract persisted vector features by area: open a ready vector dataset, use `Start map bbox` and click two map corners or enter EPSG:4326 bbox values, then run `Run area extract` to query backend `vector_features`. Results are highlighted on the map and can be downloaded as GeoJSON.
|
- The Map workspace can extract persisted vector features by area: open a ready vector dataset, use `Start map bbox` and click two map corners or enter EPSG:4326 bbox values, then run `Run area extract` to query backend `vector_features`. Results are highlighted on the map and can be downloaded as GeoJSON.
|
||||||
- After an area extract, `Save area export` persists the selected FeatureCollection as a normal Export Center artifact (`vector_selection_geojson`) so the handoff remains in project export history.
|
- After an area extract, `Save area export` persists the selected FeatureCollection as a normal Export Center artifact (`vector_selection_geojson`) so the handoff remains in project export history.
|
||||||
- `Save as dataset` persists the same selected FeatureCollection as a derived vector dataset, selects it in the workbench and keeps it queryable through backend `vector_features` for later QA/QC or analysis.
|
- `Save as dataset` persists the same selected FeatureCollection as a derived vector dataset, selects it in the workbench and keeps it queryable through backend `vector_features` for later QA/QC or analysis.
|
||||||
|
- Once a map selection has been saved as a derived dataset, the Map workspace can run QA/QC against a selected reference dataset without switching workspaces. The action reuses the existing QA comparison endpoint and shows precision, recall, F1 and persisted quality-check status inline.
|
||||||
- The Data catalog shows a compact selected/reference/candidate/source summary and scan-friendly badges. Persisted `reference` datasets are shown as Reference, non-reference vector/GeoJSON layers are shown as QA Candidates for workbench scanning, and raster/other uploads remain Source.
|
- The Data catalog shows a compact selected/reference/candidate/source summary and scan-friendly badges. Persisted `reference` datasets are shown as Reference, non-reference vector/GeoJSON layers are shown as QA Candidates for workbench scanning, and raster/other uploads remain Source.
|
||||||
- Dataset cards explain the recommended next action and use compact two-line action buttons for inspect, map, export/QA and metadata refresh. Disabled actions keep a visible reason, such as `Vector/GeoJSON only`.
|
- Dataset cards explain the recommended next action and use compact two-line action buttons for inspect, map, export/QA and metadata refresh. Disabled actions keep a visible reason, such as `Vector/GeoJSON only`.
|
||||||
- Raster controls show the latest generated tile manifest path from persisted `raster.tile` jobs and can hand that path directly to Detection Lab or Segmentation Lab with the selected raster dataset.
|
- Raster controls show the latest generated tile manifest path from persisted `raster.tile` jobs and can hand that path directly to Detection Lab or Segmentation Lab with the selected raster dataset.
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { useDetectionWorkflow } from './hooks/useDetectionWorkflow'
|
|||||||
import { useDatasetWorkflow } from './hooks/useDatasetWorkflow'
|
import { useDatasetWorkflow } from './hooks/useDatasetWorkflow'
|
||||||
import { useExportWorkflow } from './hooks/useExportWorkflow'
|
import { useExportWorkflow } from './hooks/useExportWorkflow'
|
||||||
import { useMapSelectionDataset } from './hooks/useMapSelectionDataset'
|
import { useMapSelectionDataset } from './hooks/useMapSelectionDataset'
|
||||||
|
import { useMapSelectionQa } from './hooks/useMapSelectionQa'
|
||||||
import { useMapWorkspaceState } from './hooks/useMapWorkspaceState'
|
import { useMapWorkspaceState } from './hooks/useMapWorkspaceState'
|
||||||
import { useMapSelectionExtract } from './hooks/useMapSelectionExtract'
|
import { useMapSelectionExtract } from './hooks/useMapSelectionExtract'
|
||||||
import { useProviderCapabilities } from './hooks/useProviderCapabilities'
|
import { useProviderCapabilities } from './hooks/useProviderCapabilities'
|
||||||
@@ -399,6 +400,19 @@ function App(): JSX.Element {
|
|||||||
loadDatasetDetails,
|
loadDatasetDetails,
|
||||||
setMapLayerVisible,
|
setMapLayerVisible,
|
||||||
})
|
})
|
||||||
|
const {
|
||||||
|
selectedMapQaReferenceDatasetId,
|
||||||
|
mapSelectionQaRunning,
|
||||||
|
mapSelectionQaError,
|
||||||
|
mapSelectionQaResult,
|
||||||
|
runMapSelectionQa,
|
||||||
|
setSelectedMapQaReferenceDatasetId,
|
||||||
|
} = useMapSelectionQa({
|
||||||
|
selectedProjectId,
|
||||||
|
latestSelectionDataset,
|
||||||
|
loadQualityChecks,
|
||||||
|
loadProjectData,
|
||||||
|
})
|
||||||
const {
|
const {
|
||||||
loadingDemoWorkflow,
|
loadingDemoWorkflow,
|
||||||
demoWorkflowMessage,
|
demoWorkflowMessage,
|
||||||
@@ -826,6 +840,11 @@ function App(): JSX.Element {
|
|||||||
selectionDatasetSaving={selectionDatasetSaving}
|
selectionDatasetSaving={selectionDatasetSaving}
|
||||||
selectionDatasetError={selectionDatasetError}
|
selectionDatasetError={selectionDatasetError}
|
||||||
latestSelectionDatasetName={latestSelectionDataset?.name ?? null}
|
latestSelectionDatasetName={latestSelectionDataset?.name ?? null}
|
||||||
|
mapQaReferenceDatasets={referenceDatasets}
|
||||||
|
selectedMapQaReferenceDatasetId={selectedMapQaReferenceDatasetId}
|
||||||
|
mapSelectionQaRunning={mapSelectionQaRunning}
|
||||||
|
mapSelectionQaError={mapSelectionQaError}
|
||||||
|
mapSelectionQaResult={mapSelectionQaResult}
|
||||||
availableMapDatasets={availableMapDatasets}
|
availableMapDatasets={availableMapDatasets}
|
||||||
selectedFeature={selectedMapFeature}
|
selectedFeature={selectedMapFeature}
|
||||||
onSelectMapArea={setSelectedMapAreaId}
|
onSelectMapArea={setSelectedMapAreaId}
|
||||||
@@ -840,6 +859,8 @@ function App(): JSX.Element {
|
|||||||
onClearMapSelectionExtract={resetMapSelectionExtract}
|
onClearMapSelectionExtract={resetMapSelectionExtract}
|
||||||
onExportMapSelection={exportMapSelectionGeoJson}
|
onExportMapSelection={exportMapSelectionGeoJson}
|
||||||
onDeriveMapSelectionDataset={deriveMapSelectionDataset}
|
onDeriveMapSelectionDataset={deriveMapSelectionDataset}
|
||||||
|
onSelectMapQaReferenceDataset={setSelectedMapQaReferenceDatasetId}
|
||||||
|
onRunMapSelectionQa={runMapSelectionQa}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import GeoMap from '../GeoMap'
|
import GeoMap from '../GeoMap'
|
||||||
import type { AreaRead, DatasetCreateResponse, VectorSelectionBBox, VectorSelectionResponse } from '../../types'
|
import type { AreaRead, DatasetCreateResponse, QaComparisonResult, VectorSelectionBBox, VectorSelectionResponse } from '../../types'
|
||||||
|
|
||||||
const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
|
const DEFAULT_SELECTED_FEATURE_FILENAME = 'selected-feature.geojson'
|
||||||
const DEFAULT_AREA_SELECTION_FILENAME = 'area-selection.geojson'
|
const DEFAULT_AREA_SELECTION_FILENAME = 'area-selection.geojson'
|
||||||
@@ -197,6 +197,11 @@ interface MapWorkspaceProps {
|
|||||||
selectionDatasetSaving: boolean
|
selectionDatasetSaving: boolean
|
||||||
selectionDatasetError: string | null
|
selectionDatasetError: string | null
|
||||||
latestSelectionDatasetName: string | null
|
latestSelectionDatasetName: string | null
|
||||||
|
mapQaReferenceDatasets: DatasetCreateResponse[]
|
||||||
|
selectedMapQaReferenceDatasetId: string
|
||||||
|
mapSelectionQaRunning: boolean
|
||||||
|
mapSelectionQaError: string | null
|
||||||
|
mapSelectionQaResult: QaComparisonResult | null
|
||||||
availableMapDatasets: DatasetCreateResponse[]
|
availableMapDatasets: DatasetCreateResponse[]
|
||||||
onSelectMapArea: (areaId: string) => void
|
onSelectMapArea: (areaId: string) => void
|
||||||
onOpenDatasetInMap: (dataset: DatasetCreateResponse) => void
|
onOpenDatasetInMap: (dataset: DatasetCreateResponse) => void
|
||||||
@@ -210,6 +215,8 @@ interface MapWorkspaceProps {
|
|||||||
onClearMapSelectionExtract: () => void
|
onClearMapSelectionExtract: () => void
|
||||||
onExportMapSelection: (bbox: VectorSelectionBBox) => void
|
onExportMapSelection: (bbox: VectorSelectionBBox) => void
|
||||||
onDeriveMapSelectionDataset: (bbox: VectorSelectionBBox) => void
|
onDeriveMapSelectionDataset: (bbox: VectorSelectionBBox) => void
|
||||||
|
onSelectMapQaReferenceDataset: (datasetId: string) => void
|
||||||
|
onRunMapSelectionQa: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MapWorkspace({
|
export function MapWorkspace({
|
||||||
@@ -238,6 +245,11 @@ export function MapWorkspace({
|
|||||||
selectionDatasetSaving,
|
selectionDatasetSaving,
|
||||||
selectionDatasetError,
|
selectionDatasetError,
|
||||||
latestSelectionDatasetName,
|
latestSelectionDatasetName,
|
||||||
|
mapQaReferenceDatasets,
|
||||||
|
selectedMapQaReferenceDatasetId,
|
||||||
|
mapSelectionQaRunning,
|
||||||
|
mapSelectionQaError,
|
||||||
|
mapSelectionQaResult,
|
||||||
availableMapDatasets,
|
availableMapDatasets,
|
||||||
onSelectMapArea,
|
onSelectMapArea,
|
||||||
onOpenDatasetInMap,
|
onOpenDatasetInMap,
|
||||||
@@ -251,6 +263,8 @@ export function MapWorkspace({
|
|||||||
onClearMapSelectionExtract,
|
onClearMapSelectionExtract,
|
||||||
onExportMapSelection,
|
onExportMapSelection,
|
||||||
onDeriveMapSelectionDataset,
|
onDeriveMapSelectionDataset,
|
||||||
|
onSelectMapQaReferenceDataset,
|
||||||
|
onRunMapSelectionQa,
|
||||||
}: MapWorkspaceProps): JSX.Element {
|
}: MapWorkspaceProps): JSX.Element {
|
||||||
const [bboxSelectionMode, setBboxSelectionMode] = useState(false)
|
const [bboxSelectionMode, setBboxSelectionMode] = useState(false)
|
||||||
const [firstSelectionCorner, setFirstSelectionCorner] = useState<[number, number] | null>(null)
|
const [firstSelectionCorner, setFirstSelectionCorner] = useState<[number, number] | null>(null)
|
||||||
@@ -644,6 +658,54 @@ export function MapWorkspace({
|
|||||||
{latestSelectionDatasetName ? (
|
{latestSelectionDatasetName ? (
|
||||||
<p className="muted">Saved derived dataset: {latestSelectionDatasetName}</p>
|
<p className="muted">Saved derived dataset: {latestSelectionDatasetName}</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
{latestSelectionDatasetName ? (
|
||||||
|
<div className="map-selection-qa-surface" aria-label="Map selection QA shortcut">
|
||||||
|
<label>
|
||||||
|
Reference dataset
|
||||||
|
<select
|
||||||
|
value={selectedMapQaReferenceDatasetId}
|
||||||
|
onChange={(event) => onSelectMapQaReferenceDataset(event.target.value)}
|
||||||
|
disabled={mapQaReferenceDatasets.length === 0 || mapSelectionQaRunning}
|
||||||
|
>
|
||||||
|
<option value="">Select reference</option>
|
||||||
|
{mapQaReferenceDatasets.map((dataset) => (
|
||||||
|
<option key={dataset.id} value={dataset.id}>
|
||||||
|
{dataset.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<button
|
||||||
|
className="primary-action"
|
||||||
|
disabled={!selectedMapQaReferenceDatasetId || mapSelectionQaRunning}
|
||||||
|
type="button"
|
||||||
|
onClick={onRunMapSelectionQa}
|
||||||
|
>
|
||||||
|
{mapSelectionQaRunning ? 'Running QA...' : 'Run QA on saved dataset'}
|
||||||
|
</button>
|
||||||
|
{mapSelectionQaError ? <p className="error">{mapSelectionQaError}</p> : null}
|
||||||
|
{mapSelectionQaResult ? (
|
||||||
|
<div className="feature-extract-grid" aria-label="Map selection QA result">
|
||||||
|
<div>
|
||||||
|
<span>Precision</span>
|
||||||
|
<strong>{mapSelectionQaResult.precision ?? 'n/a'}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Recall</span>
|
||||||
|
<strong>{mapSelectionQaResult.recall ?? 'n/a'}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>F1</span>
|
||||||
|
<strong>{mapSelectionQaResult.f1_score ?? 'n/a'}</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Quality check</span>
|
||||||
|
<strong>{mapSelectionQaResult.status}</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
{areaSelectionPreviewFeatures.length > 0 ? (
|
{areaSelectionPreviewFeatures.length > 0 ? (
|
||||||
<div className="table-scroll feature-property-table" aria-label="Area selection feature table">
|
<div className="table-scroll feature-property-table" aria-label="Area selection feature table">
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import type { DatasetCreateResponse, JobRead, QaComparisonRequest, QaComparisonResult, QualityCheckRead } from '../types'
|
||||||
|
import { formatError } from '../lib/formatError'
|
||||||
|
import { qaApi } from '../services/api'
|
||||||
|
|
||||||
|
interface UseMapSelectionQaOptions {
|
||||||
|
selectedProjectId: string | null
|
||||||
|
latestSelectionDataset: DatasetCreateResponse | null
|
||||||
|
loadQualityChecks: (projectId?: string | null) => Promise<QualityCheckRead[] | void>
|
||||||
|
loadProjectData: (projectId: string) => Promise<unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useMapSelectionQa({
|
||||||
|
selectedProjectId,
|
||||||
|
latestSelectionDataset,
|
||||||
|
loadQualityChecks,
|
||||||
|
loadProjectData,
|
||||||
|
}: UseMapSelectionQaOptions) {
|
||||||
|
const [selectedMapQaReferenceDatasetId, setSelectedMapQaReferenceDatasetId] = useState('')
|
||||||
|
const [mapSelectionQaRunning, setMapSelectionQaRunning] = useState(false)
|
||||||
|
const [mapSelectionQaError, setMapSelectionQaError] = useState<string | null>(null)
|
||||||
|
const [mapSelectionQaResult, setMapQaResult] = useState<QaComparisonResult | null>(null)
|
||||||
|
|
||||||
|
const runMapSelectionQa = async () => {
|
||||||
|
if (!selectedProjectId) {
|
||||||
|
setMapSelectionQaError('Select a project before running QA/QC.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!latestSelectionDataset) {
|
||||||
|
setMapSelectionQaError('Save the map selection as a dataset before running QA/QC.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!selectedMapQaReferenceDatasetId) {
|
||||||
|
setMapSelectionQaError('Select a reference dataset for QA/QC.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (latestSelectionDataset.id === selectedMapQaReferenceDatasetId) {
|
||||||
|
setMapSelectionQaError('Candidate and reference datasets must be different.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setMapSelectionQaRunning(true)
|
||||||
|
setMapSelectionQaError(null)
|
||||||
|
setMapQaResult(null)
|
||||||
|
try {
|
||||||
|
const request: QaComparisonRequest = {
|
||||||
|
candidate_dataset_id: latestSelectionDataset.id,
|
||||||
|
reference_dataset_id: selectedMapQaReferenceDatasetId,
|
||||||
|
iou_threshold: 0.5,
|
||||||
|
area_id: latestSelectionDataset.area_id ?? null,
|
||||||
|
}
|
||||||
|
const job: JobRead = await qaApi.runQa(request)
|
||||||
|
if (job.status === 'failed') {
|
||||||
|
setMapSelectionQaError(job.error_message || 'Map selection QA/QC failed')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const payload = job.result_json
|
||||||
|
if (!payload || typeof payload !== 'object') {
|
||||||
|
setMapSelectionQaError('Map selection QA/QC result was not available')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parsed = payload as unknown as QaComparisonResult
|
||||||
|
if (!parsed || typeof parsed.status !== 'string') {
|
||||||
|
setMapSelectionQaError('Map selection QA/QC result format was unexpected')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setMapQaResult(parsed)
|
||||||
|
await loadQualityChecks(selectedProjectId)
|
||||||
|
await loadProjectData(selectedProjectId)
|
||||||
|
} catch (error) {
|
||||||
|
setMapSelectionQaError(formatError(error, 'Map selection QA/QC failed'))
|
||||||
|
} finally {
|
||||||
|
setMapSelectionQaRunning(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectedMapQaReferenceDatasetId,
|
||||||
|
mapSelectionQaRunning,
|
||||||
|
mapSelectionQaError,
|
||||||
|
mapSelectionQaResult,
|
||||||
|
runMapSelectionQa,
|
||||||
|
setSelectedMapQaReferenceDatasetId,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2669,6 +2669,37 @@ button.entity-card {
|
|||||||
padding-top: 0.68rem;
|
padding-top: 0.68rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.map-selection-qa-surface {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(11rem, 1fr) auto;
|
||||||
|
gap: 0.58rem;
|
||||||
|
align-items: end;
|
||||||
|
min-width: 0;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
padding-top: 0.68rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-selection-qa-surface label {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-selection-qa-surface .primary-action {
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map-selection-qa-surface .error,
|
||||||
|
.map-selection-qa-surface .feature-extract-grid {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.map-selection-qa-surface {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.feature-extract-surface {
|
.feature-extract-surface {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.7rem;
|
gap: 0.7rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user