import { useState } from 'react'; import { X } from 'lucide-react'; import { toast } from 'sonner'; import { z } from 'zod'; import { Button } from '@/components/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Muted } from '@/components/ui/typography'; import { useInviteMembers } from '@/hooks/use-member-management'; const emailSchema = z.string().email(); function EmailChip({ email, onRemove, }: { email: string; onRemove: () => void; }) { return ( {email} ); } export function AddMembersDialog({ networkId, open, onOpenChange, }: { networkId: string; open: boolean; onOpenChange: (open: boolean) => void; }) { const [emails, setEmails] = useState([]); const [input, setInput] = useState(''); const [error, setError] = useState(null); const inviteMembers = useInviteMembers(networkId); const reset = () => { setEmails([]); setInput(''); setError(null); }; const handleOpenChange = (next: boolean) => { if (!next) reset(); onOpenChange(next); }; // Commits the current input as a chip. Returns the next list of emails so // callers (like submit) can act on the freshly-committed value. const commit = (raw: string): string[] | null => { const trimmed = raw.trim().replace(/,$/, '').trim(); if (!trimmed) return emails; if (!emailSchema.safeParse(trimmed).success) { setError(`"${trimmed}" doesn't look like a valid email.`); return null; } if (emails.includes(trimmed)) { setInput(''); return emails; } const next = [...emails, trimmed]; setEmails(next); setInput(''); setError(null); return next; }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.key === ',') { e.preventDefault(); commit(input); } else if (e.key === 'Backspace' && input === '' && emails.length > 0) { setEmails(emails.slice(0, -1)); } }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); const next = commit(input); if (next === null) return; // invalid pending input if (next.length === 0) return; inviteMembers.mutate(next, { onSuccess: () => { toast.success( next.length === 1 ? `Invited ${next[0]}` : `Invited ${next.length} people`, ); handleOpenChange(false); }, }); }; return ( Add members Enter email addresses to add people to this network.
{emails.map((email) => ( setEmails(emails.filter((x) => x !== email))} /> ))} { setInput(e.target.value); if (error) setError(null); }} onKeyDown={handleKeyDown} onBlur={() => commit(input)} placeholder={ emails.length === 0 ? 'name@example.com' : 'Add another…' } className="h-7 min-w-[8rem] flex-1 border-0 px-1 shadow-none focus-visible:ring-0" autoFocus />
{error ? (

{error}

) : ( Press Enter or comma to add each email. )}
); }