'use client' import { useState, useImperativeHandle, forwardRef } from 'react' import { trpc } from '@/lib/trpc/client' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from '@/components/ui/dialog' import { Badge } from '@/components/ui/badge' import { Plus, Pencil, Trash2, Mail, MailCheck, Utensils } from 'lucide-react' import { toast } from 'sonner' const ALLERGENS = [ 'GLUTEN', 'CRUSTACEANS', 'EGGS', 'FISH', 'PEANUTS', 'SOYBEANS', 'MILK', 'TREE_NUTS', 'CELERY', 'MUSTARD', 'SESAME', 'SULPHITES', 'LUPIN', 'MOLLUSCS', ] as const type Allergen = (typeof ALLERGENS)[number] const STANDALONE = '__standalone__' const NO_DISH = '__no_dish__' type Editing = { mode: 'new' } | { mode: 'edit'; id: string } | null export type LunchExternalsHandle = { openEditDialog: (id: string) => void } export const LunchExternals = forwardRef< LunchExternalsHandle, { programId: string; lunchEventId: string } >(function LunchExternals({ programId, lunchEventId }, ref) { const utils = trpc.useUtils() const { data: externals } = trpc.lunch.listExternals.useQuery({ lunchEventId }) const { data: dishes } = trpc.lunch.listDishes.useQuery({ lunchEventId }) const { data: projects } = trpc.program.listFinalistProjects.useQuery({ programId, }) const [editing, setEditing] = useState(null) useImperativeHandle( ref, () => ({ openEditDialog: (id: string) => setEditing({ mode: 'edit', id }), }), [], ) const invalidateAll = () => { utils.lunch.listExternals.invalidate({ lunchEventId }) utils.lunch.getManifest.invalidate({ programId }) } const create = trpc.lunch.createExternal.useMutation({ onSuccess: invalidateAll, onError: (e) => toast.error(e.message), }) const update = trpc.lunch.updateExternal.useMutation({ onSuccess: invalidateAll, onError: (e) => toast.error(e.message), }) const del = trpc.lunch.deleteExternal.useMutation({ onSuccess: invalidateAll, onError: (e) => toast.error(e.message), }) const sendInvite = trpc.lunch.sendExternalInvite.useMutation({ onSuccess: () => { invalidateAll() toast.success('Dish invite sent') }, onError: (e) => toast.error(e.message), }) const editingRow = editing?.mode === 'edit' ? (externals?.find((e) => e.id === editing.id) ?? null) : null return ( External attendees {externals?.length === 0 && (

No external attendees yet. Add jurors, dignitaries, or per-team plus-ones.

)} {externals && externals.length > 0 && (
{externals.map((e) => ( ))}
{e.name} {e.project?.title ?? 'Standalone'} {e.roleNote ?? ''} {e.dishId ? ( Picked ) : !e.email ? ( No email ) : e.inviteSentAt ? ( Invited ) : ( Not invited )} {e.email && !e.dishId && ( )}
)}
{editing && ( setEditing(null)} onSubmit={(values) => { if (editing.mode === 'new') { create.mutate( { lunchEventId, ...values }, { onSuccess: () => setEditing(null) }, ) } else { update.mutate( { externalId: editing.id, ...values }, { onSuccess: () => setEditing(null) }, ) } }} /> )}
) }) function ExternalDialog({ mode, initial, dishes, projects, submitting, onClose, onSubmit, }: { mode: 'new' | 'edit' initial: { name: string email: string | null projectId: string | null roleNote: string | null dishId: string | null allergens: string[] allergenOther: string | null } | null dishes: Array<{ id: string; name: string }> projects: Array<{ id: string; title: string }> submitting: boolean onClose: () => void onSubmit: (values: { name: string email?: string projectId?: string | null roleNote?: string dishId?: string | null allergens: Allergen[] allergenOther?: string | null }) => void }) { const [name, setName] = useState(initial?.name ?? '') const [email, setEmail] = useState(initial?.email ?? '') const [projectId, setProjectId] = useState(initial?.projectId ?? '') const [roleNote, setRoleNote] = useState(initial?.roleNote ?? '') const [dishId, setDishId] = useState(initial?.dishId ?? '') const [allergens, setAllergens] = useState( (initial?.allergens as Allergen[]) ?? [], ) const [allergenOther, setAllergenOther] = useState(initial?.allergenOther ?? '') return ( { if (!o) onClose() }}> {mode === 'new' ? 'Add external attendee' : 'Edit external attendee'}
setName(e.target.value)} />
setEmail(e.target.value)} />
setRoleNote(e.target.value)} placeholder="e.g. Foundation rep, Speaker, Sponsor" />
{ALLERGENS.map((a) => ( ))}