Polish export and system mobile layout
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-20 01:38:39 +02:00
parent 1975ee2bb5
commit 82d42c4a81
7 changed files with 232 additions and 15 deletions
+8
View File
@@ -7,6 +7,14 @@
# Changelog
## Sprint 76 Export/System mobile visual polish (2026-06-20)
- Added scan-friendly Provider Capabilities cards with structured status, authority, geometry, query mode and layer chips.
- Tightened mobile export action cards, export history controls and export card headers.
- Added overflow wrapping for long provider limitations, attribution text, export ids and artifact paths.
- Added static regression coverage for Export/System mobile CSS and workflow markup contracts.
- No API contracts, migrations, backend behavior, provider fetching or AI model behavior changed.
## Sprint 75 AI Labs mobile visual polish (2026-06-20)
- Tightened mobile Detection and Segmentation Lab model card, form and result-summary sizing.
@@ -0,0 +1,41 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def test_export_and_system_mobile_density_css_contracts() -> None:
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert ".system-provider-list" in css
assert ".system-provider-card" in css
assert ".system-provider-grid" in css
assert ".system-provider-card div" in css
assert ".provider-layer-list" in css
assert ".provider-layer-chip" in css
assert ".export-action-grid" in css
assert ".handoff-action-card p" in css
assert ".export-history-controls input" in css
assert ".export-history-controls select" in css
assert ".export-card-header" in css
assert "minmax(7.5rem, 1fr)" in css
assert "overflow-wrap: anywhere;" in css
def test_export_and_provider_components_keep_workflow_markup() -> None:
export_center = (ROOT / "frontend" / "src" / "components" / "exports" / "ExportCenter.tsx").read_text(
encoding="utf-8"
)
provider_panel = (ROOT / "frontend" / "src" / "components" / "providers" / "ProviderPanel.tsx").read_text(
encoding="utf-8"
)
assert 'className="export-center"' in export_center
assert "export-action-grid" in export_center
assert "handoff-action-card" in export_center
assert "export-history-controls" in export_center
assert "export-card-header" in export_center
assert "system-provider-list" in provider_panel
assert "system-provider-card" in provider_panel
assert "system-provider-grid" in provider_panel
assert "provider-layer-chip" in provider_panel
+24
View File
@@ -2824,3 +2824,27 @@ Limitations:
Next recommended pass:
- Continue with Export/System mobile-density polish, especially export action cards and provider capability lists.
## Sprint 76 Export/System mobile visual polish (2026-06-20)
Changed:
- Added scan-friendly Provider Capabilities cards with structured provider header, status badge, authority/configuration/geometry/query mode fields and layer chips.
- Added mobile-density CSS for provider cards, export action cards, export history controls and export card headers.
- Added overflow wrapping for long provider limitations, attribution/license notes, export ids and artifact paths.
- Added `backend/tests/test_sprint76_export_system_mobile_polish.py`.
- Updated `frontend/README.md`, `docs/TODO.md` and `CHANGELOG.md`.
Tested:
- Red step: `python -m pytest backend/tests/test_sprint76_export_system_mobile_polish.py -q` failed on missing Export/System mobile CSS and provider markup contracts.
- `python -m pytest backend/tests/test_sprint76_export_system_mobile_polish.py backend/tests/test_sprint75_ai_labs_mobile_polish.py backend/tests/test_sprint74_data_map_mobile_polish.py backend/tests/test_sprint31_unraid_template.py backend/tests/test_sprint64_export_handoff_polish.py backend/tests/test_sprint7b_provider_registry.py backend/tests/test_sprint30_workbench_components.py -q` (`27 passed`)
- `cd frontend && npm run typecheck`
- `cd frontend && npm run build`
Open:
- Run full readiness, redeploy Tower and verify live Export/System mobile polish after deployment.
Limitations:
- Frontend presentation polish only; no export workflow, provider API, persistence, migration, provider fetching or AI/model changes.
Next recommended pass:
- Continue with right-side inspector mobile polish, especially long dataset/vector/raster metadata and action groups.
+1
View File
@@ -65,6 +65,7 @@ This file now starts with the current implementation status. Older preparation/b
- [x] Improve Map workspace layer provenance, empty guidance and selected-feature summary readability.
- [x] Add Data/Map mobile visual polish for upload forms, action grids and map toolbar density.
- [x] Add AI Labs mobile visual polish for model cards, lab forms and result tables.
- [x] Add Export/System mobile visual polish for export actions and provider capability cards.
## Sprint 8 status
+2
View File
@@ -8,6 +8,8 @@ Data and Map workspaces include mobile-density CSS for file inputs, dataset acti
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.
## Scope implemented
- API client layer (`src/services/api`)
- Project and area list/create flows
@@ -14,25 +14,54 @@ export function ProviderPanel({
onRefresh,
}: ProviderPanelProps): JSX.Element {
return (
<section>
<h2>Provider Capabilities</h2>
<button type="button" onClick={onRefresh} disabled={loadingCapabilities}>
Refresh providers
</button>
<section className="system-provider-panel">
<div className="panel-title-row">
<div>
<h2>Provider Capabilities</h2>
<p className="muted">Review configured local providers and honest not-configured external sources.</p>
</div>
<button type="button" className="secondary-action" onClick={onRefresh} disabled={loadingCapabilities}>
Refresh providers
</button>
</div>
{loadingCapabilities ? <p>Loading provider capabilities...</p> : null}
{capabilitiesError ? <p className="error">{capabilitiesError}</p> : null}
{providers.length === 0 && !loadingCapabilities ? <p>No providers reported by backend</p> : null}
<ul>
<ul className="system-provider-list">
{providers.map((provider) => (
<li key={provider.provider_name}>
<strong>{provider.display_name}</strong>
<div>provider: {provider.provider_name}</div>
<div>authority: {provider.authority_level}</div>
<div>status: {provider.status}</div>
<div>configured: {provider.configured ? 'yes' : 'no'}</div>
<div>layers: {provider.supported_layers.join(', ')}</div>
<div>geometry: {provider.supported_geometry_types.join(', ')}</div>
<div>query modes: {provider.supported_query_modes.join(', ')}</div>
<li className="system-provider-card" key={provider.provider_name}>
<div className="system-provider-header">
<div>
<strong>{provider.display_name}</strong>
<span>{provider.provider_name}</span>
</div>
<span className={provider.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{provider.status}</span>
</div>
<div className="system-provider-grid">
<div>
<span>Authority</span>
<strong>{provider.authority_level}</strong>
</div>
<div>
<span>Configured</span>
<strong>{provider.configured ? 'yes' : 'no'}</strong>
</div>
<div>
<span>Geometry</span>
<strong>{provider.supported_geometry_types.join(', ')}</strong>
</div>
<div>
<span>Query modes</span>
<strong>{provider.supported_query_modes.join(', ')}</strong>
</div>
</div>
<div className="provider-layer-list" aria-label={`${provider.provider_name} supported layers`}>
{provider.supported_layers.map((layer) => (
<span className="provider-layer-chip" key={layer}>
{layer}
</span>
))}
</div>
<div>limitation: {provider.limitation_message}</div>
<div>attribution: {provider.attribution}</div>
<div>license: {provider.license_note}</div>
+112
View File
@@ -1493,6 +1493,12 @@ button.entity-card {
text-align: left;
}
.export-history-controls input,
.export-history-controls select {
width: 100%;
min-width: 0;
}
.handoff-summary-card {
display: grid;
gap: 0.72rem;
@@ -1575,6 +1581,7 @@ button.entity-card {
color: var(--muted);
font-size: 0.84rem;
line-height: 1.36;
overflow-wrap: anywhere;
}
.handoff-action-card button {
@@ -1671,6 +1678,95 @@ button.entity-card {
overflow-wrap: anywhere;
}
.system-provider-list {
display: grid;
gap: 0.72rem;
padding: 0;
list-style: none;
}
.system-provider-card {
display: grid;
min-width: 0;
gap: 0.62rem;
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.85rem;
background: var(--panel-soft);
}
.system-provider-card div {
min-width: 0;
overflow-wrap: anywhere;
}
.system-provider-header {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 0.65rem;
align-items: start;
}
.system-provider-header strong,
.system-provider-header span {
display: block;
}
.system-provider-header span:not(.status-badge) {
margin-top: 0.16rem;
color: var(--muted);
font-size: 0.82rem;
}
.system-provider-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
gap: 0.5rem;
}
.system-provider-grid > div {
border: 1px solid var(--line);
border-radius: 7px;
padding: 0.55rem;
background: #ffffff;
}
.system-provider-grid span {
display: block;
color: var(--muted);
font-size: 0.68rem;
font-weight: 800;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.system-provider-grid strong {
display: block;
margin-top: 0.18rem;
font-size: 0.88rem;
line-height: 1.25;
overflow-wrap: anywhere;
}
.provider-layer-list {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.provider-layer-chip {
display: inline-flex;
min-height: 1.45rem;
align-items: center;
border: 1px solid rgba(15, 118, 110, 0.2);
border-radius: 999px;
padding: 0.16rem 0.5rem;
background: var(--accent-soft);
color: var(--accent-strong);
font-size: 0.74rem;
font-weight: 800;
}
.export-preview-panel .job-result {
max-height: 38rem;
}
@@ -2114,6 +2210,22 @@ button.entity-card {
min-width: 36rem;
}
.export-action-grid,
.handoff-action-grid,
.system-provider-grid {
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.handoff-action-card,
.system-provider-card {
padding: 0.68rem;
}
.system-provider-header,
.export-card-header {
grid-template-columns: minmax(0, 1fr);
}
.dataset-action-button,
.map-empty-action-grid button {
min-height: 3rem;