From c299e05e7b3f86f86f27d03ef05bb6935e5e8d01 Mon Sep 17 00:00:00 2001 From: krypticmouse Date: Thu, 26 Mar 2026 18:08:42 +0000 Subject: [PATCH] feat: integrate SetupWizard into desktop boot flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SetupWizard orchestrates the pick→connect→ingest→ready state machine with a step indicator; SetupScreen now transitions to the wizard after all boot checks pass (phase === 'ready') instead of immediately calling onReady(). Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/components/SetupScreen.tsx | 10 +- frontend/src/components/setup/SetupWizard.tsx | 119 ++++++++++++++++++ 2 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 frontend/src/components/setup/SetupWizard.tsx 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' && ( + + )} +
+
+
+ ); +}