Polish export and system mobile layout
This commit is contained in:
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user