'use client' import { useCallback, useEffect, useState, type FormEvent } from 'react' import Image from 'next/image' import { useTranslations } from 'next-intl' import { Button } from '@/components/ui/button' import { LanguageSwitcherSelect } from '@/components/ui/language-switcher' type SetupStep = 'form' | 'creating' interface ProgressStep { label: string status: 'pending' | 'active' | 'done' | 'error' } function getInitialProgress(t: (key: string) => string): ProgressStep[] { return [ { label: t('validatingCredentials'), status: 'pending' }, { label: t('creatingAdminAccount'), status: 'pending' }, { label: t('configuringSession'), status: 'pending' }, { label: t('launchingDashboard'), status: 'pending' }, ] } function ProgressIndicator({ steps }: { steps: ProgressStep[] }) { return (
{steps.map((step, i) => (
{step.status === 'done' && ( )} {step.status === 'active' && (
)} {step.status === 'pending' && (
)} {step.status === 'error' && ( )}
{step.label}
))}
) } export default function SetupPage() { const t = useTranslations('auth') const tc = useTranslations('common') const [username, setUsername] = useState('admin') const [password, setPassword] = useState('') const [confirmPassword, setConfirmPassword] = useState('') const [displayName, setDisplayName] = useState('') const [error, setError] = useState('') const [step, setStep] = useState('form') const [progress, setProgress] = useState(() => getInitialProgress(t)) const [checking, setChecking] = useState(true) const [setupAvailable, setSetupAvailable] = useState(false) useEffect(() => { fetch('/api/setup') .then((res) => res.json()) .then((data) => { if (!data.needsSetup) { window.location.href = '/login' return } setSetupAvailable(true) setChecking(false) }) .catch(() => { setError(t('failedToCheckSetup')) setChecking(false) }) }, [t]) const updateProgress = useCallback((index: number, status: ProgressStep['status']) => { setProgress((prev) => prev.map((s, i) => (i === index ? { ...s, status } : s))) }, []) const handleSubmit = useCallback(async (e: FormEvent) => { e.preventDefault() setError('') if (password !== confirmPassword) { setError(t('passwordsDoNotMatch')) return } if (password.length < 12) { setError(t('passwordTooShort')) return } setStep('creating') setProgress(getInitialProgress(t)) // Step 1: Validating updateProgress(0, 'active') await new Promise((r) => setTimeout(r, 400)) updateProgress(0, 'done') // Step 2: Creating account updateProgress(1, 'active') try { const res = await fetch('/api/setup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password, displayName: displayName || undefined, }), }) if (!res.ok) { const data = await res.json().catch(() => null) updateProgress(1, 'error') setError(data?.error || t('setupFailed')) // Allow retry after a brief pause await new Promise((r) => setTimeout(r, 1500)) setStep('form') setProgress(getInitialProgress(t)) return } updateProgress(1, 'done') // Step 3: Configuring session updateProgress(2, 'active') await new Promise((r) => setTimeout(r, 500)) updateProgress(2, 'done') // Step 4: Launching updateProgress(3, 'active') await new Promise((r) => setTimeout(r, 300)) updateProgress(3, 'done') await new Promise((r) => setTimeout(r, 500)) window.location.href = '/' } catch { updateProgress(1, 'error') setError(t('networkError')) await new Promise((r) => setTimeout(r, 1500)) setStep('form') setProgress(getInitialProgress(t)) } }, [username, password, confirmPassword, displayName, updateProgress, t]) if (checking) { return (
{t('checkingSetupStatus')}
) } if (!setupAvailable) { return null } return (
Mission Control logo

{step === 'form' ? t('welcomeToMC') : t('settingUpMC')}

{step === 'form' ? t('createAdminToStart') : t('creatingAdmin')}

{step === 'creating' && (
{error && (
{error}
)}
)} {step === 'form' && ( <> {error && (
{error}
)}
setUsername(e.target.value)} className="w-full h-10 px-3 rounded-lg bg-secondary border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-smooth" placeholder="admin" autoComplete="username" autoFocus required minLength={2} maxLength={64} pattern="[a-z0-9_.\-]+" title={t('usernameHint')} />
setDisplayName(e.target.value)} className="w-full h-10 px-3 rounded-lg bg-secondary border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-smooth" placeholder="Admin" maxLength={100} />
setPassword(e.target.value)} className="w-full h-10 px-3 rounded-lg bg-secondary border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-smooth" placeholder={t('atLeast12Chars')} autoComplete="new-password" required minLength={12} /> {password.length > 0 && password.length < 12 && (

{t('moreCharsNeeded', { count: 12 - password.length })}

)}
setConfirmPassword(e.target.value)} className="w-full h-10 px-3 rounded-lg bg-secondary border border-border text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary transition-smooth" placeholder={t('repeatPassword')} autoComplete="new-password" required minLength={12} /> {confirmPassword.length > 0 && password !== confirmPassword && (

{t('passwordsDoNotMatch')}

)}

{t('firstTimeSetupOnly')}

)}
) }