Add AI lab run readiness panels
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-24 23:47:55 +02:00
parent 299f29c94e
commit 98fff63a97
7 changed files with 222 additions and 0 deletions
@@ -84,6 +84,16 @@ export function DetectionLab({
onSelectReferenceDataset,
onRunQa,
}: DetectionLabProps): JSX.Element {
const selectedDetectionModel = detectionModels.find((model) => model.model_id === selectedDetectionModelId) ?? null
const detectionRequiresTileManifest = selectedDetectionModelId === 'yolo-configured'
const detectionHasDataset = selectedDetectionDatasetId.length > 0
const detectionHasModel = selectedDetectionModel !== null
const detectionModelReady = Boolean(selectedDetectionModel?.configured)
const detectionHasTileManifest =
!detectionRequiresTileManifest || detectionTileManifestPath.trim().length > 0
const detectionRunReady =
Boolean(selectedProjectId) && detectionHasDataset && detectionHasModel && detectionModelReady && detectionHasTileManifest
return (
<section className="workspace-panel ai-lab-shell detection-lab-shell">
<div className="panel-title-row">
@@ -143,6 +153,44 @@ export function DetectionLab({
<div className="lab-block">
<div className="ai-lab-run-surface" aria-label="Detection run controls">
<h3>Run detection</h3>
<div
className={detectionRunReady ? 'lab-readiness-panel lab-readiness-panel-ready' : 'lab-readiness-panel'}
aria-label="Detection run readiness"
>
<div className="ai-lab-section-header">
<div>
<h3>Run readiness</h3>
<p>Checks the selected dataset, model and tile manifest before submitting a detection job.</p>
</div>
<span className={detectionRunReady ? 'status-badge status-badge-ready' : 'status-badge'}>
{detectionRunReady ? 'Ready to submit' : 'Blocked'}
</span>
</div>
<div className="lab-readiness-grid">
<div className={detectionHasDataset ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Raster dataset</span>
<strong>{detectionHasDataset ? 'Selected' : 'Select a raster dataset'}</strong>
</div>
<div className={detectionModelReady ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Model availability</span>
<strong>
{detectionModelReady
? `${selectedDetectionModel?.display_name ?? selectedDetectionModelId} is configured`
: selectedDetectionModel?.limitation_message ?? 'Select a configured model'}
</strong>
</div>
<div className={detectionHasTileManifest ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Tile manifest</span>
<strong>
{detectionRequiresTileManifest
? detectionHasTileManifest
? 'Provided for configured YOLO'
: 'Required for configured YOLO'
: 'Not required for this model'}
</strong>
</div>
</div>
</div>
{rasterDatasets.length === 0 ? (
<div className="result-state result-state-empty">
<strong>No raster datasets available for detection.</strong>
@@ -88,6 +88,11 @@ export function SegmentationLab({
onSelectReferenceDataset,
onRunQa,
}: SegmentationLabProps): JSX.Element {
const segmentationHasDataset = selectedSegmentationDatasetId.length > 0
const segmentationHasTileManifest = segmentationTileManifestPath.trim().length > 0
const segmentationRunReady =
Boolean(selectedProjectId) && segmentationHasDataset && selectedSegmentationModelConfigured
return (
<section className="workspace-panel ai-lab-shell segmentation-lab-shell">
<div className="panel-title-row">
@@ -147,6 +152,38 @@ export function SegmentationLab({
<div className="lab-block">
<div className="ai-lab-run-surface" aria-label="Segmentation run controls">
<h3>Run segmentation</h3>
<div
className={segmentationRunReady ? 'lab-readiness-panel lab-readiness-panel-ready' : 'lab-readiness-panel'}
aria-label="Segmentation run readiness"
>
<div className="ai-lab-section-header">
<div>
<h3>Run readiness</h3>
<p>Checks the selected raster and segmenter state before submitting a segmentation job.</p>
</div>
<span className={segmentationRunReady ? 'status-badge status-badge-ready' : 'status-badge'}>
{segmentationRunReady ? 'Ready to submit' : 'Blocked'}
</span>
</div>
<div className="lab-readiness-grid">
<div className={segmentationHasDataset ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Raster dataset</span>
<strong>{segmentationHasDataset ? 'Selected' : 'Select a raster dataset'}</strong>
</div>
<div className={selectedSegmentationModelConfigured ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Model availability</span>
<strong>
{selectedSegmentationModelConfigured
? 'Selected model is configured'
: selectedSegmentationModelLimitation ?? 'Select a configured segmentation model'}
</strong>
</div>
<div className={segmentationHasTileManifest ? 'lab-readiness-item lab-readiness-item-ready' : 'lab-readiness-item'}>
<span>Tile manifest</span>
<strong>{segmentationHasTileManifest ? 'Provided for provenance' : 'Optional for the fixture segmenter'}</strong>
</div>
</div>
</div>
{rasterDatasets.length === 0 ? (
<div className="result-state result-state-empty">
<strong>No raster datasets available for segmentation.</strong>
+52
View File
@@ -2667,6 +2667,58 @@ button.entity-card {
min-width: 0;
}
.lab-readiness-panel {
display: grid;
gap: 0.6rem;
min-width: 0;
border-left: 4px solid #c9d8d1;
border-radius: 8px;
padding: 0.64rem 0.68rem;
background: #f8fbf9;
}
.lab-readiness-panel-ready {
border-left-color: #2f7d56;
background: #f5fbf6;
}
.lab-readiness-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
gap: 0.5rem;
min-width: 0;
}
.lab-readiness-item {
display: grid;
gap: 0.18rem;
min-width: 0;
border: 1px solid #d8e3de;
border-radius: 7px;
padding: 0.52rem 0.58rem;
background: #ffffff;
}
.lab-readiness-item-ready {
border-color: #b8dcc9;
background: #fbfffc;
}
.lab-readiness-item span {
color: var(--muted);
font-size: 0.72rem;
font-weight: 700;
text-transform: uppercase;
}
.lab-readiness-item strong {
min-width: 0;
color: var(--text);
font-size: 0.88rem;
line-height: 1.28;
overflow-wrap: anywhere;
}
.ai-lab-shell .model-list {
grid-template-columns: repeat(auto-fit, minmax(12.5rem, 1fr));
gap: 0.55rem;