'use client'; import { useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { Loader2, ChevronsUpDown, Check } from 'lucide-react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter, } from '@/components/ui/sheet'; import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command'; import { Checkbox } from '@/components/ui/checkbox'; import { Separator } from '@/components/ui/separator'; import { TagPicker } from '@/components/shared/tag-picker'; import { apiFetch } from '@/lib/api/client'; import { useEntityOptions } from '@/hooks/use-entity-options'; import { createInterestSchema, type CreateInterestInput } from '@/lib/validators/interests'; import { PIPELINE_STAGES, LEAD_CATEGORIES } from '@/lib/constants'; import { cn } from '@/lib/utils'; const STAGE_LABELS: Record = { open: 'Open', details_sent: 'Details Sent', in_communication: 'In Communication', visited: 'Visited', signed_eoi_nda: 'Signed EOI/NDA', deposit_10pct: 'Deposit 10%', contract: 'Contract', completed: 'Completed', }; const CATEGORY_LABELS: Record = { general_interest: 'General Interest', specific_qualified: 'Specific Qualified', hot_lead: 'Hot Lead', }; interface InterestFormProps { open: boolean; onOpenChange: (open: boolean) => void; interest?: { id: string; clientId: string; clientName?: string | null; berthId?: string | null; berthMooringNumber?: string | null; pipelineStage: string; leadCategory?: string | null; source?: string | null; notes?: string | null; reminderEnabled?: boolean; reminderDays?: number | null; tags?: Array<{ id: string }>; }; } export function InterestForm({ open, onOpenChange, interest }: InterestFormProps) { const queryClient = useQueryClient(); const isEdit = !!interest; const [clientOpen, setClientOpen] = useState(false); const [berthOpen, setBerthOpen] = useState(false); const { register, handleSubmit, watch, setValue, reset, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(createInterestSchema), defaultValues: { clientId: '', pipelineStage: 'open', reminderEnabled: false, tagIds: [], }, }); const tagIds = watch('tagIds') ?? []; const reminderEnabled = watch('reminderEnabled'); const selectedClientId = watch('clientId'); const selectedBerthId = watch('berthId'); const { options: clientOptions, isLoading: clientsLoading, setSearch: setClientSearch } = useEntityOptions({ endpoint: '/api/v1/clients/options', labelKey: 'fullName', }); const { options: berthOptions, isLoading: berthsLoading, setSearch: setBerthSearch } = useEntityOptions({ endpoint: '/api/v1/berths/options', labelKey: 'mooringNumber', }); useEffect(() => { if (interest && open) { reset({ clientId: interest.clientId, berthId: interest.berthId ?? undefined, pipelineStage: interest.pipelineStage as typeof PIPELINE_STAGES[number], leadCategory: interest.leadCategory as typeof LEAD_CATEGORIES[number] | undefined, source: interest.source ?? undefined, notes: interest.notes ?? undefined, reminderEnabled: interest.reminderEnabled ?? false, reminderDays: interest.reminderDays ?? undefined, tagIds: interest.tags?.map((t) => t.id) ?? [], }); } else if (!interest && open) { reset({ clientId: '', pipelineStage: 'open', reminderEnabled: false, tagIds: [], }); } }, [interest, open, reset]); const mutation = useMutation({ mutationFn: async (data: CreateInterestInput) => { if (isEdit) { const { tagIds: tIds, ...rest } = data; await apiFetch(`/api/v1/interests/${interest!.id}`, { method: 'PATCH', body: rest }); if (tIds) { await apiFetch(`/api/v1/interests/${interest!.id}/tags`, { method: 'PUT', body: { tagIds: tIds }, }); } } else { await apiFetch('/api/v1/interests', { method: 'POST', body: data }); } }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['interests'] }); onOpenChange(false); }, }); const selectedClient = clientOptions.find((c) => c.value === selectedClientId); const selectedBerth = berthOptions.find((b) => b.value === selectedBerthId); return ( {isEdit ? 'Edit Interest' : 'New Interest'}
mutation.mutate(data))} className="space-y-6 py-6" > {/* Client */}

Client & Berth

{clientsLoading ? 'Loading...' : 'No clients found.'} {clientOptions.map((option) => ( { setValue('clientId', val); setClientOpen(false); }} > {option.label} ))} {errors.clientId && (

{errors.clientId.message}

)}
{berthsLoading ? 'Loading...' : 'No berths found.'} { setValue('berthId', undefined); setBerthOpen(false); }} > None {berthOptions.map((option) => ( { setValue('berthId', val); setBerthOpen(false); }} > {option.label} ))}
{/* Pipeline */}

Pipeline

{/* Notes */}