'use client'; import { useMemo, useState } from 'react'; import Link from 'next/link'; import { useParams } from 'next/navigation'; import { useQuery } from '@tanstack/react-query'; import { apiFetch } from '@/lib/api/client'; import { useRealtimeInvalidation } from '@/hooks/use-realtime-invalidation'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { TableSkeleton } from '@/components/shared/loading-skeleton'; import { EmptyState } from '@/components/shared/empty-state'; import { Bookmark } from 'lucide-react'; import type { InterestRow } from '@/components/interests/interest-columns'; interface BerthInterestsTabProps { berthId: string; } type StageFilter = 'all' | 'active' | 'lost'; type SortMode = 'newest' | 'stage' | 'category'; 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 STAGE_ORDER: Record = { open: 0, details_sent: 1, in_communication: 2, visited: 3, signed_eoi_nda: 4, deposit_10pct: 5, contract: 6, completed: 7, }; const CATEGORY_RANK: Record = { hot_lead: 0, specific_qualified: 1, general_interest: 2, }; const CATEGORY_LABELS: Record = { hot_lead: 'Hot Lead', specific_qualified: 'Specific Qualified', general_interest: 'General Interest', }; const SOURCE_LABELS: Record = { website: 'Website', manual: 'Manual', referral: 'Referral', broker: 'Broker', }; interface ListResponse { data: InterestRow[]; total: number; } export function BerthInterestsTab({ berthId }: BerthInterestsTabProps) { const params = useParams<{ portSlug: string }>(); const portSlug = params?.portSlug ?? ''; const [stage, setStage] = useState('all'); const [sortMode, setSortMode] = useState('newest'); const { data, isLoading } = useQuery({ queryKey: ['interests', 'by-berth', berthId], queryFn: () => apiFetch(`/api/v1/interests?berthId=${berthId}&limit=200`), staleTime: 30_000, }); useRealtimeInvalidation({ 'interest:created': [['interests', 'by-berth', berthId]], 'interest:updated': [['interests', 'by-berth', berthId]], 'interest:stageChanged': [['interests', 'by-berth', berthId]], 'interest:archived': [['interests', 'by-berth', berthId]], 'interest:berthLinked': [['interests', 'by-berth', berthId]], 'interest:berthUnlinked': [['interests', 'by-berth', berthId]], }); const rows = useMemo(() => { const all = data?.data ?? []; const filtered = all.filter((i) => { if (stage === 'active') return i.pipelineStage !== 'completed' && !i.archivedAt; if (stage === 'lost') return Boolean(i.archivedAt); return true; }); const sorted = [...filtered].sort((a, b) => { if (sortMode === 'stage') { const sa = STAGE_ORDER[a.pipelineStage] ?? 99; const sb = STAGE_ORDER[b.pipelineStage] ?? 99; if (sa !== sb) return sb - sa; // furthest along first } if (sortMode === 'category') { const ca = CATEGORY_RANK[a.leadCategory ?? ''] ?? 99; const cb = CATEGORY_RANK[b.leadCategory ?? ''] ?? 99; if (ca !== cb) return ca - cb; // hottest first } // Default + tiebreaker: newest first. return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); return sorted; }, [data?.data, stage, sortMode]); if (isLoading) return ; if ((data?.data ?? []).length === 0) { return ( ); } return (
{rows.length} of {data?.total ?? 0} interest{(data?.total ?? 0) === 1 ? '' : 's'}
{rows.map((i) => ( ))}
Client Stage Category Source Last activity
{i.clientName ?? '—'} {STAGE_LABELS[i.pipelineStage] ?? i.pipelineStage} {i.leadCategory ? (CATEGORY_LABELS[i.leadCategory] ?? i.leadCategory) : '—'} {i.source ? (SOURCE_LABELS[i.source] ?? i.source) : '—'} {new Date(i.createdAt).toLocaleDateString()}
); }