import React, { useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { motion, AnimatePresence } from "framer-motion"; import { Database, Server, User, ChevronRight, CheckCircle2, ArrowRight, ShieldCheck, Mail, Lock, Check } from "lucide-react"; import { cn } from "@/lib/utils"; const stepVariants = { hidden: { opacity: 0, x: 20 }, visible: { opacity: 1, x: 0, transition: { duration: 0.4, ease: "easeOut" } }, exit: { opacity: 0, x: -20, transition: { duration: 0.2, ease: "easeIn" } } }; export default function SetupWizard() { const [step, setStep] = useState(1); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [dbType, setDbType] = useState("sqlite"); const [dbUrl, setDbUrl] = useState(""); const [cacheType, setCacheType] = useState("local"); const [redisUrl, setRedisUrl] = useState(""); const [adminUser, setAdminUser] = useState({ username: "", email: "", password: "", confirmPassword: "" }); const handleNext = () => setStep(step + 1); const handleBack = () => setStep(step - 1); const handleSubmit = async () => { if (adminUser.password !== adminUser.confirmPassword) { setError("Passwords do not match"); return; } if (!adminUser.username || !adminUser.email || !adminUser.password) { setError("Please fill out all admin fields"); return; } setLoading(true); setError(""); try { const res = await fetch("/api/setup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ database: { driver: dbType, url: dbType === "sqlite" ? "file:./data/opencodehub.db" : dbUrl }, cache: { type: cacheType, url: cacheType === "local" ? "" : redisUrl }, admin: { username: adminUser.username, email: adminUser.email, password: adminUser.password } }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "Failed to setup"); window.location.href = "/"; } catch (e: any) { setError(e.message); setLoading(false); } }; const steps = [ { num: 1, title: "Database", icon: Database }, { num: 2, title: "Caching", icon: Server }, { num: 3, title: "Administrator", icon: ShieldCheck } ]; return (
{/* Progress Header */}
{steps.map((s, idx) => { const isActive = step === s.num; const isPast = step > s.num; const Icon = s.icon; return (
{isPast ? : s.num}
{s.title}
); })}
{/* Main Content Area */}
{error && ( {error} )} {step === 1 && (

Configure Database

Select the storage backend for OpenCodeHub.

setDbType("sqlite")} className={cn( "relative p-6 rounded-2xl cursor-pointer transition-all duration-300 border-2 group hover:-translate-y-1", dbType === "sqlite" ? "border-violet-500 bg-violet-500/10" : "border-border bg-secondary hover:border-border/80" )} >
{dbType === "sqlite" &&
}

Local (SQLite)

Zero-configuration standalone setup. Ideal for local dev, single VPS, or NAS deployments.

setDbType("postgres")} className={cn( "relative p-6 rounded-2xl cursor-pointer transition-all duration-300 border-2 group hover:-translate-y-1", dbType === "postgres" ? "border-violet-500 bg-violet-500/10" : "border-border bg-secondary hover:border-border/80" )} >
{dbType === "postgres" &&
}

PostgreSQL

High-performance production database. Required for multi-node setups and large teams.

{dbType === "postgres" && ( setDbUrl(e.target.value)} className="bg-black/50 border-border focus:border-violet-500 focus:ring-violet-500 h-12 text-base" /> )} )} {step === 2 && (

Performance Layer

Configure caching to accelerate repository indexing and API requests.

setCacheType("local")} className={cn( "relative p-6 rounded-2xl cursor-pointer transition-all duration-300 border-2 group hover:-translate-y-1", cacheType === "local" ? "border-emerald-500 bg-emerald-500/10" : "border-border bg-secondary hover:border-border/80" )} >
{cacheType === "local" &&
}

Local Memory

Fast on-device caching. Recommended for single-instance deployments.

setCacheType("redis")} className={cn( "relative p-6 rounded-2xl cursor-pointer transition-all duration-300 border-2 group hover:-translate-y-1", cacheType === "redis" ? "border-emerald-500 bg-emerald-500/10" : "border-border bg-secondary hover:border-border/80" )} >
{cacheType === "redis" &&
}

External Redis

Distributed caching. Essential for High Availability and horizontal scaling.

{cacheType === "redis" && ( setRedisUrl(e.target.value)} className="bg-black/50 border-border focus:border-emerald-500 focus:ring-emerald-500 h-12 text-base" /> )} )} {step === 3 && (

Create Administrator

Set up the initial master account for platform administration.

setAdminUser({...adminUser, username: e.target.value})} className="pl-10 bg-black/50 border-border focus:border-blue-500 h-11" placeholder="admin" />
setAdminUser({...adminUser, email: e.target.value})} className="pl-10 bg-black/50 border-border focus:border-blue-500 h-11" placeholder="admin@example.com" />
setAdminUser({...adminUser, password: e.target.value})} className="pl-10 bg-black/50 border-border focus:border-blue-500 h-11" placeholder="••••••••" />
setAdminUser({...adminUser, confirmPassword: e.target.value})} className="pl-10 bg-black/50 border-border focus:border-blue-500 h-11" placeholder="••••••••" />
)}
{/* Footer Navigation */}
{step > 1 ? ( ) : (
// Spacer )} {step < 3 ? ( ) : ( )}
); }