import { useState } from 'react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { ArrowRight, Loader2 } from 'lucide-react'; import { AuthCanvas } from '@/components/auth/AuthCanvas'; import { AuthStepHeader } from '@/components/auth/AuthStepHeader'; import { ErrorRail } from '@/components/auth/ErrorRail'; import { EnvironmentChecks } from '@/components/settings/EnvironmentChecks'; interface SetupProps { onComplete: () => void; } const INPUT_CLASS = 'h-11 bg-background/60 border-card-border font-sans text-base shadow-[inset_0_2px_4px_0_oklch(0_0_0/0.25)] placeholder:text-stat-subtitle/60 focus-visible:border-brand/60 focus-visible:ring-2 focus-visible:ring-brand/40 focus-visible:ring-offset-0'; type Strength = { label: string; tone: 'weak' | 'fair' | 'strong' } | null; function gaugePassword(pw: string): Strength { if (pw.length === 0) return null; if (pw.length < 8) return { label: 'Weak', tone: 'weak' }; const classes = Number(/[a-z]/.test(pw)) + Number(/[A-Z]/.test(pw)) + Number(/\d/.test(pw)) + Number(/[^A-Za-z0-9]/.test(pw)); if (pw.length >= 12 && classes >= 3) return { label: 'Strong', tone: 'strong' }; return { label: 'Fair', tone: 'fair' }; } export function Setup({ onComplete, className, ...props }: SetupProps & React.ComponentPropsWithoutRef<'div'>) { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); // The admin account is created in step 1; /api/auth/setup signs the operator // in (session cookie), so step 2 can run the admin-gated environment checks // before handing off to the console. const [step, setStep] = useState<'account' | 'env'>('account'); const strength = gaugePassword(password); const strengthClass = strength?.tone === 'strong' ? 'text-success' : strength?.tone === 'fair' ? 'text-warning' : strength ? 'text-destructive' : ''; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(''); if (password !== confirmPassword) { setError('Passwords do not match'); return; } if (username.length < 3) { setError('Username must be at least 3 characters'); return; } if (password.length < 8) { setError('Password must be at least 8 characters'); return; } setIsLoading(true); try { const response = await fetch('/api/auth/setup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ username, password, confirmPassword }), }); const data = await response.json(); if (response.ok && data.success) { setStep('env'); } else { setError(data.error || 'Setup failed'); } } catch { setError('Network error. Please try again.'); } finally { setIsLoading(false); } }; if (step === 'env') { return (
Console · First boot Account ready
} >
); } return (
Console · First boot Empty database
} >
setUsername(e.target.value)} className={INPUT_CLASS} />
{strength && ( {strength.label} )}
setPassword(e.target.value)} className={INPUT_CLASS} />
setConfirmPassword(e.target.value)} className={INPUT_CLASS} /> {error && {error}}
); } function Field({ id, label, children }: { id: string; label: string; children: React.ReactNode }) { return (
{children}
); }