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
+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;