import { useEffect, useState } from 'react'; import { useNavigate, useSearchParams } from 'react-router-dom'; import { Alert } from '@/components/ui/Alert'; import { Button } from '@/components/ui/Button'; import { Card } from '@/components/ui/Card'; import { SectionHeader } from '@/components/ui/SectionHeader'; import { TextField } from '@/components/ui/TextField'; import { ApiError, apiGet, apiPost } from '@/lib/api'; interface InvitationPreview { is_valid: boolean; reason: string | null; email_hint: string | null; expires_at: string; groups: string[]; } export function RegisterPage() { const [params] = useSearchParams(); const token = params.get('token') ?? ''; const navigate = useNavigate(); const [preview, setPreview] = useState(null); const [previewError, setPreviewError] = useState(null); const [email, setEmail] = useState(''); const [displayName, setDisplayName] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [busy, setBusy] = useState(false); const [error, setError] = useState(null); const [done, setDone] = useState(false); useEffect(() => { if (!token) { setPreviewError('Missing invitation token in the URL.'); return; } apiGet(`/invitations/preview/${encodeURIComponent(token)}`, { anonymous: true, }) .then((p) => { setPreview(p); if (p.email_hint) setEmail(p.email_hint); }) .catch((err: unknown) => setPreviewError(err instanceof Error ? err.message : 'Could not load invitation.'), ); }, [token]); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); if (password !== confirm) { setError('Passwords do not match.'); return; } setBusy(true); try { await apiPost( `/invitations/accept/${encodeURIComponent(token)}`, { email, password, display_name: displayName || undefined }, { anonymous: true, noRefresh: true }, ); setDone(true); setTimeout(() => navigate('/login', { replace: true }), 1500); } catch (err) { if (err instanceof ApiError) { const payload = err.payload as { error?: string; message?: string } | null; setError(payload?.message ?? payload?.error ?? `HTTP ${err.status}`); } else { setError(err instanceof Error ? err.message : 'Registration failed.'); } } finally { setBusy(false); } } if (done) { return (
Account created. Redirecting to login…
); } if (previewError) { return (
{previewError}
); } if (!preview) { return (
Loading invitation…
); } if (!preview.is_valid) { return (
This invitation is not usable: {preview.reason}
); } return (
setEmail(e.target.value)} hint={preview.email_hint ? `Invited as ${preview.email_hint}` : undefined} required /> setDisplayName(e.target.value)} /> setPassword(e.target.value)} required /> setConfirm(e.target.value)} required /> {preview.groups.length > 0 && ( You will be added to: {preview.groups.map((g) => `[${g}]`).join(' ')} )} {error && {error}}
); }