"use client"; import { useEffect, useMemo, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardShell } from "@/components/templates/DashboardShell"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import SearchableSelect from "@/components/ui/searchable-select"; import { getApiBaseUrl } from "@/lib/api-base"; const apiBase = getApiBaseUrl(); type Board = { id: string; name: string; slug: string; gateway_id?: string | null; }; type Gateway = { id: string; name: string; url: string; main_session_key: string; workspace_root: string; }; const slugify = (value: string) => value .toLowerCase() .trim() .replace(/[^a-z0-9]+/g, "-") .replace(/(^-|-$)/g, "") || "board"; export default function NewBoardPage() { const router = useRouter(); const { getToken, isSignedIn } = useAuth(); const [name, setName] = useState(""); const [gateways, setGateways] = useState([]); const [gatewayId, setGatewayId] = useState(""); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const isFormReady = Boolean(name.trim() && gatewayId); const selectedGateway = useMemo( () => gateways.find((gateway) => gateway.id === gatewayId) || null, [gateways, gatewayId] ); const gatewayOptions = useMemo( () => gateways.map((gateway) => ({ value: gateway.id, label: gateway.name })), [gateways] ); const loadGateways = async () => { if (!isSignedIn) return; try { const token = await getToken(); const response = await fetch(`${apiBase}/api/v1/gateways`, { headers: { Authorization: token ? `Bearer ${token}` : "" }, }); if (!response.ok) { throw new Error("Unable to load gateways."); } const data = (await response.json()) as Gateway[]; setGateways(data); if (!gatewayId && data.length > 0) { setGatewayId(data[0].id); } } catch (err) { setError(err instanceof Error ? err.message : "Something went wrong."); } }; useEffect(() => { loadGateways(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isSignedIn]); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); if (!isSignedIn) return; const trimmedName = name.trim(); if (!trimmedName) { setError("Board name is required."); return; } if (!gatewayId) { setError("Select a gateway before creating a board."); return; } setIsLoading(true); setError(null); try { const token = await getToken(); const payload: Partial = { name: trimmedName, slug: slugify(trimmedName), gateway_id: gatewayId || null, }; const response = await fetch(`${apiBase}/api/v1/boards`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: token ? `Bearer ${token}` : "", }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error("Unable to create board."); } const created = (await response.json()) as Board; router.push(`/boards/${created.id}`); } catch (err) { setError(err instanceof Error ? err.message : "Something went wrong."); } finally { setIsLoading(false); } }; return (

Sign in to create a board.

Create board

Boards organize tasks and agents by mission context.

setName(event.target.value)} placeholder="e.g. Release operations" disabled={isLoading} />
{gateways.length === 0 ? (

No gateways available. Create one in{" "} Gateways {" "} to continue.

) : null} {error ?

{error}

: null}
); }