'use client' import { useState } from 'react' import { Search, UserPlus, Mail } from 'lucide-react' import { toast } from 'sonner' import { trpc } from '@/lib/trpc/client' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' interface AddMemberDialogProps { juryGroupId: string open: boolean onOpenChange: (open: boolean) => void } export function AddMemberDialog({ juryGroupId, open, onOpenChange }: AddMemberDialogProps) { const [tab, setTab] = useState<'search' | 'invite'>('search') // Search existing user state const [searchQuery, setSearchQuery] = useState('') const [selectedUserId, setSelectedUserId] = useState('') const [maxAssignments, setMaxAssignments] = useState('') const [capMode, setCapMode] = useState('') // Invite new user state const [inviteName, setInviteName] = useState('') const [inviteEmail, setInviteEmail] = useState('') const [inviteMaxAssignments, setInviteMaxAssignments] = useState('') const [inviteCapMode, setInviteCapMode] = useState('') const [inviteExpertise, setInviteExpertise] = useState('') const utils = trpc.useUtils() const { data: userResponse, isLoading: isSearching } = trpc.user.list.useQuery( { search: searchQuery, perPage: 20 }, { enabled: searchQuery.length > 0 } ) const users = userResponse?.users || [] const { mutate: addMember, isPending: isAdding } = trpc.juryGroup.addMember.useMutation({ onSuccess: () => { utils.juryGroup.getById.invalidate({ id: juryGroupId }) toast.success('Member added successfully') onOpenChange(false) resetForm() }, onError: (err) => { toast.error(err.message) }, }) const { mutate: createUser, isPending: isCreating } = trpc.user.create.useMutation({ onSuccess: (newUser) => { // Immediately add the newly created user to the jury group addMember({ juryGroupId, userId: newUser.id, role: 'MEMBER', maxAssignmentsOverride: inviteMaxAssignments ? parseInt(inviteMaxAssignments, 10) : null, capModeOverride: inviteCapMode && inviteCapMode !== 'DEFAULT' ? (inviteCapMode as 'HARD' | 'SOFT' | 'NONE') : null, }) // Send invitation email sendInvitation({ userId: newUser.id, juryGroupId }) }, onError: (err) => { toast.error(err.message) }, }) const { mutate: sendInvitation } = trpc.user.sendInvitation.useMutation({ onSuccess: (result) => { toast.success(`Invitation sent to ${result.email}`) }, onError: (err) => { // Don't block — user was created and added, just invitation failed toast.error(`Member added but invitation email failed: ${err.message}`) }, }) const resetForm = () => { setSearchQuery('') setSelectedUserId('') setMaxAssignments('') setCapMode('') setInviteName('') setInviteEmail('') setInviteMaxAssignments('') setInviteCapMode('') setInviteExpertise('') } const handleSearchSubmit = (e: React.FormEvent) => { e.preventDefault() if (!selectedUserId) { toast.error('Please select a user') return } addMember({ juryGroupId, userId: selectedUserId, role: 'MEMBER', maxAssignmentsOverride: maxAssignments ? parseInt(maxAssignments, 10) : null, capModeOverride: capMode && capMode !== 'DEFAULT' ? (capMode as 'HARD' | 'SOFT' | 'NONE') : null, }) } const handleInviteSubmit = (e: React.FormEvent) => { e.preventDefault() if (!inviteEmail.trim()) { toast.error('Please enter an email address') return } const expertiseTags = inviteExpertise .split(',') .map((t) => t.trim()) .filter(Boolean) createUser({ email: inviteEmail.trim(), name: inviteName.trim() || undefined, role: 'JURY_MEMBER', expertiseTags: expertiseTags.length > 0 ? expertiseTags : undefined, maxAssignments: inviteMaxAssignments ? parseInt(inviteMaxAssignments, 10) : undefined, }) } const isPending = isAdding || isCreating return ( Add Member to Jury Group Search for an existing user or invite a new juror to the platform setTab(v as 'search' | 'invite')}> Search Existing Invite New {/* Search existing user tab */}
setSearchQuery(e.target.value)} />
{isSearching && (

Searching...

)} {users && users.length > 0 && (
{users.map((user) => ( ))}
)}
setMaxAssignments(e.target.value)} />
{/* Invite new user tab */}
setInviteName(e.target.value)} />
setInviteEmail(e.target.value)} />
setInviteMaxAssignments(e.target.value)} />
setInviteExpertise(e.target.value)} />

Comma-separated tags for smart assignment matching

This will create a new user account and send an invitation email to join the platform as a jury member.

) }