import { useState, useEffect, useCallback } from 'react'; import { Copy, Check, Plus, RefreshCw } from 'lucide-react'; import { api } from '@/lib/api'; interface Invitation { id: string; code: string; createdBy: { username: string | null; email: string | null }; usedBy: { username: string | null; email: string | null } | null; createdAt: string; expiresAt: string | null; used: boolean; } function displayUser(u: { username: string | null; email: string | null }): string { return u.username || u.email || 'Unknown'; } function StatusBadge({ used, expiresAt }: { used: boolean; expiresAt: string | null }) { if (used) { return Used; } if (expiresAt && new Date(expiresAt) < new Date()) { return Expired; } return Active; } export function InvitationsPage() { const [invitations, setInvitations] = useState([]); const [loading, setLoading] = useState(true); const [generating, setGenerating] = useState(false); const [copiedCode, setCopiedCode] = useState(null); const fetchInvitations = useCallback(async () => { try { const result = await api.invites.list(); setInvitations(result.invitations); } catch { // silent } finally { setLoading(false); } }, []); useEffect(() => { fetchInvitations(); }, [fetchInvitations]); async function handleGenerate() { setGenerating(true); try { const result = await api.invites.create(); const url = `${window.location.origin}?invite=${result.code}`; await navigator.clipboard.writeText(url); setCopiedCode(result.code); setTimeout(() => setCopiedCode(null), 2000); fetchInvitations(); } catch { // silent } finally { setGenerating(false); } } async function handleCopyCode(code: string) { const url = `${window.location.origin}?invite=${code}`; await navigator.clipboard.writeText(url); setCopiedCode(code); setTimeout(() => setCopiedCode(null), 2000); } const activeCount = invitations.filter(i => !i.used && (!i.expiresAt || new Date(i.expiresAt) > new Date())).length; const usedCount = invitations.filter(i => i.used).length; return (

Invitations

{activeCount} active, {usedCount} used, {invitations.length} total

{loading ? (
Loading invitations...
) : invitations.length === 0 ? (
No invitations yet. Generate one to get started.
) : ( {invitations.map((inv) => ( ))}
Code Created By Used By Created Status
{inv.code} {displayUser(inv.createdBy)} {inv.usedBy ? displayUser(inv.usedBy) : } {new Date(inv.createdAt).toLocaleDateString()} {!inv.used && ( )}
)}
); }