import { useEffect, useMemo, useState } from 'react' import { useForm } from 'react-hook-form' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useNavigate } from '@tanstack/react-router' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { cn } from '@/lib/utils' import { useSystemConfig } from '@/hooks/use-system-config' import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card' import { Form } from '@/components/ui/form' import { Skeleton } from '@/components/ui/skeleton' import { ErrorState } from '@/components/error-state' import { LanguageSwitcher } from '@/components/language-switcher' import { LoadingState } from '@/components/loading-state' import { buildSetupPayload, getSetupStatus, submitSetup } from './api' import { AdminStep } from './components/admin-step' import { CompleteStep } from './components/complete-step' import { DatabaseStep } from './components/database-step' import { StepNavigation } from './components/step-navigation' import { UsageModeStep } from './components/usage-mode-step' import type { SetupFormValues, SetupStatus } from './types' const STEPS = [ { titleKey: 'Database check', descriptionKey: 'Verify your database connection', }, { titleKey: 'Administrator account', descriptionKey: 'Create credentials for the root user', }, { titleKey: 'Usage mode', descriptionKey: 'Choose how the platform will operate', }, { titleKey: 'Review & initialize', descriptionKey: 'Confirm settings and finish setup', }, ] const DEFAULT_FORM_VALUES: SetupFormValues = { username: '', password: '', confirmPassword: '', usageMode: 'external', } export function SetupWizard() { const { t } = useTranslation() const navigate = useNavigate() const queryClient = useQueryClient() const { systemName, logo, loading: systemConfigLoading } = useSystemConfig() const [currentStep, setCurrentStep] = useState(0) const [setupStatus, setSetupStatus] = useState() const form = useForm({ defaultValues: DEFAULT_FORM_VALUES, mode: 'onBlur', }) const watchedValues = form.watch() const { data: statusResponse, isLoading, isError, refetch, } = useQuery({ queryKey: ['setup-status'], queryFn: getSetupStatus, retry: false, }) const mutation = useMutation({ mutationKey: ['setup-submit'], mutationFn: submitSetup, onSuccess: async (response) => { if (response.success) { toast.success(t('System initialized successfully! Redirecting…')) await queryClient.invalidateQueries({ queryKey: ['setup-status'] }) setTimeout(() => { navigate({ to: '/' }) }, 1200) } else { toast.error( response.message || t('Initialization failed, please try again.') ) } }, onError: () => { toast.error(t('Failed to initialize system')) }, }) useEffect(() => { if (!statusResponse) return if (!statusResponse.success) { toast.error(statusResponse.message || t('Failed to load setup status')) return } const status = statusResponse.data if (!status) return if (status.status) { navigate({ to: '/' }) return } setSetupStatus(status) setCurrentStep(0) // Pre-fill usage mode if backend echoes it if (status.SelfUseModeEnabled) { form.setValue('usageMode', 'self', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) } else if (status.DemoSiteEnabled) { form.setValue('usageMode', 'demo', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) } else { form.setValue('usageMode', 'external', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [statusResponse, navigate, form]) useEffect(() => { if (!setupStatus) return // Reset admin fields when backend reports they are already initialized if (setupStatus.root_init) { form.setValue('username', '', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) form.setValue('password', '', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) form.setValue('confirmPassword', '', { shouldDirty: false, shouldTouch: false, shouldValidate: false, }) } }, [setupStatus, form]) const currentStepComponent = useMemo(() => { if (currentStep === 0) { return } if (currentStep === 1) { return ( ) } if (currentStep === 2) { return } return }, [currentStep, setupStatus, form, watchedValues]) const validateAdminStep = () => { if (setupStatus?.root_init) return true const username = form.getValues('username')?.trim() const password = form.getValues('password')?.trim() const confirmPassword = form.getValues('confirmPassword')?.trim() if (!username) { form.setError('username', { type: 'manual', message: t('Please enter an administrator username'), }) toast.error(t('Please enter an administrator username')) return false } if (!password || password.length < 8) { form.setError('password', { type: 'manual', message: t('Password must be at least 8 characters long'), }) toast.error(t('Password must be at least 8 characters long')) return false } if (password !== confirmPassword) { form.setError('confirmPassword', { type: 'manual', message: t('Passwords do not match'), }) toast.error(t('Passwords do not match')) return false } return true } const validateUsageModeStep = () => { const usageMode = form.getValues('usageMode') if (!usageMode) { form.setError('usageMode', { type: 'manual', message: t('Select a usage mode to continue'), }) toast.error(t('Select a usage mode to continue')) return false } return true } const handleNextStep = () => { if (currentStep === 1 && !validateAdminStep()) return if (currentStep === 2 && !validateUsageModeStep()) return setCurrentStep((step) => Math.min(step + 1, STEPS.length - 1)) } const handlePreviousStep = () => { setCurrentStep((step) => Math.max(step - 1, 0)) } const handleSubmit = async () => { const adminValid = validateAdminStep() const usageValid = validateUsageModeStep() if (!adminValid || !usageValid) return const payload = buildSetupPayload( form.getValues(), Boolean(setupStatus?.root_init) ) mutation.mutate(payload) } return (
{systemConfigLoading ? ( ) : ( {t('System )}
{systemConfigLoading ? ( ) : (

{t('Initialize')} {systemName}

)}

{t( 'Follow the guided steps to prepare your workspace before the first login.' )}

{t('System setup wizard')} {t('Complete these steps to finish the initial installation.')}
    {STEPS.map((step, index) => { const isActive = currentStep === index const isCompleted = currentStep > index return (
  1. {index + 1}

    {t(step.titleKey)}

    {t(step.descriptionKey)}

  2. ) })}
{isLoading ? ( ) : isError ? ( refetch()} /> ) : (
event.preventDefault()} > {currentStepComponent}
)}
{!isLoading && !isError && ( )}
) }