Polish change detection density
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 87 Change Detection density polish (2026-06-20)
|
||||||
|
|
||||||
|
- Grouped Change Detection heading, input controls, result states, summary and warnings into focused surfaces.
|
||||||
|
- Reused shared result-state cards for not-enough-data and error states.
|
||||||
|
- Added compact desktop/mobile grids for vector inputs and change summary metrics.
|
||||||
|
- Added static regression coverage for Change Detection hierarchy and density contracts.
|
||||||
|
- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed.
|
||||||
|
|
||||||
## Sprint 86 QA/QC workspace density polish (2026-06-20)
|
## Sprint 86 QA/QC workspace density polish (2026-06-20)
|
||||||
|
|
||||||
- Grouped QA/QC summary, dataset evidence, refresh/filter controls and result history into focused surfaces.
|
- Grouped QA/QC summary, dataset evidence, refresh/filter controls and result history into focused surfaces.
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_detection_panel_exposes_structured_surfaces() -> None:
|
||||||
|
panel = (ROOT / "frontend" / "src" / "components" / "analysis" / "ChangeDetectionPanel.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 'className="panel change-detection-shell"' in panel
|
||||||
|
assert 'className="panel-header change-detection-heading"' in panel
|
||||||
|
assert 'className="change-detection-input-surface"' in panel
|
||||||
|
assert 'aria-label="Change detection input controls"' in panel
|
||||||
|
assert 'className="change-detection-state-stack"' in panel
|
||||||
|
assert 'className="change-detection-result-surface"' in panel
|
||||||
|
assert 'aria-label="Change detection result summary"' in panel
|
||||||
|
assert 'className="change-detection-warning-surface"' in panel
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_detection_panel_preserves_existing_compare_controls() -> None:
|
||||||
|
panel = (ROOT / "frontend" / "src" / "components" / "analysis" / "ChangeDetectionPanel.tsx").read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "onSourceDatasetChange" in panel
|
||||||
|
assert "onTargetDatasetChange" in panel
|
||||||
|
assert "onIouThresholdChange" in panel
|
||||||
|
assert "onIncludeUnchangedChange" in panel
|
||||||
|
assert "onRun" in panel
|
||||||
|
assert "Compare vectors" in panel
|
||||||
|
assert "Upload at least two vector datasets to compare." in panel
|
||||||
|
assert "result.added_count" in panel
|
||||||
|
assert "result.removed_count" in panel
|
||||||
|
assert "result.unchanged_count" in panel
|
||||||
|
|
||||||
|
|
||||||
|
def test_change_detection_density_css_contracts() -> None:
|
||||||
|
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert ".change-detection-shell" in css
|
||||||
|
assert ".change-detection-heading" in css
|
||||||
|
assert ".change-detection-input-surface" in css
|
||||||
|
assert ".change-detection-state-stack" in css
|
||||||
|
assert ".change-detection-result-surface" in css
|
||||||
|
assert ".change-detection-warning-surface" in css
|
||||||
|
assert ".change-detection-shell .form-grid" in css
|
||||||
|
assert ".change-detection-shell .summary-grid" in css
|
||||||
|
assert "grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));" in css
|
||||||
|
assert "grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));" in css
|
||||||
@@ -3143,3 +3143,31 @@ Limitations:
|
|||||||
|
|
||||||
Next recommended pass:
|
Next recommended pass:
|
||||||
- Continue with Change Detection panel hierarchy and analysis workspace balance after this pass is deployed and visually checked.
|
- Continue with Change Detection panel hierarchy and analysis workspace balance after this pass is deployed and visually checked.
|
||||||
|
|
||||||
|
## Sprint 87 Change Detection density polish (2026-06-20)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Wrapped Change Detection input controls in `change-detection-input-surface`.
|
||||||
|
- Replaced loose error/empty text with shared `result-state` cards inside `change-detection-state-stack`.
|
||||||
|
- Wrapped result metrics in `change-detection-result-surface`.
|
||||||
|
- Wrapped warning output in `change-detection-warning-surface`.
|
||||||
|
- Added compact responsive CSS contracts for Change Detection form and summary grids.
|
||||||
|
- Added `backend/tests/test_sprint87_change_detection_density.py`.
|
||||||
|
- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`.
|
||||||
|
|
||||||
|
Tested:
|
||||||
|
- Live browser pre-check attempt against `http://192.168.10.150:1202` hit a transient browser automation click timeout on the QA/QC workspace nav; source review showed Change Detection was still the older header/form/summary stack.
|
||||||
|
- Red step: `python -m pytest backend/tests/test_sprint87_change_detection_density.py -q` failed on missing Change Detection surface and density CSS contracts.
|
||||||
|
- `python -m pytest backend/tests/test_sprint87_change_detection_density.py backend/tests/test_sprint18_change_detection.py backend/tests/test_sprint39_frontend_orchestration_hooks.py backend/tests/test_sprint86_quality_workspace_density.py backend/tests/test_sprint47_workbench_interaction_smoke.py -q` (`20 passed`)
|
||||||
|
- `cd frontend && npm run typecheck`
|
||||||
|
- `cd frontend && npm run build`
|
||||||
|
- Full readiness and Tower deploy pending in this pass.
|
||||||
|
|
||||||
|
Open:
|
||||||
|
- None known before full readiness/deploy validation.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
- Frontend Change Detection presentation hierarchy only; no change-detection behavior, API contract, persistence, migration, provider fetching or AI/model changes.
|
||||||
|
|
||||||
|
Next recommended pass:
|
||||||
|
- Continue with AI Labs run-form hierarchy and detection/segmentation result density after this pass is deployed and visually checked.
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Add Data workspace selected-summary and panel density polish.
|
- [x] Add Data workspace selected-summary and panel density polish.
|
||||||
- [x] Add Map workspace panel hierarchy and layer-control density polish.
|
- [x] Add Map workspace panel hierarchy and layer-control density polish.
|
||||||
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
- [x] Add QA/QC workspace result hierarchy and filter density polish.
|
||||||
|
- [x] Add Change Detection panel hierarchy and analysis workspace density polish.
|
||||||
|
|
||||||
## Sprint 8 status
|
## Sprint 8 status
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ Map workspace now surfaces the selected AOI, active layer and rendered feature s
|
|||||||
|
|
||||||
QA/QC Results now separates persisted check summary, candidate/reference evidence, refresh/filter controls and result history into focused surfaces. Existing filters, refresh behavior, metric cards and stored result rendering are unchanged, with denser mobile grids for the same controls.
|
QA/QC Results now separates persisted check summary, candidate/reference evidence, refresh/filter controls and result history into focused surfaces. Existing filters, refresh behavior, metric cards and stored result rendering are unchanged, with denser mobile grids for the same controls.
|
||||||
|
|
||||||
|
Change Detection now follows the same analysis workspace hierarchy: vector input controls, error/empty states, result metrics and warnings are separated into focused surfaces while the existing compare action and map overlay result flow remain unchanged.
|
||||||
|
|
||||||
AI Labs uses the same mobile-density baseline for Detection and Segmentation model cards, run forms, QA/result summaries and scroll-safe result tables. This keeps long model ids, tile paths and artifact paths from widening the workbench on phones.
|
AI Labs uses the same mobile-density baseline for Detection and Segmentation model cards, run forms, QA/result summaries and scroll-safe result tables. This keeps long model ids, tile paths and artifact paths from widening the workbench on phones.
|
||||||
|
|
||||||
Exports and System provider capabilities now use compact, mobile-safe cards for handoff actions, artifact filters and provider metadata. Long provider limitations, export ids and artifact paths wrap inside their cards instead of widening the workbench.
|
Exports and System provider capabilities now use compact, mobile-safe cards for handoff actions, artifact filters and provider metadata. Long provider limitations, export ids and artifact paths wrap inside their cards instead of widening the workbench.
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export function ChangeDetectionPanel({
|
|||||||
onRun,
|
onRun,
|
||||||
}: ChangeDetectionPanelProps): JSX.Element {
|
}: ChangeDetectionPanelProps): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<section className="panel">
|
<section className="panel change-detection-shell">
|
||||||
<div className="panel-header">
|
<div className="panel-header change-detection-heading">
|
||||||
<div>
|
<div>
|
||||||
<p className="eyebrow">Analysis</p>
|
<p className="eyebrow">Analysis</p>
|
||||||
<h2>Change Detection</h2>
|
<h2>Change Detection</h2>
|
||||||
@@ -48,6 +48,7 @@ export function ChangeDetectionPanel({
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="change-detection-input-surface" aria-label="Change detection input controls">
|
||||||
<div className="form-grid">
|
<div className="form-grid">
|
||||||
<label>
|
<label>
|
||||||
Source vector
|
Source vector
|
||||||
@@ -91,11 +92,25 @@ export function ChangeDetectionPanel({
|
|||||||
Include unchanged geometry
|
Include unchanged geometry
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{error ? <p className="error">{error}</p> : null}
|
<div className="change-detection-state-stack">
|
||||||
{!error && vectorDatasets.length < 2 ? <p className="muted">Upload at least two vector datasets to compare.</p> : null}
|
{error ? (
|
||||||
|
<div className="result-state result-state-error">
|
||||||
|
<strong>Change detection failed.</strong>
|
||||||
|
<p>{error}</p>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{!error && vectorDatasets.length < 2 ? (
|
||||||
|
<div className="result-state result-state-empty">
|
||||||
|
<strong>Not enough vector datasets</strong>
|
||||||
|
<p>Upload at least two vector datasets to compare.</p>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
{result ? (
|
{result ? (
|
||||||
|
<div className="change-detection-result-surface" aria-label="Change detection result summary">
|
||||||
<div className="summary-grid">
|
<div className="summary-grid">
|
||||||
<div>
|
<div>
|
||||||
<span className="metric">{result.added_count}</span>
|
<span className="metric">{result.added_count}</span>
|
||||||
@@ -114,14 +129,18 @@ export function ChangeDetectionPanel({
|
|||||||
<span>Map features</span>
|
<span>Map features</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{result?.warnings.length ? (
|
{result?.warnings.length ? (
|
||||||
|
<div className="change-detection-warning-surface">
|
||||||
|
<strong>Warnings</strong>
|
||||||
<ul className="compact-list">
|
<ul className="compact-list">
|
||||||
{result.warnings.map((warning) => (
|
{result.warnings.map((warning) => (
|
||||||
<li key={warning}>{warning}</li>
|
<li key={warning}>{warning}</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1465,6 +1465,71 @@ button.entity-card {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.change-detection-shell {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-heading {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-input-surface,
|
||||||
|
.change-detection-result-surface,
|
||||||
|
.change-detection-warning-surface {
|
||||||
|
min-width: 0;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.72rem;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-input-surface {
|
||||||
|
background: linear-gradient(180deg, #ffffff, #f7fbf8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-state-stack {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-shell .form-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||||||
|
gap: 0.62rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-shell .form-grid label {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-shell .form-grid input,
|
||||||
|
.change-detection-shell .form-grid select {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-shell .summary-grid {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
|
||||||
|
gap: 0.55rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-warning-surface {
|
||||||
|
border-color: rgba(180, 83, 9, 0.24);
|
||||||
|
background: rgba(180, 83, 9, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-warning-surface strong {
|
||||||
|
display: block;
|
||||||
|
color: #92400e;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-detection-warning-surface .compact-list {
|
||||||
|
margin-top: 0.45rem;
|
||||||
|
}
|
||||||
|
|
||||||
.quality-results-shell .quality-summary-grid {
|
.quality-results-shell .quality-summary-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
@@ -2798,12 +2863,16 @@ button.entity-card {
|
|||||||
.quality-summary-surface,
|
.quality-summary-surface,
|
||||||
.quality-evidence-surface,
|
.quality-evidence-surface,
|
||||||
.quality-control-surface,
|
.quality-control-surface,
|
||||||
.quality-history-surface {
|
.quality-history-surface,
|
||||||
|
.change-detection-input-surface,
|
||||||
|
.change-detection-result-surface,
|
||||||
|
.change-detection-warning-surface {
|
||||||
padding: 0.58rem;
|
padding: 0.58rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.quality-results-shell .quality-summary-grid,
|
.quality-results-shell .quality-summary-grid,
|
||||||
.quality-results-shell .quality-history-controls {
|
.quality-results-shell .quality-history-controls,
|
||||||
|
.change-detection-shell .form-grid {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -2811,7 +2880,8 @@ button.entity-card {
|
|||||||
.quality-results-shell .quality-handoff-grid,
|
.quality-results-shell .quality-handoff-grid,
|
||||||
.quality-results-shell .quality-score-row,
|
.quality-results-shell .quality-score-row,
|
||||||
.quality-results-shell .quality-metric-grid,
|
.quality-results-shell .quality-metric-grid,
|
||||||
.quality-results-shell .metric-list {
|
.quality-results-shell .metric-list,
|
||||||
|
.change-detection-shell .summary-grid {
|
||||||
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user