diff --git a/frontend/src/components/SetupScreen.tsx b/frontend/src/components/SetupScreen.tsx index 62449da6..05c2663a 100644 --- a/frontend/src/components/SetupScreen.tsx +++ b/frontend/src/components/SetupScreen.tsx @@ -1,6 +1,7 @@ import { useState, useEffect, useCallback } from 'react'; import { Loader2, CheckCircle2, XCircle, Cpu, Server, Database } from 'lucide-react'; import { getSetupStatus, type SetupStatus } from '../lib/api'; +import { SetupWizard } from './setup/SetupWizard'; const STEPS = [ { key: 'ollama_ready', label: 'Inference Engine', icon: Cpu, detail: 'Starting Ollama...' }, @@ -70,14 +71,15 @@ function StepRow({ export function SetupScreen({ onReady }: { onReady: () => void }) { const [status, setStatus] = useState(null); + const [showWizard, setShowWizard] = useState(false); const poll = useCallback(async () => { const s = await getSetupStatus(); if (s) setStatus(s); if (s?.phase === 'ready') { - setTimeout(onReady, 600); + setTimeout(() => setShowWizard(true), 600); } - }, [onReady]); + }, []); useEffect(() => { poll(); @@ -85,6 +87,10 @@ export function SetupScreen({ onReady }: { onReady: () => void }) { return () => clearInterval(interval); }, [poll]); + if (showWizard) { + return ; + } + const activeStep: StepKey | null = status && !status.ollama_ready ? 'ollama_ready' diff --git a/frontend/src/components/setup/SetupWizard.tsx b/frontend/src/components/setup/SetupWizard.tsx new file mode 100644 index 00000000..0ec116c6 --- /dev/null +++ b/frontend/src/components/setup/SetupWizard.tsx @@ -0,0 +1,119 @@ +import { useState } from 'react'; +import { SourcePicker } from './SourcePicker'; +import { SourceConnectFlow } from './SourceConnectFlow'; +import { IngestDashboard } from './IngestDashboard'; +import { ReadyScreen } from './ReadyScreen'; +import type { WizardStep } from '../../types/connectors'; + +// --------------------------------------------------------------------------- +// SetupWizard +// --------------------------------------------------------------------------- + +export function SetupWizard({ onComplete }: { onComplete: (firstQuery?: string) => void }) { + const [step, setStep] = useState('pick'); + const [selectedIds, setSelectedIds] = useState([]); + const [connectedIds, setConnectedIds] = useState([]); + + const handlePick = (ids: string[]) => { + setSelectedIds(ids); + setStep('connect'); + }; + + const handleConnectComplete = () => { + // connectedIds: those actually in selectedIds that were not skipped. + // We don't track skip state here — IngestDashboard receives all selected + // (skipped ones will just fail gracefully on sync status fetch). + setConnectedIds(selectedIds); + setStep('ingest'); + }; + + const handleIngestReady = () => { + setStep('ready'); + }; + + const handleStart = (query?: string) => { + onComplete(query); + }; + + return ( +
+
+ {/* Step indicator */} +
+ {(['pick', 'connect', 'ingest', 'ready'] as WizardStep[]).map((s, i) => ( +
+
+ {i + 1} +
+ {i < 3 && ( +
+ )} +
+ ))} +
+ + {/* Step content */} +
+ {step === 'pick' && } + {step === 'connect' && ( + + )} + {step === 'ingest' && ( + + )} + {step === 'ready' && ( + + )} +
+
+
+ ); +}