Polish workbench visual shell
This commit is contained in:
@@ -70,7 +70,7 @@ export function DatasetPanel({
|
||||
const readyDatasets = datasets.filter((dataset) => dataset.status === 'ready').length
|
||||
|
||||
return (
|
||||
<section data-testid="dataset-panel">
|
||||
<section className="workspace-panel" data-testid="dataset-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Data catalog</p>
|
||||
@@ -127,7 +127,12 @@ export function DatasetPanel({
|
||||
</form>
|
||||
|
||||
{loadingDatasets ? <p>Loading datasets...</p> : null}
|
||||
{datasets.length === 0 ? <p>No datasets yet</p> : null}
|
||||
{datasets.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<strong>No datasets yet</strong>
|
||||
<p>Upload vector, GeoJSON or raster data after selecting a project.</p>
|
||||
</div>
|
||||
) : null}
|
||||
<ul className="dataset-list">
|
||||
{datasets.map((dataset) => (
|
||||
<li className={dataset.id === selectedDatasetId ? 'dataset-card dataset-card-active' : 'dataset-card'} key={dataset.id}>
|
||||
@@ -144,7 +149,7 @@ export function DatasetPanel({
|
||||
{dataset.status === 'ready' ? 'ready' : dataset.status === 'failed' ? 'failed' : 'pending'}
|
||||
</span>
|
||||
</div>
|
||||
<div className="dataset-metrics">
|
||||
<div className="dataset-metrics data-density-grid">
|
||||
<span>size: {formatBytes(dataset.size_bytes)}</span>
|
||||
<span>features: {dataset.feature_count ?? dataset.vector_summary?.feature_count ?? 'n/a'}</span>
|
||||
<span>bbox: {formatBounds(dataset.bounds_json ?? dataset.vector_summary?.bounds_json)}</span>
|
||||
|
||||
@@ -85,7 +85,7 @@ export function DetectionLab({
|
||||
onRunQa,
|
||||
}: DetectionLabProps): JSX.Element {
|
||||
return (
|
||||
<section>
|
||||
<section className="workspace-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Object detection</p>
|
||||
@@ -166,7 +166,7 @@ export function DetectionLab({
|
||||
</div>
|
||||
{detectionRunError ? <p className="error">{detectionRunError}</p> : null}
|
||||
{detectionRunResult ? (
|
||||
<div>
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {detectionRunResult.status}</p>
|
||||
<p>Message: {detectionRunResult.message}</p>
|
||||
<p>Analysis run: {detectionRunResult.analysis_run_id}</p>
|
||||
@@ -221,26 +221,28 @@ export function DetectionLab({
|
||||
{loadingDetectionResults ? <p>Loading detection results...</p> : null}
|
||||
<p>Detections loaded: {detectionItems.length}</p>
|
||||
{detectionItems.length > 0 ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th>Confidence</th>
|
||||
<th>Model</th>
|
||||
<th>Source tile</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{detectionItems.map((detection) => (
|
||||
<tr key={detection.id}>
|
||||
<td>{detection.class_name}</td>
|
||||
<td>{detection.confidence.toFixed(2)}</td>
|
||||
<td>{detection.model_name}</td>
|
||||
<td>{detection.source_tile_path || 'n/a'}</td>
|
||||
<div className="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th>Confidence</th>
|
||||
<th>Model</th>
|
||||
<th>Source tile</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{detectionItems.map((detection) => (
|
||||
<tr key={detection.id}>
|
||||
<td>{detection.class_name}</td>
|
||||
<td>{detection.confidence.toFixed(2)}</td>
|
||||
<td>{detection.model_name}</td>
|
||||
<td>{detection.source_tile_path || 'n/a'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="lab-block">
|
||||
@@ -261,7 +263,7 @@ export function DetectionLab({
|
||||
</button>
|
||||
{detectionQaError ? <p className="error">{detectionQaError}</p> : null}
|
||||
{detectionQaResult ? (
|
||||
<div>
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {detectionQaResult.status}</p>
|
||||
<p>Quality check: {detectionQaResult.quality_check_id}</p>
|
||||
<p>Precision: {detectionQaResult.precision?.toFixed(3) ?? 'n/a'}</p>
|
||||
|
||||
@@ -27,7 +27,7 @@ export function ProjectPanel({
|
||||
onLoadDemoWorkflow,
|
||||
}: ProjectPanelProps): JSX.Element {
|
||||
return (
|
||||
<section data-testid="project-panel">
|
||||
<section className="workspace-panel" data-testid="project-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Context</p>
|
||||
@@ -89,7 +89,12 @@ export function ProjectPanel({
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{projects.length === 0 ? <li>No projects yet</li> : null}
|
||||
{projects.length === 0 ? (
|
||||
<li className="empty-state">
|
||||
<strong>No projects yet</strong>
|
||||
<p>Create a project or load the offline demo workflow to populate the workbench.</p>
|
||||
</li>
|
||||
) : null}
|
||||
</ul>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ export function SegmentationLab({
|
||||
onRunQa,
|
||||
}: SegmentationLabProps): JSX.Element {
|
||||
return (
|
||||
<section>
|
||||
<section className="workspace-panel">
|
||||
<div className="panel-title-row">
|
||||
<div>
|
||||
<p className="eyebrow">Polygon segmentation</p>
|
||||
@@ -163,7 +163,7 @@ export function SegmentationLab({
|
||||
) : null}
|
||||
{segmentationRunError ? <p className="error">{segmentationRunError}</p> : null}
|
||||
{segmentationRunResult ? (
|
||||
<div>
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {segmentationRunResult.status}</p>
|
||||
<p>Message: {segmentationRunResult.message}</p>
|
||||
<p>Analysis run: {segmentationRunResult.analysis_run_id}</p>
|
||||
@@ -218,30 +218,32 @@ export function SegmentationLab({
|
||||
{loadingSegmentationResults ? <p>Loading segmentation results...</p> : null}
|
||||
<p>Segmentations loaded: {segmentationItems.length}</p>
|
||||
{segmentationItems.length > 0 ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th>Confidence</th>
|
||||
<th>Area m2</th>
|
||||
<th>Model</th>
|
||||
<th>Tile</th>
|
||||
<th>Mask path</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{segmentationItems.map((segmentation) => (
|
||||
<tr key={segmentation.id}>
|
||||
<td>{segmentation.class_name}</td>
|
||||
<td>{segmentation.confidence?.toFixed(2) ?? 'n/a'}</td>
|
||||
<td>{segmentation.area_m2?.toFixed(2) ?? 'n/a'}</td>
|
||||
<td>{segmentation.model_name}</td>
|
||||
<td>{segmentation.source_tile_path || (segmentation.tile_index ?? 'n/a')}</td>
|
||||
<td>{segmentation.mask_path || 'n/a'}</td>
|
||||
<div className="table-scroll">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Class</th>
|
||||
<th>Confidence</th>
|
||||
<th>Area m2</th>
|
||||
<th>Model</th>
|
||||
<th>Tile</th>
|
||||
<th>Mask path</th>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</thead>
|
||||
<tbody>
|
||||
{segmentationItems.map((segmentation) => (
|
||||
<tr key={segmentation.id}>
|
||||
<td>{segmentation.class_name}</td>
|
||||
<td>{segmentation.confidence?.toFixed(2) ?? 'n/a'}</td>
|
||||
<td>{segmentation.area_m2?.toFixed(2) ?? 'n/a'}</td>
|
||||
<td>{segmentation.model_name}</td>
|
||||
<td>{segmentation.source_tile_path || (segmentation.tile_index ?? 'n/a')}</td>
|
||||
<td>{segmentation.mask_path || 'n/a'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="lab-block">
|
||||
@@ -262,7 +264,7 @@ export function SegmentationLab({
|
||||
</button>
|
||||
{segmentationQaError ? <p className="error">{segmentationQaError}</p> : null}
|
||||
{segmentationQaResult ? (
|
||||
<div>
|
||||
<div className="result-summary-card">
|
||||
<p>Status: {segmentationQaResult.status}</p>
|
||||
<p>Quality check: {segmentationQaResult.quality_check_id}</p>
|
||||
<p>Precision: {segmentationQaResult.precision?.toFixed(3) ?? 'n/a'}</p>
|
||||
|
||||
Reference in New Issue
Block a user