import { useState, useEffect, useCallback } from 'react' import { Plus, Copy, Trash2, Ticket, Mail, MailCheck, RefreshCw } from 'lucide-react' import { Button } from '@/components/ui/Button' import { Input } from '@/components/ui/Input' import { DataTable, PageHeader, StatusBadge, ActionMenu, EmptyState } from '@/components/admin' import type { Column } from '@/components/admin' import { Modal } from '@/components/common/Modal' import { adminApi } from '@/api/admin' import { toast } from '@/lib/toast' import { cn } from '@/lib/utils' import type { InviteCodeResponse, InviteCodeCreateRequest } from '@/types/admin' const PLAN_OPTIONS = [ { value: 'free', label: 'Free' }, { value: 'starter', label: 'Starter' }, { value: 'pro', label: 'Pro' }, { value: 'enterprise', label: 'Enterprise' }, ] as const const planBadgeVariant = (plan: string): 'success' | 'destructive' | 'warning' | 'default' => { switch (plan) { case 'pro': return 'success' case 'team': return 'warning' default: return 'default' } } export function InviteCodesPage() { const [codes, setCodes] = useState([]) const [loading, setLoading] = useState(true) const [createOpen, setCreateOpen] = useState(false) const [creating, setCreating] = useState(false) // Form state const [email, setEmail] = useState('') const [expiresInDays, setExpiresInDays] = useState('') const [assignedPlan, setAssignedPlan] = useState<'free' | 'pro' | 'starter' | 'enterprise'>('free') const [trialDays, setTrialDays] = useState('') const [note, setNote] = useState('') const fetchCodes = useCallback(async () => { setLoading(true) try { const data = await adminApi.listInviteCodes() setCodes(Array.isArray(data) ? data : []) } catch { toast.error('Failed to load invite codes') } finally { setLoading(false) } }, []) useEffect(() => { fetchCodes() }, [fetchCodes]) const resetForm = () => { setEmail('') setExpiresInDays('') setAssignedPlan('free') setTrialDays('') setNote('') } const handleCreate = async () => { setCreating(true) try { const data: InviteCodeCreateRequest = {} if (expiresInDays) { data.expires_at = new Date(Date.now() + parseInt(expiresInDays) * 86400000).toISOString() } if (email.trim()) data.email = email.trim() if (note.trim()) data.note = note.trim() data.assigned_plan = assignedPlan if (assignedPlan !== 'free' && trialDays) { data.trial_duration_days = parseInt(trialDays) } await adminApi.createInviteCode(data) toast.success('Invite code created') setCreateOpen(false) resetForm() fetchCodes() } catch { toast.error('Failed to create invite code') } finally { setCreating(false) } } const handleCopy = (code: string) => { navigator.clipboard.writeText(code) toast.success('Code copied to clipboard') } const handleDelete = async (code: string) => { try { await adminApi.deleteInviteCode(code) toast.success('Invite code deleted') fetchCodes() } catch { toast.error('Failed to delete invite code') } } const handleResend = async (code: string) => { try { const newInvite = await adminApi.resendInviteCode(code) toast.success(`New code ${newInvite.code} sent to ${newInvite.email}`) fetchCodes() } catch { toast.error('Failed to resend invite code') } } const selectClass = cn( 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm text-foreground', 'placeholder:text-muted-foreground focus:outline-hidden focus:border-primary focus:ring-2 focus:ring-primary/20' ) const columns: Column[] = [ { key: 'code', header: 'Code', render: (c) => ( {c.code} ), }, { key: 'email', header: 'Recipient', render: (c) => c.email ? (
{c.email_sent ? ( ) : ( )} {c.email}
) : ( ), }, { key: 'plan', header: 'Plan', render: (c) => ( {c.assigned_plan.charAt(0).toUpperCase() + c.assigned_plan.slice(1)} ), }, { key: 'trial', header: 'Trial', render: (c) => c.has_trial ? ( {c.trial_duration_days}d ) : ( ), }, { key: 'status', header: 'Status', render: (c) => { if (c.is_used) return Used if (c.is_expired) return Expired if (c.is_valid) return Active return Inactive }, }, { key: 'expires_at', header: 'Expires', render: (c) => ( {c.expires_at ? new Date(c.expires_at).toLocaleDateString() : 'Never'} ), }, { key: 'created_at', header: 'Created', render: (c) => ( {new Date(c.created_at).toLocaleDateString()} ), }, { key: 'actions', header: '', className: 'w-12', render: (c) => ( , onClick: () => handleCopy(c.code), }, ...(c.is_valid && c.email ? [{ label: 'Resend', icon: , onClick: () => handleResend(c.code), }] : []), ...(!c.is_used ? [{ label: 'Delete', icon: , onClick: () => handleDelete(c.code), destructive: true, }] : []), ]} /> ), }, ] return (
setCreateOpen(true)}> Create Code } /> c.id} isLoading={loading} emptyState={ } title="No invite codes" description="Create an invite code to allow new user registrations." /> } /> { setCreateOpen(false); resetForm() }} title="Create Invite Code" size="sm" footer={
} >
setEmail(e.target.value)} placeholder="Optional — will send invite email" />
{assignedPlan !== 'free' && (
setTrialDays(e.target.value)} placeholder="e.g. 14 (1-90)" min={1} max={90} />

Leave empty for no trial — account gets full plan immediately.

)}
setExpiresInDays(e.target.value)} placeholder="Leave empty for no expiry" />
setNote(e.target.value)} placeholder="Optional note (e.g. who this is for)" />
) } export default InviteCodesPage