import { useState } from 'react' import axios from 'axios' interface Props { wizardBase: string onComplete: () => void } interface SetupForm { admin_username: string admin_password: string admin_email: string admin_full_name: string db_host: string db_port: number db_user: string db_password: string db_database: string backend_base_url: string project_name: string project_description: string } const STEPS = ['Welcome', 'Database', 'Admin', 'Backend', 'Finish'] export default function SetupWizardPage({ wizardBase, onComplete }: Props) { const [step, setStep] = useState(0) const [error, setError] = useState('') const [saving, setSaving] = useState(false) const [wizardOk, setWizardOk] = useState(null) const [form, setForm] = useState({ admin_username: 'admin', admin_password: '', admin_email: '', admin_full_name: 'Admin', db_host: 'mysql', db_port: 3306, db_user: 'harborforge', db_password: 'harborforge_pass', db_database: 'harborforge', backend_base_url: 'http://backend:8000', project_name: '', project_description: '', }) const wizardApi = axios.create({ baseURL: wizardBase, timeout: 5000, }) const set = (key: keyof SetupForm, value: string | number) => setForm((f) => ({ ...f, [key]: value })) const checkWizard = async () => { setError('') try { await wizardApi.get('/health') setWizardOk(true) setStep(1) } catch { setWizardOk(false) setError(`Unable to connect to AbstractWizard (${wizardBase}).\nPlease ensure the SSH tunnel is configured:\nssh -L :127.0.0.1: user@server`) } } const saveConfig = async () => { setError('') setSaving(true) try { const config = { initialized: true, admin: { username: form.admin_username, password: form.admin_password, email: form.admin_email, full_name: form.admin_full_name, }, database: { host: form.db_host, port: form.db_port, user: form.db_user, password: form.db_password, database: form.db_database, }, backend_url: form.backend_base_url || undefined, } await wizardApi.put('/api/v1/config/harborforge.json', config, { headers: { 'Content-Type': 'application/json' }, }) if (form.backend_base_url) { localStorage.setItem('HF_BACKEND_BASE_URL', form.backend_base_url) } setStep(4) } catch (err: any) { setError(`Failed to save configuration: ${err.message}`) } finally { setSaving(false) } } return (

⚓ HarborForge Setup Wizard

{STEPS.map((s, i) => ( {i < step ? '✓' : i + 1}. {s} ))}
{error &&
{error}
} {/* Step 0: Welcome */} {step === 0 && (

Welcome to HarborForge

Agent/Human collaborative task management platform

⚠️ The setup wizard connects to AbstractWizard via SSH tunnel. Ensure the port is forwarded:

ssh -L <wizard_port>:127.0.0.1:<wizard_port> user@your-server
{wizardOk === false && (

Connection failed. Check the SSH tunnel.

)}
)} {/* Step 1: Database */} {step === 1 && (

Database configuration

Configure MySQL connection (docker-compose defaults are fine if using the bundled MySQL).

)} {/* Step 2: Admin */} {step === 2 && (

Admin account

Create the first admin user

)} {/* Step 3: Backend */} {step === 3 && (

Backend URL

Configure the HarborForge backend API URL

)} {/* Step 4: Done */} {step === 4 && (

✅ Setup complete!

Configuration saved to AbstractWizard.

Restart services on the server:

docker compose restart

After the backend starts, refresh this page to go to login.

Admin account: {form.admin_username}

)}
) }