Extract demo workflow hook
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 07:52:14 +02:00
parent 0c135b420d
commit 6d655a1f1a
8 changed files with 165 additions and 44 deletions
+1
View File
@@ -215,6 +215,7 @@ React + TypeScript + MapLibre foundation for project/area/dataset workflow.
- Derived MapLibre workbench state lives in `src/hooks/useMapWorkspaceState.ts`.
- Project, area and dataset-list loading lives in `src/hooks/useProjectWorkspace.ts`.
- Area fallback selection now lives with the owning workflow hooks: clip-area selection in `useDatasetWorkflow.ts` and map-area selection in `useMapWorkspaceState.ts`.
- Offline demo workflow orchestration lives in `src/hooks/useDemoWorkflow.ts`, because it coordinates project, dataset, map, QA/QC, detection, segmentation and export state after the backend fixture seed.
## Raster dependency visibility
+23 -39
View File
@@ -1,6 +1,5 @@
import { FormEvent, useEffect, useMemo, useState } from 'react'
import './styles/app.css'
import { demoApi } from './services/api'
import { ChangeDetectionPanel } from './components/analysis/ChangeDetectionPanel'
import { DatasetDetailPanel } from './components/datasets/DatasetDetailPanel'
import { DatasetPanel } from './components/datasets/DatasetPanel'
@@ -16,6 +15,7 @@ import type { DatasetCreateResponse } from './types'
import { ProviderPanel } from './components/providers/ProviderPanel'
import { SegmentationLab } from './components/segmentation/SegmentationLab'
import { useChangeDetectionWorkflow } from './hooks/useChangeDetectionWorkflow'
import { useDemoWorkflow } from './hooks/useDemoWorkflow'
import { useDetectionWorkflow } from './hooks/useDetectionWorkflow'
import { useDatasetWorkflow } from './hooks/useDatasetWorkflow'
import { useExportWorkflow } from './hooks/useExportWorkflow'
@@ -24,16 +24,12 @@ import { useProviderCapabilities } from './hooks/useProviderCapabilities'
import { useProjectWorkspace } from './hooks/useProjectWorkspace'
import { useQualityWorkflow } from './hooks/useQualityWorkflow'
import { useSegmentationWorkflow } from './hooks/useSegmentationWorkflow'
import { formatError } from './lib/formatError'
function isVectorDatasetType(datasetType: string): boolean {
return datasetType === 'vector' || datasetType === 'geojson'
}
function App(): JSX.Element {
const [loadingDemoWorkflow, setLoadingDemoWorkflow] = useState(false)
const [demoWorkflowMessage, setDemoWorkflowMessage] = useState<string | null>(null)
const {
projects,
selectedProject,
@@ -324,6 +320,28 @@ function App(): JSX.Element {
datasetContent,
selectedDataset,
})
const {
loadingDemoWorkflow,
demoWorkflowMessage,
loadDemoWorkflow,
} = useDemoWorkflow({
loadProjects,
loadProjectData,
loadDatasetDetails,
loadDetectionRuns,
loadSegmentationRuns,
loadQualityChecks,
loadExports,
setSelectedProjectId,
setSelectedDatasetId,
setSelectedMapAreaId,
setQaCandidateDatasetId,
setQaReferenceDatasetId,
setQaAreaId,
setDetectionReferenceDatasetId,
setSegmentationReferenceDatasetId,
setErrorMessage,
})
useEffect(() => {
loadProjects().catch(() => null)
@@ -356,40 +374,6 @@ function App(): JSX.Element {
loadSegmentationResults().catch(() => null)
}, [selectedSegmentationRunId, segmentationClassFilter, segmentationMinConfidenceFilter])
const loadDemoWorkflow = async () => {
setLoadingDemoWorkflow(true)
setDemoWorkflowMessage(null)
setErrorMessage(null)
try {
const result = await demoApi.seedWorkflow()
setSelectedProjectId(result.project_id)
setSelectedDatasetId(result.candidate_dataset_id)
setSelectedMapAreaId(result.area_id)
setQaCandidateDatasetId(result.candidate_dataset_id)
setQaReferenceDatasetId(result.reference_dataset_id)
setQaAreaId(result.area_id)
setDetectionReferenceDatasetId(result.reference_dataset_id)
setSegmentationReferenceDatasetId(result.reference_dataset_id)
setDemoWorkflowMessage(result.message)
await loadProjects()
const [projectData] = await Promise.all([
loadProjectData(result.project_id),
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadQualityChecks(result.project_id),
loadExports(result.project_id),
])
const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id)
if (candidateDataset) {
await loadDatasetDetails(result.project_id, candidateDataset)
}
} catch (error) {
setErrorMessage(formatError(error, 'Failed to load demo workflow'))
} finally {
setLoadingDemoWorkflow(false)
}
}
return (
<div className="app-shell">
<header>
+85
View File
@@ -0,0 +1,85 @@
import { useState } from 'react'
import { demoApi } from '../services/api'
import type { DatasetCreateResponse, QualityCheckRead } from '../types'
import { formatError } from '../lib/formatError'
interface DemoWorkflowOptions {
loadProjects: () => Promise<void>
loadProjectData: (projectId: string) => Promise<{ datasets: DatasetCreateResponse[] } | null>
loadDatasetDetails: (projectId: string, dataset: DatasetCreateResponse) => Promise<void>
loadDetectionRuns: (projectId?: string | null) => Promise<void>
loadSegmentationRuns: (projectId?: string | null) => Promise<void>
loadQualityChecks: (projectId?: string | null) => Promise<QualityCheckRead[] | void>
loadExports: (projectId?: string | null) => Promise<void>
setSelectedProjectId: (projectId: string) => void
setSelectedDatasetId: (datasetId: string | null) => void
setSelectedMapAreaId: (areaId: string) => void
setQaCandidateDatasetId: (datasetId: string) => void
setQaReferenceDatasetId: (datasetId: string) => void
setQaAreaId: (areaId: string) => void
setDetectionReferenceDatasetId: (datasetId: string) => void
setSegmentationReferenceDatasetId: (datasetId: string) => void
setErrorMessage: (message: string | null) => void
}
export function useDemoWorkflow({
loadProjects,
loadProjectData,
loadDatasetDetails,
loadDetectionRuns,
loadSegmentationRuns,
loadQualityChecks,
loadExports,
setSelectedProjectId,
setSelectedDatasetId,
setSelectedMapAreaId,
setQaCandidateDatasetId,
setQaReferenceDatasetId,
setQaAreaId,
setDetectionReferenceDatasetId,
setSegmentationReferenceDatasetId,
setErrorMessage,
}: DemoWorkflowOptions) {
const [loadingDemoWorkflow, setLoadingDemoWorkflow] = useState(false)
const [demoWorkflowMessage, setDemoWorkflowMessage] = useState<string | null>(null)
const loadDemoWorkflow = async () => {
setLoadingDemoWorkflow(true)
setDemoWorkflowMessage(null)
setErrorMessage(null)
try {
const result = await demoApi.seedWorkflow()
setSelectedProjectId(result.project_id)
setSelectedDatasetId(result.candidate_dataset_id)
setSelectedMapAreaId(result.area_id)
setQaCandidateDatasetId(result.candidate_dataset_id)
setQaReferenceDatasetId(result.reference_dataset_id)
setQaAreaId(result.area_id)
setDetectionReferenceDatasetId(result.reference_dataset_id)
setSegmentationReferenceDatasetId(result.reference_dataset_id)
setDemoWorkflowMessage(result.message)
await loadProjects()
const [projectData] = await Promise.all([
loadProjectData(result.project_id),
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadQualityChecks(result.project_id),
loadExports(result.project_id),
])
const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id)
if (candidateDataset) {
await loadDatasetDetails(result.project_id, candidateDataset)
}
} catch (error) {
setErrorMessage(formatError(error, 'Failed to load demo workflow'))
} finally {
setLoadingDemoWorkflow(false)
}
}
return {
loadingDemoWorkflow,
demoWorkflowMessage,
loadDemoWorkflow,
}
}