Polish workbench workspace usability
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-17 20:57:43 +02:00
parent 76f9b49538
commit a3df753732
14 changed files with 751 additions and 296 deletions
@@ -86,60 +86,81 @@ export function DetectionLab({
}: DetectionLabProps): JSX.Element {
return (
<section>
<h2>Detection Lab</h2>
<button type="button" onClick={onLoadModels} disabled={loadingDetectionModels}>
Refresh detection models
</button>
<div className="panel-title-row">
<div>
<p className="eyebrow">Object detection</p>
<h2>Detection Lab</h2>
</div>
<button className="secondary-action" type="button" onClick={onLoadModels} disabled={loadingDetectionModels}>
Refresh models
</button>
</div>
{loadingDetectionModels ? <p>Loading detection models...</p> : null}
{detectionModelError ? <p className="error">{detectionModelError}</p> : null}
{detectionModels.length === 0 && !loadingDetectionModels ? <p>No detection models reported by backend</p> : null}
<ul>
<ul className="model-list">
{detectionModels.map((model) => (
<li key={model.model_id}>
<li className={model.configured ? 'model-card model-card-ready' : 'model-card'} key={model.model_id}>
<strong>{model.display_name}</strong>
<div>model: {model.model_id}</div>
<div>framework: {model.framework}</div>
<div>task: {model.task_type}</div>
<div>status: {model.status}</div>
<div>configured: {model.configured ? 'yes' : 'no'}</div>
<div>classes: {model.supported_classes.join(', ')}</div>
<div>limitation: {model.limitation_message}</div>
<span className={model.configured ? 'status-badge status-badge-ready' : 'status-badge'}>{model.status}</span>
<div className="entity-meta">
<span>{model.model_id}</span>
<span>{model.framework}</span>
<span>{model.task_type}</span>
</div>
<p className="muted">classes: {model.supported_classes.join(', ')}</p>
<p className="muted">{model.limitation_message}</p>
</li>
))}
</ul>
<div>
<select value={selectedDetectionDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
<option value="">Select raster dataset</option>
{rasterDatasets.map((dataset) => (
<option key={dataset.id} value={dataset.id}>
{dataset.name}
</option>
))}
</select>
<select value={selectedDetectionModelId} onChange={(event) => onSelectModel(event.target.value)}>
{detectionModels.map((model) => (
<option key={model.model_id} value={model.model_id}>
{model.display_name}
</option>
))}
</select>
<input
type="number"
min="0"
max="1"
step="0.05"
value={detectionConfidenceThreshold}
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
/>
<div className="lab-block">
<h3>Run detection</h3>
<div className="lab-form-grid">
<label>
Raster dataset
<select value={selectedDetectionDatasetId} onChange={(event) => onSelectDataset(event.target.value)}>
<option value="">Select raster dataset</option>
{rasterDatasets.map((dataset) => (
<option key={dataset.id} value={dataset.id}>
{dataset.name}
</option>
))}
</select>
</label>
<label>
Model
<select value={selectedDetectionModelId} onChange={(event) => onSelectModel(event.target.value)}>
{detectionModels.map((model) => (
<option key={model.model_id} value={model.model_id}>
{model.display_name}
</option>
))}
</select>
</label>
<label>
Min confidence
<input
type="number"
min="0"
max="1"
step="0.05"
value={detectionConfidenceThreshold}
onChange={(event) => onSetConfidenceThreshold(Number(event.target.value))}
/>
</label>
</div>
{selectedDetectionModelId === 'yolo-configured' ? (
<input
type="text"
placeholder="Raster tile manifest path"
value={detectionTileManifestPath}
onChange={(event) => onSetTileManifestPath(event.target.value)}
/>
<label>
Tile manifest
<input
type="text"
placeholder="Raster tile manifest path"
value={detectionTileManifestPath}
onChange={(event) => onSetTileManifestPath(event.target.value)}
/>
</label>
) : null}
<button type="button" onClick={onRunDetection} disabled={runningDetection || !selectedProjectId || rasterDatasets.length === 0}>
<button className="primary-action" type="button" onClick={onRunDetection} disabled={runningDetection || !selectedProjectId || rasterDatasets.length === 0}>
Run detection
</button>
</div>
@@ -154,34 +175,47 @@ export function DetectionLab({
{detectionRunResult.error_code ? <p className="error">Code: {detectionRunResult.error_code}</p> : null}
</div>
) : null}
<div>
<h3>Detection results</h3>
<button type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
Refresh detection runs
</button>
<select value={selectedDetectionRunId} onChange={(event) => onSelectRun(event.target.value)}>
<option value="">Select detection run</option>
{detectionRuns.map((run) => (
<option key={run.id} value={run.id}>
{run.model_name || 'detection'} - {run.status} - {run.id}
</option>
))}
</select>
<input
type="text"
placeholder="Class filter"
value={detectionClassFilter}
onChange={(event) => onSetClassFilter(event.target.value)}
/>
<input
type="number"
min="0"
max="1"
step="0.05"
value={detectionMinConfidenceFilter}
onChange={(event) => onSetMinConfidenceFilter(Number(event.target.value))}
/>
<button type="button" onClick={onLoadResults} disabled={!selectedDetectionRunId || loadingDetectionResults}>
<div className="lab-block">
<div className="panel-title-row">
<h3>Detection results</h3>
<button className="secondary-action" type="button" onClick={onLoadRuns} disabled={!selectedProjectId}>
Refresh runs
</button>
</div>
<div className="lab-form-grid">
<label>
Run
<select value={selectedDetectionRunId} onChange={(event) => onSelectRun(event.target.value)}>
<option value="">Select detection run</option>
{detectionRuns.map((run) => (
<option key={run.id} value={run.id}>
{run.model_name || 'detection'} - {run.status} - {run.id}
</option>
))}
</select>
</label>
<label>
Class
<input
type="text"
placeholder="Class filter"
value={detectionClassFilter}
onChange={(event) => onSetClassFilter(event.target.value)}
/>
</label>
<label>
Min confidence
<input
type="number"
min="0"
max="1"
step="0.05"
value={detectionMinConfidenceFilter}
onChange={(event) => onSetMinConfidenceFilter(Number(event.target.value))}
/>
</label>
</div>
<button className="primary-action" type="button" onClick={onLoadResults} disabled={!selectedDetectionRunId || loadingDetectionResults}>
Load detections
</button>
{loadingDetectionResults ? <p>Loading detection results...</p> : null}
@@ -209,17 +243,20 @@ export function DetectionLab({
</table>
) : null}
</div>
<div>
<div className="lab-block">
<h3>Detection QA</h3>
<select value={detectionReferenceDatasetId} onChange={(event) => onSelectReferenceDataset(event.target.value)}>
<option value="">Select reference dataset</option>
{referenceDatasets.map((dataset) => (
<option key={dataset.id} value={dataset.id}>
{dataset.name}
</option>
))}
</select>
<button type="button" onClick={onRunQa} disabled={runningDetectionQa || !selectedDetectionRunId || !detectionReferenceDatasetId}>
<label>
Reference dataset
<select value={detectionReferenceDatasetId} onChange={(event) => onSelectReferenceDataset(event.target.value)}>
<option value="">Select reference dataset</option>
{referenceDatasets.map((dataset) => (
<option key={dataset.id} value={dataset.id}>
{dataset.name}
</option>
))}
</select>
</label>
<button className="primary-action" type="button" onClick={onRunQa} disabled={runningDetectionQa || !selectedDetectionRunId || !detectionReferenceDatasetId}>
Compare detections to reference
</button>
{detectionQaError ? <p className="error">{detectionQaError}</p> : null}