diff --git a/CHANGELOG.md b/CHANGELOG.md index d02fffe9..9f4fdea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ # 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) - Grouped QA/QC summary, dataset evidence, refresh/filter controls and result history into focused surfaces. diff --git a/backend/tests/test_sprint87_change_detection_density.py b/backend/tests/test_sprint87_change_detection_density.py new file mode 100644 index 00000000..99442e9c --- /dev/null +++ b/backend/tests/test_sprint87_change_detection_density.py @@ -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 diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index bb67dc69..57eafe16 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -3143,3 +3143,31 @@ Limitations: Next recommended pass: - 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. diff --git a/docs/TODO.md b/docs/TODO.md index 454b0d69..7aa6fb1e 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -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 Map workspace panel hierarchy and layer-control 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 diff --git a/frontend/README.md b/frontend/README.md index 340cb786..7ca4a2c2 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -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. +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. 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. diff --git a/frontend/src/components/analysis/ChangeDetectionPanel.tsx b/frontend/src/components/analysis/ChangeDetectionPanel.tsx index c2a9e991..210649a5 100644 --- a/frontend/src/components/analysis/ChangeDetectionPanel.tsx +++ b/frontend/src/components/analysis/ChangeDetectionPanel.tsx @@ -37,8 +37,8 @@ export function ChangeDetectionPanel({ onRun, }: ChangeDetectionPanelProps): JSX.Element { return ( -
-
+
+

Analysis

Change Detection

@@ -48,80 +48,99 @@ export function ChangeDetectionPanel({
-
- - - - +
+
+ + + + +
- {error ?

{error}

: null} - {!error && vectorDatasets.length < 2 ?

Upload at least two vector datasets to compare.

: null} +
+ {error ? ( +
+ Change detection failed. +

{error}

+
+ ) : null} + {!error && vectorDatasets.length < 2 ? ( +
+ Not enough vector datasets +

Upload at least two vector datasets to compare.

+
+ ) : null} +
{result ? ( -
-
- {result.added_count} - Added -
-
- {result.removed_count} - Removed -
-
- {result.unchanged_count} - Unchanged -
-
- {result.geojson.features.length} - Map features +
+
+
+ {result.added_count} + Added +
+
+ {result.removed_count} + Removed +
+
+ {result.unchanged_count} + Unchanged +
+
+ {result.geojson.features.length} + Map features +
) : null} {result?.warnings.length ? ( -
    - {result.warnings.map((warning) => ( -
  • {warning}
  • - ))} -
+
+ Warnings +
    + {result.warnings.map((warning) => ( +
  • {warning}
  • + ))} +
+
) : null}
) diff --git a/frontend/src/styles/app.css b/frontend/src/styles/app.css index 4ad51efb..44874a68 100644 --- a/frontend/src/styles/app.css +++ b/frontend/src/styles/app.css @@ -1465,6 +1465,71 @@ button.entity-card { 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 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -2798,12 +2863,16 @@ button.entity-card { .quality-summary-surface, .quality-evidence-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; } .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)); gap: 0.5rem; } @@ -2811,7 +2880,8 @@ button.entity-card { .quality-results-shell .quality-handoff-grid, .quality-results-shell .quality-score-row, .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)); gap: 0.5rem; }