Polish populated workbench states
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import type { DatasetCreateResponse, ExportCreateResponse, ExportRead } from '../../types'
|
||||
|
||||
interface ExportCenterProps {
|
||||
@@ -34,6 +35,14 @@ function canPreviewJson(item: ExportRead): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
function compactPath(value: string): string {
|
||||
const parts = value.split('/').filter(Boolean)
|
||||
if (parts.length <= 2) {
|
||||
return value
|
||||
}
|
||||
return parts.slice(-2).join('/')
|
||||
}
|
||||
|
||||
export function ExportCenter({
|
||||
selectedProjectId,
|
||||
selectedDataset,
|
||||
@@ -53,9 +62,12 @@ export function ExportCenter({
|
||||
onPreviewContent,
|
||||
onDownload,
|
||||
}: ExportCenterProps): JSX.Element {
|
||||
const [showAllExports, setShowAllExports] = useState(false)
|
||||
const canExportDataset = Boolean(selectedDataset && isVectorDatasetType(selectedDataset.dataset_type))
|
||||
const geojsonExports = exports.filter((item) => item.export_type.includes('geojson')).length
|
||||
const reportExports = exports.filter((item) => item.export_type === 'project_report_html').length
|
||||
const visibleExports = showAllExports ? exports : exports.slice(0, 10)
|
||||
const hiddenExportCount = Math.max(exports.length - visibleExports.length, 0)
|
||||
|
||||
return (
|
||||
<section className="export-center" data-testid="export-center">
|
||||
@@ -126,8 +138,18 @@ export function ExportCenter({
|
||||
<p>Create metadata, GeoJSON or HTML report artifacts once the active project has data to hand off.</p>
|
||||
</div>
|
||||
) : null}
|
||||
{exports.length > 10 ? (
|
||||
<div className="list-limit-banner">
|
||||
<span>
|
||||
Showing {visibleExports.length} latest exports. {hiddenExportCount > 0 ? `${hiddenExportCount} older artifacts hidden.` : 'All artifacts shown.'}
|
||||
</span>
|
||||
<button type="button" className="secondary-action" onClick={() => setShowAllExports((value) => !value)}>
|
||||
{showAllExports ? 'Show latest 10' : 'Show all exports'}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="export-list">
|
||||
{exports.map((item) => (
|
||||
{visibleExports.map((item) => (
|
||||
<li className="export-card" key={item.id}>
|
||||
<div className="export-card-header">
|
||||
<div>
|
||||
@@ -138,7 +160,7 @@ export function ExportCenter({
|
||||
</div>
|
||||
<span className={item.status === 'ready' ? 'status-badge status-badge-ready' : 'status-badge'}>{item.status}</span>
|
||||
</div>
|
||||
<p>{item.storage_path}</p>
|
||||
<p title={item.storage_path}>{compactPath(item.storage_path)}</p>
|
||||
<div className="button-row">
|
||||
{canPreviewJson(item) ? (
|
||||
<button type="button" className="secondary-action" onClick={() => onPreviewContent(item.id)}>
|
||||
|
||||
@@ -2,11 +2,7 @@ interface ExportPreviewProps {
|
||||
content: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export function ExportPreview({ content }: ExportPreviewProps): JSX.Element | null {
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
export function ExportPreview({ content }: ExportPreviewProps): JSX.Element {
|
||||
return (
|
||||
<section className="export-preview-panel">
|
||||
<div className="panel-title-row">
|
||||
@@ -16,7 +12,14 @@ export function ExportPreview({ content }: ExportPreviewProps): JSX.Element | nu
|
||||
</div>
|
||||
<span className="count-pill">preview</span>
|
||||
</div>
|
||||
<pre className="job-result">{JSON.stringify(content, null, 2)}</pre>
|
||||
{content ? (
|
||||
<pre className="job-result">{JSON.stringify(content, null, 2)}</pre>
|
||||
) : (
|
||||
<div className="empty-state">
|
||||
<strong>No export preview selected.</strong>
|
||||
<p>Use Preview JSON content on a JSON or GeoJSON artifact to inspect the stored payload here.</p>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -679,7 +679,7 @@ section li strong + div {
|
||||
}
|
||||
|
||||
.workspace-grid-data {
|
||||
grid-template-columns: minmax(16rem, 0.75fr) minmax(18rem, 0.95fr) minmax(22rem, 1.35fr);
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.workspace-grid-analysis,
|
||||
@@ -701,6 +701,10 @@ section li strong + div {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.workspace-grid-data > section:nth-child(3) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.workspace-grid-ai > section {
|
||||
align-self: stretch;
|
||||
}
|
||||
@@ -943,6 +947,16 @@ button.entity-card {
|
||||
margin-top: 0.7rem;
|
||||
}
|
||||
|
||||
.dataset-card .button-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
|
||||
}
|
||||
|
||||
.dataset-card .button-row button {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -1068,6 +1082,29 @@ button.entity-card {
|
||||
background: var(--accent-soft);
|
||||
}
|
||||
|
||||
.list-limit-banner {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 0.7rem;
|
||||
align-items: center;
|
||||
margin-top: 0.8rem;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 0.62rem 0.72rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.list-limit-banner span {
|
||||
color: var(--muted);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.list-limit-banner button {
|
||||
width: auto;
|
||||
min-height: 2.15rem;
|
||||
}
|
||||
|
||||
.export-list {
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
@@ -1311,7 +1348,8 @@ button.entity-card {
|
||||
.quality-summary-grid,
|
||||
.quality-score-row,
|
||||
.export-action-grid,
|
||||
.inspector-action-bar {
|
||||
.inspector-action-bar,
|
||||
.list-limit-banner {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user