'use client'; import { useEffect, useState, useTransition } from 'react'; import { useRouter } from 'next/navigation'; import { createSupabaseBrowserClient } from '@/lib/supabase/browser'; import { validateSubdomain } from '@/lib/validation'; type CheckState = | { kind: 'idle' } | { kind: 'checking' } | { kind: 'ok' } | { kind: 'taken' } | { kind: 'invalid'; message: string }; export function ClaimForm() { const router = useRouter(); const [subdomain, setSubdomain] = useState(''); const [check, setCheck] = useState({ kind: 'idle' }); const [error, setError] = useState(null); const [isPending, startTransition] = useTransition(); useEffect(() => { const v = validateSubdomain(subdomain); if (!subdomain) { setCheck({ kind: 'idle' }); return; } if (!v.ok) { setCheck({ kind: 'invalid', message: v.error }); return; } setCheck({ kind: 'checking' }); const ctrl = new AbortController(); const t = setTimeout(async () => { try { const res = await fetch( `/api/tunnel/check?subdomain=${encodeURIComponent(v.value)}`, { signal: ctrl.signal }, ); const body = (await res.json()) as { available?: boolean }; setCheck(body.available ? { kind: 'ok' } : { kind: 'taken' }); } catch (e) { if ((e as { name?: string }).name !== 'AbortError') { setCheck({ kind: 'idle' }); } } }, 300); return () => { ctrl.abort(); clearTimeout(t); }; }, [subdomain]); async function onSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); const v = validateSubdomain(subdomain); if (!v.ok) { setError(v.error); return; } startTransition(async () => { const supabase = createSupabaseBrowserClient(); const { data: { session }, } = await supabase.auth.getSession(); if (!session) { setError('Session expired. Please sign in again.'); return; } const res = await fetch('/api/tunnel/claim', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${session.access_token}`, }, body: JSON.stringify({ subdomain: v.value }), }); if (!res.ok) { const body = (await res.json().catch(() => ({}))) as { error?: string }; setError(body.error ?? `Request failed (${res.status})`); return; } router.refresh(); }); } const submitDisabled = isPending || check.kind === 'invalid' || check.kind === 'taken' || check.kind === 'checking' || !subdomain; return (
setSubdomain(e.target.value.toLowerCase())} required autoCapitalize="none" autoCorrect="off" spellCheck={false} placeholder="smith" /> .linumiq.net
{check.kind === 'checking' && ( Checking availability… )} {check.kind === 'ok' && Available} {check.kind === 'taken' && Taken} {check.kind === 'invalid' && ( {check.message} )}
{error &&

{error}

}
); }