import { useEffect, useState } from "react"; import { Globe, Loader2, Sparkles } from "lucide-react"; import { TurnstileWidget } from "@/client/components/turnstile-widget"; import { Button, Card, ErrorBanner } from "@/client/components/ui"; import { useTurnstileForm } from "@/client/hooks/useTurnstileForm"; import { TEMP_MAILBOX_TTL_HOURS, createInbox, getErrorMessage, isTurnstileError, listDomains, type InboxSession, type TempMailboxTtlHours, } from "../lib/api"; const TTL_OPTION_DETAILS: Record = { 24: { label: "24 hours", hint: "standard" }, 48: { label: "48 hours", hint: "extended" }, 72: { label: "72 hours", hint: "max" }, }; const TTL_OPTIONS = TEMP_MAILBOX_TTL_HOURS.map((value) => ({ ...TTL_OPTION_DETAILS[value], value, })); interface CreateInboxProps { onCreated: (session: InboxSession) => void; } export function CreateInbox({ onCreated }: CreateInboxProps) { const [domains, setDomains] = useState([]); const [selectedDomain, setSelectedDomain] = useState(""); const [ttlHours, setTtlHours] = useState(TEMP_MAILBOX_TTL_HOURS[0]); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const { turnstileToken, setTurnstileToken, turnstileResetKey, handleTurnstileError, resetTurnstile } = useTurnstileForm({ onTurnstileError: () => { setError(null); }, }); useEffect(() => { let active = true; const load = async () => { setLoading(true); setError(null); try { const items = await listDomains(); if (!active) { return; } setDomains(items); setSelectedDomain(items[0] ?? ""); } catch (nextError) { if (active) { setError(getErrorMessage(nextError)); } } finally { if (active) { setLoading(false); } } }; void load(); return () => { active = false; }; }, []); const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); if (!selectedDomain) { setError("No active domains are configured yet."); return; } if (!turnstileToken) { setError("Complete human verification to continue."); return; } setSubmitting(true); setError(null); try { const session = await createInbox(selectedDomain, ttlHours, turnstileToken); resetTurnstile(); onCreated(session); } catch (nextError) { setError(getErrorMessage(nextError)); if (isTurnstileError(nextError)) { resetTurnstile(); } } finally { setSubmitting(false); } }; return ( Instant Address

Spin up a test inbox

Get a real address. Paste it into your app. Watch the mail land the moment it's sent.

Plus aliases (name+tag@domain) all hit the same inbox; each message shows the exact recipient.

Mailbox lifetime
{TTL_OPTIONS.map((option) => { const selected = option.value === ttlHours; return ( ); })}
{loading ? (

Loading available domains...

) : null} {error ? {error} : null} {!loading && domains.length === 0 ? (

No domains available yet. An admin needs to add one first.

) : null}
); }