'use client'; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { useCallback, useState } from "react"; import { z } from "zod"; import { PlusCircleIcon, Loader2 } from "lucide-react"; import { OrgRole } from "@prisma/client"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { createInvites } from "@/actions"; import { useDomain } from "@/hooks/useDomain"; import { isServiceError } from "@/lib/utils"; import { useToast } from "@/components/hooks/use-toast"; import { useRouter } from "next/navigation"; import useCaptureEvent from "@/hooks/useCaptureEvent"; export const inviteMemberFormSchema = z.object({ emails: z.array(z.object({ email: z.string().email() })) .refine((emails) => { const emailSet = new Set(emails.map(e => e.email.toLowerCase())); return emailSet.size === emails.length; }, "Duplicate email addresses are not allowed") }); interface InviteMemberCardProps { currentUserRole: OrgRole; } export const InviteMemberCard = ({ currentUserRole }: InviteMemberCardProps) => { const [isInviteDialogOpen, setIsInviteDialogOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const domain = useDomain(); const { toast } = useToast(); const router = useRouter(); const captureEvent = useCaptureEvent(); const form = useForm>({ resolver: zodResolver(inviteMemberFormSchema), defaultValues: { emails: [{ email: "" }] }, }); const addEmailField = useCallback(() => { const emails = form.getValues().emails; form.setValue('emails', [...emails, { email: "" }]); }, [form]); const onSubmit = useCallback((data: z.infer) => { setIsLoading(true); createInvites(data.emails.map(e => e.email), domain) .then((res) => { if (isServiceError(res)) { toast({ description: `❌ Failed to invite members. Reason: ${res.message}` }); captureEvent('wa_invite_member_card_invite_fail', { error: res.errorCode, num_emails: data.emails.length, }); } else { form.reset(); router.push(`?tab=invites`); router.refresh(); toast({ description: `✅ Successfully invited ${data.emails.length} members` }); captureEvent('wa_invite_member_card_invite_success', { num_emails: data.emails.length, }); } }) .finally(() => { setIsLoading(false); }); }, [domain, form, toast, router, captureEvent]); return ( <> Invite Member Invite new members to your organization.
setIsInviteDialogOpen(true))}> Email Address {form.watch('emails').map((_, index) => ( ( )} /> ))} {form.formState.errors.emails?.root?.message && ( {form.formState.errors.emails.root.message} )}
Invite Team Members {`Your team is growing! By confirming, you will be inviting ${form.getValues().emails.length} new members to your organization. Your subscription's seat count will be adjusted when a member accepts their invitation.`}
{form.getValues().emails.map(({ email }, index) => (

{email}

))}
captureEvent('wa_invite_member_card_invite_cancel', { num_emails: form.getValues().emails.length, })}>Cancel onSubmit(form.getValues())} > Invite
) }