import React, { useState } from 'react' import { useNavigate } from 'react-router-dom' import { Server, Globe, ShieldCheck, CheckCircle, ChevronRight, ChevronLeft } from 'lucide-react' import { api } from '../api/client' import { useAuth } from '../contexts/AuthContext' const STEPS = [ { id: 1, label: 'Server', icon: Server }, { id: 2, label: 'Nameservers', icon: Globe }, { id: 3, label: 'Admin', icon: ShieldCheck }, { id: 4, label: 'Finish', icon: CheckCircle }, ] interface FormData { hostname: string nameserver1: string nameserver2: string adminEmail: string adminPassword: string confirmPassword: string } export default function Setup() { const { user, markSetupComplete } = useAuth() const navigate = useNavigate() const [step, setStep] = useState(1) const [error, setError] = useState('') const [saving, setSaving] = useState(false) const [form, setForm] = useState({ hostname: '', nameserver1: '', nameserver2: '', adminEmail: user?.email ?? '', adminPassword: '', confirmPassword: '', }) const set = (key: keyof FormData) => (e: React.ChangeEvent) => setForm(prev => ({ ...prev, [key]: e.target.value })) // ── Validation per step ────────────────────────────────────────────────────── function validate(): string { if (step === 1) { if (!form.hostname.trim()) return 'Hostname is required' if (!/^[a-zA-Z0-9.-]+$/.test(form.hostname)) return 'Invalid hostname format' } if (step === 2) { if (!form.nameserver1.trim()) return 'Primary nameserver is required' if (!form.nameserver2.trim()) return 'Secondary nameserver is required' } if (step === 3) { if (!form.adminEmail.trim()) return 'Email is required' if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.adminEmail)) return 'Invalid email address' if (form.adminPassword && form.adminPassword.length < 8) return 'Password must be at least 8 characters' if (form.adminPassword !== form.confirmPassword) return 'Passwords do not match' } return '' } function next() { const err = validate() if (err) { setError(err); return } setError('') setStep(s => s + 1) } function back() { setError('') setStep(s => s - 1) } async function finish() { setSaving(true) setError('') try { await api.post('/nixserver/setup/complete', { hostname: form.hostname, nameserver1: form.nameserver1, nameserver2: form.nameserver2, adminEmail: form.adminEmail, adminPassword: form.adminPassword || undefined, confirmPassword: form.confirmPassword || undefined, }) markSetupComplete() navigate('/', { replace: true }) } catch (err: unknown) { const msg = (err as { response?: { data?: { error?: string } } })?.response?.data?.error setError(msg ?? 'Failed to save setup — please try again') } finally { setSaving(false) } } // ── Render ─────────────────────────────────────────────────────────────────── return (
{/* Header */}

NixServer Setup

Configure your server before getting started

{/* Step indicators */}
{STEPS.map((s, i) => { const Icon = s.icon const active = step === s.id const complete = step > s.id return (
{s.label}
{i < STEPS.length - 1 && (
s.id ? 'bg-indigo-600' : 'bg-[#2a2d3e]'}`} /> )} ) })}
{/* Card */}
{/* Step 1 — Server Identity */} {step === 1 && <>

Server Identity

Set the fully-qualified hostname for this server.

Example: server.yourdomain.com

} {/* Step 2 — Nameservers */} {step === 2 && <>

Nameservers

These nameservers will be used for hosted domains.

} {/* Step 3 — Admin Account */} {step === 3 && <>

Admin Account

Update your admin email and optionally set a new password.

} {/* Step 4 — Review */} {step === 4 && <>

Review & Finish

Confirm your settings before applying them.

} {error && (
{error}
)} {/* Navigation */}
{step < 4 ? ( ) : ( )}

NixServer v0.3.2 · You can revisit these settings anytime under Settings

) } // ── Small helpers ────────────────────────────────────────────────────────────── function Field({ label, value, onChange, placeholder, type = 'text', autoFocus, }: { label: string value: string onChange: (e: React.ChangeEvent) => void placeholder?: string type?: string autoFocus?: boolean }) { return (
) } function ReviewRow({ label, value }: { label: string; value: string }) { return (
{label} {value}
) }