Polish dataset catalog actions
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 69 Data catalog action polish (2026-06-19)
|
||||||
|
|
||||||
|
- Added recommended-action hints to each dataset card so reference and candidate layers explain their QA role.
|
||||||
|
- Reworked dataset card actions into compact two-line buttons for Inspect, Map, Export / QA and Metadata.
|
||||||
|
- Preserved existing handlers, API contracts and persistence behavior; this is UI affordance polish only.
|
||||||
|
- Added static regression coverage for the action hints, disabled-action copy and responsive action grid.
|
||||||
|
- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed.
|
||||||
|
|
||||||
## Sprint 68 Data catalog density polish (2026-06-19)
|
## Sprint 68 Data catalog density polish (2026-06-19)
|
||||||
|
|
||||||
- Added a compact Data catalog summary for Selected, Reference, Candidate and Source layers.
|
- Added a compact Data catalog summary for Selected, Reference, Candidate and Source layers.
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_dataset_panel_explains_recommended_actions() -> None:
|
||||||
|
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "datasetActionHint" in dataset_panel
|
||||||
|
assert "dataset-action-hint" in dataset_panel
|
||||||
|
assert "QA reference layer" in dataset_panel
|
||||||
|
assert "QA candidate layer" in dataset_panel
|
||||||
|
assert "Open on the map or send to Export / QA." in dataset_panel
|
||||||
|
|
||||||
|
|
||||||
|
def test_dataset_buttons_have_scan_friendly_action_copy() -> None:
|
||||||
|
dataset_panel = (ROOT / "frontend" / "src" / "components" / "datasets" / "DatasetPanel.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "dataset-action-grid" in dataset_panel
|
||||||
|
assert "Inspect" in dataset_panel
|
||||||
|
assert "Map" in dataset_panel
|
||||||
|
assert "Export / QA" in dataset_panel
|
||||||
|
assert "Metadata" in dataset_panel
|
||||||
|
assert "Vector/GeoJSON only" in dataset_panel
|
||||||
|
assert "Raster metadata is automatic" in dataset_panel
|
||||||
|
|
||||||
|
|
||||||
|
def test_dataset_action_styles_keep_buttons_dense_and_responsive() -> None:
|
||||||
|
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert ".dataset-action-hint" in css
|
||||||
|
assert ".dataset-action-grid" in css
|
||||||
|
assert "repeat(auto-fit, minmax(8.75rem, 1fr))" in css
|
||||||
|
assert ".dataset-action-button" in css
|
||||||
|
assert ".dataset-action-button small" in css
|
||||||
@@ -2603,3 +2603,32 @@ Limitations:
|
|||||||
|
|
||||||
Next recommended pass:
|
Next recommended pass:
|
||||||
- Continue with Data workspace action polish, especially making QA/export affordances clearer once candidate and reference layers are present.
|
- Continue with Data workspace action polish, especially making QA/export affordances clearer once candidate and reference layers are present.
|
||||||
|
|
||||||
|
## Sprint 69 Data catalog action polish (2026-06-19)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Added role-aware recommended action hints to dataset cards.
|
||||||
|
- Reworked dataset card actions into compact two-line buttons for Inspect, Map, Export / QA and Metadata.
|
||||||
|
- Kept all existing Data workspace handlers and API calls unchanged.
|
||||||
|
- Added disabled-action explanation copy for unsupported export/QA and raster metadata refresh cases.
|
||||||
|
- Added responsive `.dataset-action-grid` and `.dataset-action-button` CSS.
|
||||||
|
- Added `backend/tests/test_sprint69_dataset_action_polish.py`.
|
||||||
|
- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`.
|
||||||
|
|
||||||
|
Tested:
|
||||||
|
- Red step: `python -m pytest backend/tests/test_sprint69_dataset_action_polish.py -q` failed on missing action hints, action grid and responsive styles.
|
||||||
|
- `python -m pytest backend/tests/test_sprint69_dataset_action_polish.py backend/tests/test_sprint68_dataset_catalog_density.py backend/tests/test_sprint29_dataset_components.py -q` (`9 passed`)
|
||||||
|
- `cd frontend && npm run typecheck`
|
||||||
|
- `cd frontend && npm run build`
|
||||||
|
- Local browser check against `http://127.0.0.1:5175` with live API proxy: Data workspace rendered 2 role-aware hints and 8 compact dataset actions across the demo candidate/reference datasets, no console warnings/errors and no horizontal overflow on desktop or mobile.
|
||||||
|
- Screenshots captured under `artifacts/sprint69-dataset-action-polish/`.
|
||||||
|
- `bash scripts/run_readiness_check.sh` (`246 passed`)
|
||||||
|
|
||||||
|
Open:
|
||||||
|
- Run full readiness, redeploy Tower and verify the live Data action polish after deployment.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
- This is a frontend affordance/readability pass only. It does not add new workflows, change dataset persistence, change API contracts, add migrations, fetch providers or alter AI/model behavior.
|
||||||
|
|
||||||
|
Next recommended pass:
|
||||||
|
- Continue with QA/QC workspace handoff polish, especially linking the existing candidate/reference dataset context more clearly to persisted QA results.
|
||||||
|
|||||||
@@ -340,3 +340,4 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Polish lightweight HTML project report readability, print styling and handoff sections.
|
- [x] Polish lightweight HTML project report readability, print styling and handoff sections.
|
||||||
- [x] Add Map empty-state quick actions for ready vector datasets.
|
- [x] Add Map empty-state quick actions for ready vector datasets.
|
||||||
- [x] Add Data catalog role-density polish for reference/candidate/source scanning.
|
- [x] Add Data catalog role-density polish for reference/candidate/source scanning.
|
||||||
|
- [x] Add Data catalog action polish for map, metadata, export and QA affordances.
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ The workbench now uses a task-based shell instead of a single long panel stack.
|
|||||||
- The Map workspace shows active layer source/provenance/draw-state context and selected-feature property chips before the raw JSON inspector.
|
- The Map workspace shows active layer source/provenance/draw-state context and selected-feature property chips before the raw JSON inspector.
|
||||||
- When the Map workspace has no active result layer, it lists ready vector/GeoJSON datasets as direct quick actions so populated demo projects can jump straight from the empty state to map inspection.
|
- When the Map workspace has no active result layer, it lists ready vector/GeoJSON datasets as direct quick actions so populated demo projects can jump straight from the empty state to map inspection.
|
||||||
- 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`.
|
||||||
|
|
||||||
## Raster dependency visibility
|
## Raster dependency visibility
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,19 @@ function datasetRoleLabel(role: string): string {
|
|||||||
return 'Source'
|
return 'Source'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function datasetActionHint(dataset: DatasetCreateResponse, role: string): string {
|
||||||
|
if (role === 'reference') {
|
||||||
|
return 'QA reference layer. Select it when comparing candidates against trusted geometry.'
|
||||||
|
}
|
||||||
|
if (role === 'candidate') {
|
||||||
|
return 'QA candidate layer. Open on the map or send to Export / QA.'
|
||||||
|
}
|
||||||
|
if (dataset.dataset_type === 'raster') {
|
||||||
|
return 'Raster source. Use the raster tools in the inspector after selecting it.'
|
||||||
|
}
|
||||||
|
return 'Source dataset. Inspect details before using it in downstream workflows.'
|
||||||
|
}
|
||||||
|
|
||||||
export function DatasetPanel({
|
export function DatasetPanel({
|
||||||
selectedProjectId,
|
selectedProjectId,
|
||||||
selectedDatasetId,
|
selectedDatasetId,
|
||||||
@@ -225,33 +238,44 @@ export function DatasetPanel({
|
|||||||
<span>features: {dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 'n/a'}</span>
|
<span>features: {dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 'n/a'}</span>
|
||||||
<span>bbox: {formatBounds(dataset.bounds_json ?? dataset.vector_summary?.bounds_json)}</span>
|
<span>bbox: {formatBounds(dataset.bounds_json ?? dataset.vector_summary?.bounds_json)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="button-row">
|
<p className="dataset-action-hint">{datasetActionHint(dataset, datasetRole)}</p>
|
||||||
|
<div className="dataset-action-grid">
|
||||||
<button
|
<button
|
||||||
className="primary-action"
|
className="primary-action dataset-action-button"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onLoadDatasetDetails(selectedProjectId ?? '', dataset)}
|
onClick={() => onLoadDatasetDetails(selectedProjectId ?? '', dataset)}
|
||||||
data-testid={`dataset-select-${dataset.id}`}
|
data-testid={`dataset-select-${dataset.id}`}
|
||||||
>
|
>
|
||||||
Select / details
|
<span>Inspect</span>
|
||||||
</button>
|
<small>Select / details</small>
|
||||||
<button className="secondary-action" type="button" onClick={() => onOpenDatasetInMap(dataset)}>
|
|
||||||
Open in map
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="secondary-action"
|
className="secondary-action dataset-action-button"
|
||||||
|
type="button"
|
||||||
|
onClick={() => onOpenDatasetInMap(dataset)}
|
||||||
|
aria-label="Open in map"
|
||||||
|
>
|
||||||
|
<span>Map</span>
|
||||||
|
<small>Open layer</small>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="secondary-action dataset-action-button"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onOpenDatasetExport(dataset)}
|
onClick={() => onOpenDatasetExport(dataset)}
|
||||||
disabled={!(dataset.dataset_type === 'vector' || dataset.dataset_type === 'geojson')}
|
disabled={!(dataset.dataset_type === 'vector' || dataset.dataset_type === 'geojson')}
|
||||||
>
|
>
|
||||||
Export / QA
|
<span>Export / QA</span>
|
||||||
|
<small>{dataset.dataset_type === 'vector' || dataset.dataset_type === 'geojson' ? 'Compare or export' : 'Vector/GeoJSON only'}</small>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="secondary-action"
|
className="secondary-action dataset-action-button"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onRefreshMetadata(dataset.id)}
|
onClick={() => onRefreshMetadata(dataset.id)}
|
||||||
disabled={dataset.dataset_type === 'raster'}
|
disabled={dataset.dataset_type === 'raster'}
|
||||||
|
aria-label="Refresh metadata"
|
||||||
>
|
>
|
||||||
Refresh metadata
|
<span>Metadata</span>
|
||||||
|
<small>{dataset.dataset_type === 'raster' ? 'Raster metadata is automatic' : 'Refresh vector stats'}</small>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -1137,14 +1137,47 @@ button.entity-card {
|
|||||||
margin-top: 0.7rem;
|
margin-top: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dataset-card .button-row {
|
.dataset-action-hint {
|
||||||
display: grid;
|
margin: 0.65rem 0 0;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
color: #36524b;
|
||||||
|
font-size: 0.84rem;
|
||||||
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dataset-card .button-row button {
|
.dataset-card .button-row,
|
||||||
|
.dataset-action-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(8.75rem, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-action-button {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.12rem;
|
||||||
|
align-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
min-height: 3.25rem;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-action-button span,
|
||||||
|
.dataset-action-button small {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-action-button span {
|
||||||
|
font-weight: 850;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-action-button small {
|
||||||
|
font-size: 0.73rem;
|
||||||
|
font-weight: 750;
|
||||||
|
line-height: 1.2;
|
||||||
|
opacity: 0.78;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-row {
|
.button-row {
|
||||||
|
|||||||
Reference in New Issue
Block a user