Expand scoped demo analysis access
GeoIntel release gates / Compile, test, contracts and builds (push) Canceled after 0s
GeoIntel release gates / Python and npm vulnerability policy (push) Canceled after 0s
GeoIntel release gates / GIS image, SBOM and container scan (push) Canceled after 0s

This commit is contained in:
Jens
2026-08-01 16:00:13 +02:00
parent 96db4c966d
commit dfcc11b0d5
19 changed files with 206 additions and 112 deletions
+38 -25
View File
@@ -90,9 +90,21 @@ const workspaceNavGroups: WorkspaceNavigationGroup[] = [
{ label: 'Beheer', keys: ['overview', 'system'] },
]
const guestWorkspaceKeys = new Set<WorkspaceKey>(['map', 'analysis'])
const guestWorkspaceKeys = new Set<WorkspaceKey>([
'overview',
'data',
'map',
'assistant',
'analysis',
'ai',
'exports',
])
const guestWorkspaceGroups: WorkspaceNavigationGroup[] = [
{ label: 'Demowerkruimte', keys: ['map', 'analysis'] },
{ label: 'Verkennen', keys: ['map', 'data'] },
{ label: 'Vragen', keys: ['assistant'] },
{ label: 'Analyseren', keys: ['analysis', 'ai'] },
{ label: 'Afronden', keys: ['exports'] },
{ label: 'Demo', keys: ['overview'] },
]
interface WorkbenchAppProps {
@@ -589,7 +601,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
demoWorkflowMessage,
loadDemoWorkflow,
} = useDemoWorkflow({
restrictedMode: isGuest,
restrictedMode: false,
loadProjects,
loadProjectData,
loadDatasetDetails,
@@ -618,7 +630,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
}, [isGuest, loadDemoWorkflow])
useWorkbenchBootstrap({
restrictedMode: isGuest,
restrictedMode: false,
selectedProjectId,
selectedDetectionRunId,
detectionClassFilter,
@@ -720,7 +732,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
}
const openWorkflowGuidanceStep = (target: WorkspaceKey) => {
if (isGuest && !guestWorkspaceKeys.has(target)) {
setActiveWorkspace(target === 'exports' ? 'analysis' : 'map')
setActiveWorkspace('map')
return
}
if (target === 'map' && availableMapDatasets.length > 0 && !mapFeatureCollection) {
@@ -894,9 +906,9 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
<ShieldCheck aria-hidden="true" />
<div>
<strong>Tijdelijke demowerkruimte</strong>
<span>U verkent vooraf geladen voorbeelddata. Wijzigingen, nieuwe analyses en operatorfuncties zijn uitgeschakeld.</span>
<span>Alle analysemodellen en werkfuncties zijn beschikbaar. Beheer, instellingen en blijvende gegevenswijzigingen blijven afgeschermd.</span>
</div>
<span className="guest-mode-badge">Alleen-lezen</span>
<span className="guest-mode-badge">Analyse-toegang</span>
</div>
) : null}
@@ -924,7 +936,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
<WorkspaceSignal workspace={activeWorkspace} />
<div className="workspace-heading-actions">
<p>{activeWorkspaceItem.description}</p>
{!isGuest && activeWorkspace !== 'overview' ? (
{activeWorkspace !== 'overview' ? (
<button
type="button"
className={inspectorOpen ? 'inspector-toggle inspector-toggle-active' : 'inspector-toggle'}
@@ -960,6 +972,15 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
projectId={selectedProjectId ?? undefined}
loading={loadingDatasets}
/>
{isGuest ? (
<div className="guest-readonly-card">
<ShieldCheck aria-hidden="true" />
<div>
<strong>Bronnen bekijken zonder beheerrechten</strong>
<span>De demo gebruikt dezelfde beschikbare databronnen. Projecten, gebieden en uploads worden alleen door de operator beheerd.</span>
</div>
</div>
) : <>
<ProjectPanel
projects={projects}
selectedProjectId={selectedProjectId}
@@ -1002,12 +1023,13 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
onOpenDatasetInMap={openDatasetInMap}
onOpenDatasetExport={openDatasetExport}
/>
</>}
</div>
) : null}
<div className="workspace-persistent-map" hidden={activeWorkspace !== 'map'}>
<MapWorkspace
readOnly={isGuest}
readOnly={false}
selectedProjectId={selectedProjectId}
projects={projects}
areas={areas}
@@ -1103,8 +1125,8 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
onRefreshProjectData={() => (
selectedProjectId ? loadProjectData(selectedProjectId) : Promise.resolve(null)
)}
onOpenAssistant={() => setActiveWorkspace(isGuest ? 'analysis' : 'assistant')}
onOpenExports={() => setActiveWorkspace(isGuest ? 'analysis' : 'exports')}
onOpenAssistant={() => setActiveWorkspace('assistant')}
onOpenExports={() => setActiveWorkspace('exports')}
/>
</div>
@@ -1121,10 +1143,9 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
evidenceLoading={qualityEvidenceLoading}
evidenceError={qualityEvidenceError}
onOpenMapWorkspace={() => setActiveWorkspace('map')}
onOpenAnalysisWorkspace={() => setActiveWorkspace(isGuest ? 'map' : 'ai')}
onOpenAnalysisWorkspace={() => setActiveWorkspace('ai')}
/>
{!isGuest ? (
<details className="secondary-analysis-disclosure">
<details className="secondary-analysis-disclosure">
<summary>
<span>Historische vectorlagen vergelijken</span>
<strong>Geavanceerd</strong>
@@ -1144,16 +1165,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
onIncludeUnchangedChange={setChangeIncludeUnchanged}
onRun={runChangeDetection}
/>
</details>
) : (
<div className="guest-readonly-card">
<ShieldCheck aria-hidden="true" />
<div>
<strong>Controleerbaar voorbeeldresultaat</strong>
<span>Deze kwaliteitsweergave gebruikt de vooraf berekende demo. Nieuwe vergelijkingen zijn alleen beschikbaar voor operators.</span>
</div>
</div>
)}
</details>
</div>
) : null}
@@ -1171,6 +1183,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
{activeWorkspace === 'ai' ? (
<div className="workspace-grid workspace-grid-ai">
<DetectionLab
managementLocked={isGuest}
detectionModels={detectionModels}
modelAssets={modelAssets}
loadingDetectionModels={loadingDetectionModels}
@@ -1318,7 +1331,7 @@ function WorkbenchApp({ username, accessMode, loggingOut, onLogout }: WorkbenchA
) : null}
</main>
{inspectorOpen && !isGuest ? (
{inspectorOpen ? (
<aside className="workbench-inspector" id="workbench-inspector" aria-label="Details van de huidige selectie">
<WorkbenchInspector
selectedProject={selectedProject}
@@ -78,6 +78,7 @@ interface CalibrationRow {
}
interface DetectionLabProps {
managementLocked?: boolean
detectionModels: DetectionModelCapability[]
modelAssets: ModelAssetRead[]
loadingDetectionModels: boolean
@@ -138,6 +139,7 @@ interface DetectionLabProps {
}
export function DetectionLab({
managementLocked = false,
detectionModels,
modelAssets,
loadingDetectionModels,
@@ -332,7 +334,7 @@ export function DetectionLab({
</div>
) : null}
<DetectionModelManagement
{!managementLocked ? <DetectionModelManagement
detectionModels={detectionModels}
modelAssets={modelAssets}
loadingDetectionModels={loadingDetectionModels}
@@ -347,7 +349,7 @@ export function DetectionLab({
onRefreshYoloPreflight={onRefreshYoloPreflight}
onSelectModelAsset={onSelectModelAsset}
onApplyOperatorProfile={onApplyOperatorProfile}
/>
/> : null}
<div className="lab-block">
<div className="ai-lab-run-surface" aria-label="Gebouwdetectie starten">
@@ -414,7 +416,7 @@ export function DetectionLab({
<p>Voeg hieronder een gegeorefereerde GeoTIFF toe. GeoIntel controleert de projectie en bewaart het bronbestand als dataset.</p>
</div>
) : null}
<div className="guided-raster-input" aria-label="Luchtbeeld toevoegen">
{!managementLocked ? <div className="guided-raster-input" aria-label="Luchtbeeld toevoegen">
<div>
<strong>Eigen luchtbeeld toevoegen</strong>
<p>Gebruik een GeoTIFF met geldige CRS en georeferentie. Een bestaand luchtbeeld kan meteen in de keuzelijst worden gebruikt.</p>
@@ -444,7 +446,7 @@ export function DetectionLab({
>
{detectionWorkflowStage === 'uploading' ? 'Luchtbeeld toevoegen...' : 'Luchtbeeld toevoegen'}
</button>
</div>
</div> : null}
<div className="lab-form-grid">
<label>
Luchtbeeld
@@ -499,7 +501,7 @@ export function DetectionLab({
{detectionWorkflowActionLabel(detectionWorkflowStage)}
</button>
<details className="ai-lab-model-surface technical-manifest-surface" aria-label="Technische tegelinstellingen">
{!managementLocked ? <details className="ai-lab-model-surface technical-manifest-surface" aria-label="Technische tegelinstellingen">
<summary>
<span>Technische tegelinstellingen</span>
<strong>{detectionHasTileManifest ? 'manifest beschikbaar' : 'automatisch'}</strong>
@@ -528,7 +530,7 @@ export function DetectionLab({
Bestaande beeldtegels analyseren
</button>
</div>
</details>
</details> : null}
</div>
</div>
@@ -556,7 +558,7 @@ export function DetectionLab({
) : null}
</div>
<details className="ai-lab-model-surface guided-calibration-surface" aria-label="Modelkalibratie voor beheerders">
{!managementLocked ? <details className="ai-lab-model-surface guided-calibration-surface" aria-label="Modelkalibratie voor beheerders">
<summary>
<span>Modelkalibratie voor beheerders</span>
<strong>{detectionCalibrationRows.length > 0 ? `${detectionCalibrationRows.length} drempels getest` : 'gesloten'}</strong>
@@ -677,7 +679,7 @@ export function DetectionLab({
</div>
)}
</div>
</details>
</details> : null}
<div className="ai-lab-results-surface" aria-label="Resultaten van de beeldanalyse">
<div className="panel-title-row">
+6 -8
View File
@@ -67,17 +67,15 @@ export function useDemoWorkflow({
setSegmentationReferenceDatasetId(result.reference_dataset_id)
setDemoWorkflowMessage(result.message)
await loadProjects(result.project_id)
const operatorOnlyLoads = restrictedMode
? Promise.resolve()
: Promise.all([
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadExports(result.project_id),
]).then(() => undefined)
const analysisLoads = Promise.all([
loadDetectionRuns(result.project_id),
loadSegmentationRuns(result.project_id),
loadExports(result.project_id),
]).then(() => undefined)
const [projectData] = await Promise.all([
loadProjectData(result.project_id),
loadQualityChecks(result.project_id),
operatorOnlyLoads,
analysisLoads,
])
const candidateDataset = projectData?.datasets.find((dataset) => dataset.id === result.candidate_dataset_id)
const rasterDataset = projectData?.datasets.find((dataset) => dataset.id === result.raster_dataset_id)
+3 -2
View File
@@ -207,6 +207,7 @@ export function useDetectionWorkflow({
setDetectionRunError(null)
try {
const params = {
project_id: selectedProjectId ?? '',
class_name: detectionClassFilter || null,
min_confidence: detectionMinConfidenceFilter > 0 ? detectionMinConfidenceFilter : null,
}
@@ -415,7 +416,7 @@ export function useDetectionWorkflow({
setDetectionQaResult(null)
setRunningDetectionQa(true)
try {
const result = await detectionApi.compareWithReference(analysisRunId, {
const result = await detectionApi.compareWithReference(analysisRunId, selectedProjectId!, {
reference_dataset_id: referenceDatasetId,
iou_threshold: iouThresholdOverride ?? qaIouThreshold,
class_name: useCurrentFilters ? detectionClassFilter || null : null,
@@ -486,7 +487,7 @@ export function useDetectionWorkflow({
parameters_json: { calibration: true, calibration_thresholds: thresholds },
})
setSelectedDetectionRunId(result.analysis_run_id)
const qa = await detectionApi.compareWithReference(result.analysis_run_id, {
const qa = await detectionApi.compareWithReference(result.analysis_run_id, selectedProjectId, {
reference_dataset_id: detectionReferenceDatasetId,
iou_threshold: qaIouThreshold,
class_name: detectionClassFilter || null,
+10 -8
View File
@@ -59,7 +59,7 @@ export function useExportWorkflow({
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedDataset.project_id, {
dataset_id: selectedDataset.id,
export_kind: 'dataset',
name: selectedDataset.name.replace(/\.(geo)?json$/i, ''),
@@ -74,14 +74,14 @@ export function useExportWorkflow({
}
const exportSelectedDetectionRunGeoJson = async () => {
if (!selectedDetectionRunId) {
if (!selectedDetectionRunId || !selectedProjectId) {
setExportError('Select a detection run before exporting GeoJSON.')
return
}
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedProjectId, {
analysis_run_id: selectedDetectionRunId,
export_kind: 'detection_run',
})
@@ -95,14 +95,14 @@ export function useExportWorkflow({
}
const exportSelectedSegmentationRunGeoJson = async () => {
if (!selectedSegmentationRunId) {
if (!selectedSegmentationRunId || !selectedProjectId) {
setExportError('Select a segmentation run before exporting GeoJSON.')
return
}
setExporting(true)
setExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedProjectId, {
analysis_run_id: selectedSegmentationRunId,
export_kind: 'segmentation_run',
})
@@ -123,7 +123,7 @@ export function useExportWorkflow({
setSelectionExporting(true)
setSelectionExportError(null)
try {
const response = await exportsApi.exportGeojson({
const response = await exportsApi.exportGeojson(selectedDataset.project_id, {
dataset_id: selectedDataset.id,
area_id: areaId,
export_kind: 'vector_selection',
@@ -199,7 +199,8 @@ export function useExportWorkflow({
const previewExportContent = async (exportId: string) => {
setExportError(null)
try {
const response = await exportsApi.getContent(exportId)
if (!selectedProjectId) return
const response = await exportsApi.getContent(selectedProjectId, exportId)
setExportPreview(response.content)
} catch (error) {
setExportError(formatError(error, 'Failed to load export content'))
@@ -207,7 +208,8 @@ export function useExportWorkflow({
}
const downloadExportArtifact = (exportId: string) => {
window.open(exportsApi.downloadUrl(exportId), '_blank', 'noopener,noreferrer')
if (!selectedProjectId) return
window.open(exportsApi.downloadUrl(selectedProjectId, exportId), '_blank', 'noopener,noreferrer')
}
const resetExportsForProject = () => {
+1 -1
View File
@@ -51,7 +51,7 @@ export function useMapSelectionQa({
iou_threshold: 0.5,
area_id: candidateDataset.area_id ?? null,
}
const job: JobRead = await qaApi.runQa(request)
const job: JobRead = await qaApi.runQa(selectedProjectId, request)
if (job.status === 'failed') {
setMapSelectionQaError(job.error_message || 'Map selection QA/QC failed')
return null
+1 -1
View File
@@ -92,7 +92,7 @@ export function useQualityWorkflow({ selectedProjectId, loadProjectData }: Quali
iou_threshold: qaIouThreshold,
area_id: qaAreaId || null,
}
const job: JobRead = await qaApi.runQa(request)
const job: JobRead = await qaApi.runQa(selectedProjectId, request)
if (job.status === 'failed') {
setQaError(job.error_message || 'QA comparison failed')
return
@@ -104,6 +104,7 @@ export function useSegmentationWorkflow({
setSegmentationRunError(null)
try {
const params = {
project_id: selectedProjectId ?? '',
class_name: segmentationClassFilter || null,
min_confidence: segmentationMinConfidenceFilter > 0 ? segmentationMinConfidenceFilter : null,
}
@@ -175,7 +176,7 @@ export function useSegmentationWorkflow({
setSegmentationQaResult(null)
setRunningSegmentationQa(true)
try {
const result = await segmentationApi.compareWithReference(selectedSegmentationRunId, {
const result = await segmentationApi.compareWithReference(selectedSegmentationRunId, selectedProjectId!, {
reference_dataset_id: segmentationReferenceDatasetId,
iou_threshold: qaIouThreshold,
class_name: segmentationClassFilter || null,
@@ -65,19 +65,19 @@ describe('useWorkbenchBootstrap', () => {
expect(state.loadExports).toHaveBeenCalledWith('project-1')
})
it('keeps the guest bootstrap inside the read-only demo surface', async () => {
it('loads the same analysis catalog and project results for a guest', async () => {
const state = { ...options('project-1'), restrictedMode: true }
renderHook(() => useWorkbenchBootstrap(state))
await waitFor(() => expect(state.loadProjectData).toHaveBeenCalledWith('project-1'))
expect(state.loadCapabilities).toHaveBeenCalledOnce()
expect(state.loadQualityChecks).toHaveBeenCalledWith('project-1')
expect(state.loadDetectionModels).not.toHaveBeenCalled()
expect(state.loadSegmentationModels).not.toHaveBeenCalled()
expect(state.loadDetectionRuns).not.toHaveBeenCalled()
expect(state.loadSegmentationRuns).not.toHaveBeenCalled()
expect(state.loadExports).not.toHaveBeenCalled()
expect(state.loadDetectionResults).not.toHaveBeenCalled()
expect(state.loadSegmentationResults).not.toHaveBeenCalled()
expect(state.loadDetectionModels).toHaveBeenCalledOnce()
expect(state.loadSegmentationModels).toHaveBeenCalledOnce()
expect(state.loadDetectionRuns).toHaveBeenCalledWith('project-1')
expect(state.loadSegmentationRuns).toHaveBeenCalledWith('project-1')
expect(state.loadExports).toHaveBeenCalledWith('project-1')
expect(state.loadDetectionResults).toHaveBeenCalledOnce()
expect(state.loadSegmentationResults).toHaveBeenCalledOnce()
})
})
+5 -11
View File
@@ -59,10 +59,8 @@ export function useWorkbenchBootstrap({
useEffect(() => {
loadProjects().catch(() => null)
loadCapabilities().catch(() => null)
if (!restrictedMode) {
loadDetectionModels().catch(() => null)
loadSegmentationModels().catch(() => null)
}
loadDetectionModels().catch(() => null)
loadSegmentationModels().catch(() => null)
}, [restrictedMode])
useEffect(() => {
@@ -81,20 +79,16 @@ export function useWorkbenchBootstrap({
resetExportsForProject()
loadProjectData(selectedProjectId).catch(() => null)
loadQualityChecks(selectedProjectId).catch(() => null)
if (!restrictedMode) {
loadDetectionRuns(selectedProjectId).catch(() => null)
loadSegmentationRuns(selectedProjectId).catch(() => null)
loadExports(selectedProjectId).catch(() => null)
}
loadDetectionRuns(selectedProjectId).catch(() => null)
loadSegmentationRuns(selectedProjectId).catch(() => null)
loadExports(selectedProjectId).catch(() => null)
}, [restrictedMode, selectedProjectId])
useEffect(() => {
if (restrictedMode) return
loadDetectionResults().catch(() => null)
}, [restrictedMode, selectedDetectionRunId, detectionClassFilter, detectionMinConfidenceFilter])
useEffect(() => {
if (restrictedMode) return
loadSegmentationResults().catch(() => null)
}, [restrictedMode, selectedSegmentationRunId, segmentationClassFilter, segmentationMinConfidenceFilter])
}
+5 -5
View File
@@ -29,21 +29,21 @@ export const detectionApi = {
getYoloPreflight: (params: { tile_manifest_path?: string | null; check_model_load?: boolean | null; model_asset_id?: string | null } = {}): Promise<YoloPreflightResponse> =>
apiGet<YoloPreflightResponse>(`/api/v1/detection/yolo/preflight${queryString(params)}`),
run: (payload: DetectionRunRequest): Promise<DetectionRunResponse> =>
apiPost<DetectionRunResponse>('/api/v1/detection/run', payload),
apiPost<DetectionRunResponse>(`/api/v1/detection/run?project_id=${encodeURIComponent(payload.project_id)}`, payload),
listRuns: (params: { project_id?: string | null; dataset_id?: string | null } = {}): Promise<DetectionRunListResponse> =>
apiGet<DetectionRunListResponse>(`/api/v1/detection/runs${queryString(params)}`),
getRun: (analysisRunId: string): Promise<DetectionRunRead> =>
apiGet<DetectionRunRead>(`/api/v1/detection/runs/${analysisRunId}`),
listDetections: (
analysisRunId: string,
params: { dataset_id?: string | null; class_name?: string | null; min_confidence?: number | null } = {},
params: { project_id: string; dataset_id?: string | null; class_name?: string | null; min_confidence?: number | null },
): Promise<DetectionListResponse> =>
apiGet<DetectionListResponse>(`/api/v1/detection/runs/${analysisRunId}/detections${queryString(params)}`),
getRunGeoJson: (
analysisRunId: string,
params: { class_name?: string | null; min_confidence?: number | null } = {},
params: { project_id: string; class_name?: string | null; min_confidence?: number | null },
): Promise<GeoJSON.FeatureCollection> =>
apiGet<GeoJSON.FeatureCollection>(`/api/v1/detection/runs/${analysisRunId}/geojson${queryString(params)}`),
compareWithReference: (analysisRunId: string, payload: DetectionQaRequest): Promise<DetectionQaResult> =>
apiPost<DetectionQaResult>(`/api/v1/detection/runs/${analysisRunId}/qa/reference`, payload),
compareWithReference: (analysisRunId: string, projectId: string, payload: DetectionQaRequest): Promise<DetectionQaResult> =>
apiPost<DetectionQaResult>(`/api/v1/detection/runs/${analysisRunId}/qa/reference?project_id=${encodeURIComponent(projectId)}`, payload),
}
+12 -9
View File
@@ -11,6 +11,7 @@ import type {
export const exportsApi = {
exportGeojson: (
projectId: string,
payload:
| {
dataset_id?: string
@@ -24,18 +25,20 @@ export const exportsApi = {
| string,
): Promise<ExportCreateResponse> => {
const body = typeof payload === 'string' ? { dataset_id: payload, export_kind: 'dataset' } : payload
return apiPost<ExportCreateResponse>(`/api/v1/exports/geojson`, body)
return apiPost<ExportCreateResponse>(`/api/v1/exports/geojson?project_id=${encodeURIComponent(projectId)}`, body)
},
exportProjectMetadata: (projectId: string, name?: string): Promise<ExportCreateResponse> =>
apiPost<ExportCreateResponse>(`/api/v1/exports/metadata`, { project_id: projectId, name }),
apiPost<ExportCreateResponse>(`/api/v1/exports/metadata?project_id=${encodeURIComponent(projectId)}`, { project_id: projectId, name }),
exportProjectReport: (projectId: string, name?: string): Promise<ExportCreateResponse> =>
apiPost<ExportCreateResponse>(`/api/v1/exports/report`, { project_id: projectId, name }),
apiPost<ExportCreateResponse>(`/api/v1/exports/report?project_id=${encodeURIComponent(projectId)}`, { project_id: projectId, name }),
exportMapResult: (payload: MapResultExportRequest): Promise<ExportCreateResponse> =>
apiPost<ExportCreateResponse>(`/api/v1/exports/map-result`, payload),
apiPost<ExportCreateResponse>(`/api/v1/exports/map-result?project_id=${encodeURIComponent(payload.project_id)}`, payload),
listProjectExports: (projectId: string): Promise<ExportListResponse> =>
apiGet<ExportListResponse>(`/api/v1/exports/projects/${projectId}/exports`),
getExport: (exportId: string): Promise<ExportRead> => apiGet<ExportRead>(`/api/v1/exports/${exportId}`),
getContent: (exportId: string): Promise<ExportContentResponse> =>
apiGet<ExportContentResponse>(`/api/v1/exports/${exportId}/content`),
downloadUrl: (exportId: string): string => apiUrl(`/api/v1/exports/${exportId}/download`),
apiGet<ExportListResponse>(`/api/v1/exports/projects/${projectId}/exports?project_id=${encodeURIComponent(projectId)}`),
getExport: (projectId: string, exportId: string): Promise<ExportRead> =>
apiGet<ExportRead>(`/api/v1/exports/${exportId}?project_id=${encodeURIComponent(projectId)}`),
getContent: (projectId: string, exportId: string): Promise<ExportContentResponse> =>
apiGet<ExportContentResponse>(`/api/v1/exports/${exportId}/content?project_id=${encodeURIComponent(projectId)}`),
downloadUrl: (projectId: string, exportId: string): string =>
apiUrl(`/api/v1/exports/${exportId}/download?project_id=${encodeURIComponent(projectId)}`),
}
+2 -2
View File
@@ -10,8 +10,8 @@ import type {
} from '../../types'
export const qaApi = {
runQa: (payload: QaComparisonRequest): Promise<JobRead> =>
apiPost<JobRead>('/api/v1/qa/detections-vs-reference', payload),
runQa: (projectId: string, payload: QaComparisonRequest): Promise<JobRead> =>
apiPost<JobRead>(`/api/v1/qa/detections-vs-reference?project_id=${encodeURIComponent(projectId)}`, payload),
listQualityChecks: (projectId: string): Promise<QualityCheckListResponse> =>
apiGet<QualityCheckListResponse>(`/api/v1/projects/${projectId}/quality-checks`),
getQualityEvidenceGeoJson: (projectId: string, qualityCheckId: string): Promise<QualityEvidenceGeoJsonResponse> =>
+5 -5
View File
@@ -24,21 +24,21 @@ function queryString(params: Record<string, string | number | null | undefined>)
export const segmentationApi = {
listModels: (): Promise<SegmentationModelsResponse> => apiGet<SegmentationModelsResponse>('/api/v1/segmentation/models'),
run: (payload: SegmentationRunRequest): Promise<SegmentationRunResponse> =>
apiPost<SegmentationRunResponse>('/api/v1/segmentation/run', payload),
apiPost<SegmentationRunResponse>(`/api/v1/segmentation/run?project_id=${encodeURIComponent(payload.project_id)}`, payload),
listRuns: (params: { project_id?: string | null; dataset_id?: string | null } = {}): Promise<SegmentationRunListResponse> =>
apiGet<SegmentationRunListResponse>(`/api/v1/segmentation/runs${queryString(params)}`),
getRun: (analysisRunId: string): Promise<SegmentationRunRead> =>
apiGet<SegmentationRunRead>(`/api/v1/segmentation/runs/${analysisRunId}`),
listSegmentations: (
analysisRunId: string,
params: { dataset_id?: string | null; class_name?: string | null; min_confidence?: number | null } = {},
params: { project_id: string; dataset_id?: string | null; class_name?: string | null; min_confidence?: number | null },
): Promise<SegmentationListResponse> =>
apiGet<SegmentationListResponse>(`/api/v1/segmentation/runs/${analysisRunId}/segmentations${queryString(params)}`),
getRunGeoJson: (
analysisRunId: string,
params: { class_name?: string | null; min_confidence?: number | null } = {},
params: { project_id: string; class_name?: string | null; min_confidence?: number | null },
): Promise<GeoJSON.FeatureCollection> =>
apiGet<GeoJSON.FeatureCollection>(`/api/v1/segmentation/runs/${analysisRunId}/geojson${queryString(params)}`),
compareWithReference: (analysisRunId: string, payload: SegmentationQaRequest): Promise<SegmentationQaResult> =>
apiPost<SegmentationQaResult>(`/api/v1/segmentation/runs/${analysisRunId}/qa/reference`, payload),
compareWithReference: (analysisRunId: string, projectId: string, payload: SegmentationQaRequest): Promise<SegmentationQaResult> =>
apiPost<SegmentationQaResult>(`/api/v1/segmentation/runs/${analysisRunId}/qa/reference?project_id=${encodeURIComponent(projectId)}`, payload),
}