import { useEffect, useState } from 'react'; import { useNavigate } from 'react-router'; import { WizardShell } from '@/components/setup/wizard-shell'; import { WizardStepIdentity } from '@/components/setup/wizard-step-identity'; import { WizardStepClinics } from '@/components/setup/wizard-step-clinics'; import { WizardStepDoctors } from '@/components/setup/wizard-step-doctors'; import { WizardStepTeam } from '@/components/setup/wizard-step-team'; import { WizardStepTelephony } from '@/components/setup/wizard-step-telephony'; import { WizardStepAi } from '@/components/setup/wizard-step-ai'; import type { WizardStepComponentProps } from '@/components/setup/wizard-step-types'; import { SETUP_STEP_NAMES, type SetupState, type SetupStepName, getSetupState, markSetupStepComplete, dismissSetupWizard, } from '@/lib/setup-state'; import { notify } from '@/lib/toast'; import { useAuth } from '@/providers/auth-provider'; // Top-level onboarding wizard. Auto-shown by login.tsx redirect when the // workspace has incomplete setup steps. // // Phase 5: each step is now backed by a real form component. The parent // owns the shell navigation + per-step completion state, and passes a // WizardStepComponentProps bundle to the dispatched child so the child // can trigger save + mark-complete + advance from its own Save action. const STEP_COMPONENTS: Record React.ReactElement> = { identity: WizardStepIdentity, clinics: WizardStepClinics, doctors: WizardStepDoctors, team: WizardStepTeam, telephony: WizardStepTelephony, ai: WizardStepAi, }; export const SetupWizardPage = () => { const navigate = useNavigate(); const { user } = useAuth(); const [state, setState] = useState(null); const [activeStep, setActiveStep] = useState('identity'); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; getSetupState() .then((s) => { if (cancelled) return; setState(s); // Land on the first incomplete step so the operator picks up // where they left off. const firstIncomplete = SETUP_STEP_NAMES.find((name) => !s.steps[name].completed); if (firstIncomplete) setActiveStep(firstIncomplete); }) .catch((err) => { console.error('Failed to load setup state', err); notify.error('Setup', 'Could not load setup state. Please reload.'); }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); if (loading || !state) { return (

Loading setup…

); } const activeIndex = SETUP_STEP_NAMES.indexOf(activeStep); const isLastStep = activeIndex === SETUP_STEP_NAMES.length - 1; const onPrev = activeIndex > 0 ? () => setActiveStep(SETUP_STEP_NAMES[activeIndex - 1]) : null; const onNext = !isLastStep ? () => setActiveStep(SETUP_STEP_NAMES[activeIndex + 1]) : null; const handleComplete = async (step: SetupStepName) => { // No try/catch here — if the setup-state PUT fails, we WANT // the error to propagate up to the step's handleSave so the // agent sees a toast AND the advance flow pauses until the // issue is resolved. Silent swallowing hid the real failure // mode during the Ramaiah local test. const updated = await markSetupStepComplete(step, user?.email); setState(updated); }; const handleAdvance = () => { if (!isLastStep) { setActiveStep(SETUP_STEP_NAMES[activeIndex + 1]); } }; const handleFinish = () => { notify.success('Setup complete', 'Welcome to your workspace!'); navigate('/', { replace: true }); }; const handleDismiss = async () => { try { await dismissSetupWizard(); notify.success('Setup dismissed', 'You can finish setup later from Settings.'); navigate('/', { replace: true }); } catch { notify.error('Setup', 'Could not dismiss the wizard. Please try again.'); } }; const StepComponent = STEP_COMPONENTS[activeStep]; const stepProps: WizardStepComponentProps = { isCompleted: state.steps[activeStep].completed, isLast: isLastStep, onPrev, onNext, onComplete: handleComplete, onAdvance: handleAdvance, onFinish: handleFinish, }; return ( ); };