/** * Custom hook for managing wizard state * * Handles step navigation logic including script mode shortcuts. */ import { useState, useCallback } from 'react'; import type { WizardState, WizardStep } from './types'; import { initialWizardState } from './types'; import { WIZARD_STEPS } from './constants'; interface UseWizardStateReturn { state: WizardState; updateState: (updates: Partial) => void; resetState: () => void; isScriptMode: boolean; canProceed: boolean; goNext: () => void; goBack: () => void; getProjectData: () => ProjectCreateData; } /** * Data structure for project creation API call */ export interface ProjectCreateData { name: string; slug: string; description: string | undefined; autonomy_level: 'full_control' | 'milestone' | 'autonomous'; settings: { complexity: string; client_mode: string; repo_url?: string; }; } /** * Generate a URL-safe slug from a project name */ function generateSlug(name: string): string { return name .toLowerCase() .trim() .replace(/[^\w\s-]/g, '') // Remove special characters .replace(/\s+/g, '-') // Replace spaces with hyphens .replace(/-+/g, '-') // Replace multiple hyphens with single .replace(/^-+|-+$/g, ''); // Remove leading/trailing hyphens } export function useWizardState(): UseWizardStateReturn { const [state, setState] = useState(initialWizardState); const isScriptMode = state.complexity === 'script'; const updateState = useCallback((updates: Partial) => { setState((prev) => ({ ...prev, ...updates })); }, []); const resetState = useCallback(() => { setState(initialWizardState); }, []); /** * Check if user can proceed to next step */ const canProceed = (() => { switch (state.step) { case WIZARD_STEPS.BASIC_INFO: return state.projectName.trim().length >= 3; case WIZARD_STEPS.COMPLEXITY: return state.complexity !== null; case WIZARD_STEPS.CLIENT_MODE: return isScriptMode || state.clientMode !== null; case WIZARD_STEPS.AUTONOMY: return isScriptMode || state.autonomyLevel !== null; case WIZARD_STEPS.AGENT_CHAT: return true; // Agent chat is preview only case WIZARD_STEPS.REVIEW: return true; default: return false; } })(); /** * Navigate to next step, handling script mode skip logic */ const goNext = useCallback(() => { if (!canProceed) return; setState((prev) => { let nextStep = (prev.step + 1) as WizardStep; const currentIsScriptMode = prev.complexity === 'script'; // For scripts, skip from step 2 directly to step 5 (agent chat) if (currentIsScriptMode && prev.step === WIZARD_STEPS.COMPLEXITY) { return { ...prev, step: WIZARD_STEPS.AGENT_CHAT as WizardStep, clientMode: 'auto', autonomyLevel: 'autonomous', }; } // Don't go past review step if (nextStep > WIZARD_STEPS.REVIEW) { nextStep = WIZARD_STEPS.REVIEW as WizardStep; } return { ...prev, step: nextStep }; }); }, [canProceed]); /** * Navigate to previous step, handling script mode skip logic */ const goBack = useCallback(() => { setState((prev) => { if (prev.step <= 1) return prev; let prevStep = (prev.step - 1) as WizardStep; const currentIsScriptMode = prev.complexity === 'script'; // For scripts, go from step 5 back to step 2 if (currentIsScriptMode && prev.step === WIZARD_STEPS.AGENT_CHAT) { prevStep = WIZARD_STEPS.COMPLEXITY as WizardStep; } return { ...prev, step: prevStep }; }); }, []); /** * Get data formatted for the project creation API */ const getProjectData = useCallback((): ProjectCreateData => { const slug = generateSlug(state.projectName); return { name: state.projectName.trim(), slug, description: state.description.trim() || undefined, autonomy_level: state.autonomyLevel || 'milestone', settings: { complexity: state.complexity || 'medium', client_mode: state.clientMode || 'auto', ...(state.repoUrl && { repo_url: state.repoUrl }), }, }; }, [state]); return { state, updateState, resetState, isScriptMode, canProceed, goNext, goBack, getProjectData, }; }