import { useDeferredValue, useMemo, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import type { ColumnDef } from '@tanstack/react-table'; import type { PermissionGrant } from '@pig/core'; import { Building2, Mail, Pencil, Plus, RefreshCw, Search, UserPlus } from 'lucide-react'; import { AccountSheet, ContactSheet, type AccountRecord, type ContactRecord, type ContactRow } from '@/components/RecordSheets'; import { DataTable, DataTableColumnHeader } from '@/components/DataTable'; import { Badge, Button, Card, ConfidenceBadge, EmptyState, Input, Skeleton } from '@/components/ui'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { get, relativeTime } from '@/lib/api'; import { can } from '@/lib/permissions'; import { usePageTitle } from '@/lib/title'; interface Me { permissions: PermissionGrant[] } type View = 'accounts' | 'contacts'; export function Accounts() { usePageTitle('Accounts'); const [side, setSide] = useState<'all' | 'supply' | 'demand'>('all'); const [view, setView] = useState('accounts'); const [mobileSearch, setMobileSearch] = useState(''); const deferredSearch = useDeferredValue(mobileSearch.trim().toLocaleLowerCase()); const [accountSheet, setAccountSheet] = useState<{ open: boolean; record?: AccountRecord }>({ open: false }); const [contactSheet, setContactSheet] = useState<{ open: boolean; record?: ContactRecord; accountId?: string }>({ open: false }); const { data: me } = useQuery({ queryKey: ['me'], queryFn: () => get('/api/me') }); const accountsQuery = useQuery({ queryKey: ['accounts', side], queryFn: () => get(`/api/accounts${side === 'all' ? '' : `?side=${side}`}`) }); const contactsQuery = useQuery({ queryKey: ['contacts', 'table'], queryFn: () => get('/api/contacts') }); const canDemand = can(me, 'deal:write', 'demand'); const canSupply = can(me, 'deal:write', 'supply'); const canAny = canDemand || canSupply; const canAccount = (account: AccountRecord) => account.side === 'both' ? canAny : account.side === 'demand' ? canDemand : canSupply; const canContact = (row: ContactRow) => row.accountSide === 'both' ? canAny : row.accountSide === 'demand' ? canDemand : row.accountSide === 'supply' ? canSupply : false; const visibleAccounts = useMemo(() => !deferredSearch ? accountsQuery.data ?? [] : (accountsQuery.data ?? []).filter((account) => [account.name, account.domain, account.country, account.supplierType, account.customerSegment].some((value) => value?.toLocaleLowerCase().includes(deferredSearch))), [accountsQuery.data, deferredSearch]); const visibleContacts = useMemo(() => !deferredSearch ? contactsQuery.data ?? [] : (contactsQuery.data ?? []).filter((row) => [row.contact.fullName, row.contact.email, row.contact.title, row.accountName].some((value) => value?.toLocaleLowerCase().includes(deferredSearch))), [contactsQuery.data, deferredSearch]); const accountColumns: ColumnDef[] = [ { id: 'account', accessorFn: (account) => `${account.name} ${account.domain ?? ''}`, header: ({ column }) => , cell: ({ row }) =>

{row.original.name}

{row.original.domain ?

{row.original.domain}

: null}
}, { accessorKey: 'side', header: ({ column }) => , cell: ({ row }) => }, { id: 'type', accessorFn: (account) => account.supplierType ?? account.customerSegment ?? '', header: ({ column }) => , cell: ({ row }) => { const type = accountType(row.original); return type ? {type.replace(/_/g, ' ')} : '—'; } }, { accessorKey: 'country', header: ({ column }) => , cell: ({ row }) => row.original.country ?? '—' }, { accessorKey: 'confidence', header: ({ column }) => , cell: ({ row }) => }, { accessorKey: 'lastActivityAt', header: ({ column }) => , cell: ({ row }) => row.original.lastActivityAt ? relativeTime(row.original.lastActivityAt) : '—' }, { id: 'actions', enableHiding: false, enableSorting: false, header: 'Actions', cell: ({ row }) =>
}, ]; const contactColumns: ColumnDef[] = [ { id: 'contact', accessorFn: (row) => `${row.contact.fullName} ${row.contact.email ?? ''}`, header: ({ column }) => , cell: ({ row }) =>

{row.original.contact.fullName}

{row.original.contact.email ?

{row.original.contact.email}

:

No email recorded

}
}, { accessorKey: 'accountName', header: ({ column }) => , cell: ({ row }) => row.original.accountName ?? 'Unassigned' }, { id: 'title', accessorFn: (row) => row.contact.title ?? '', header: ({ column }) => , cell: ({ row }) => row.original.contact.title ?? '—' }, { id: 'affiliation', accessorFn: (row) => row.contact.affiliation, header: ({ column }) => , cell: ({ row }) => {row.original.contact.affiliation.replace(/_/g, ' ')} }, { id: 'confidence', accessorFn: (row) => row.contact.confidence, header: ({ column }) => , cell: ({ row }) => }, { id: 'lastActivityAt', accessorFn: (row) => row.contact.lastActivityAt ?? '', header: ({ column }) => , cell: ({ row }) => row.original.contact.lastActivityAt ? relativeTime(row.original.contact.lastActivityAt) : '—' }, { id: 'actions', enableHiding: false, enableSorting: false, header: 'Actions', cell: ({ row }) =>
}, ]; const activeQuery = view === 'accounts' ? accountsQuery : contactsQuery; const count = view === 'accounts' ? accountsQuery.data?.length ?? 0 : contactsQuery.data?.length ?? 0; return

Accounts

Providers we buy from, customers we sell to, and the people who make each relationship real.

{ setView(value as View); setMobileSearch(''); }}>AccountsContacts

{count} {view}{view === 'accounts' && side !== 'all' ? ` · ${side}` : ''}

{view === 'accounts' ?
{(['all', 'supply', 'demand'] as const).map((value) => )}
: null}
{activeQuery.isLoading ? : null} {activeQuery.isError ? void activeQuery.refetch()}>Try again} /> : null} {!activeQuery.isLoading && !activeQuery.isError && view === 'accounts' ? <>
{visibleAccounts.map((account) => setContactSheet({ open: true, accountId: account.id })} onEdit={() => setAccountSheet({ open: true, record: account })} />)}{visibleAccounts.length === 0 ? } title={accountsQuery.data?.length ? 'No accounts match' : `No ${side === 'all' ? '' : `${side} `}accounts`} description={accountsQuery.data?.length ? 'Try a broader search.' : 'No relationship records are available in this view.'} /> : null}
: null} {!activeQuery.isLoading && !activeQuery.isError && view === 'contacts' ? <>
{visibleContacts.map((row) => setContactSheet({ open: true, record: row.contact })} />)}{visibleContacts.length === 0 ? } title={contactsQuery.data?.length ? 'No contacts match' : 'No contacts recorded'} description={contactsQuery.data?.length ? 'Try a broader search.' : 'Add a sourced contact to an account when the relationship is known.'} /> : null}
: null} setAccountSheet((state) => ({ ...state, open }))} record={accountSheet.record} identity={me} /> setContactSheet((state) => ({ ...state, open }))} record={contactSheet.record} defaultAccountId={contactSheet.accountId} identity={me} />
; } function AccountCard({ account, writable, onAddContact, onEdit }: { account: AccountRecord; writable: boolean; onAddContact(): void; onEdit(): void }) { const type = accountType(account); return

{account.name}

{account.domain ?? 'No domain recorded'}

} />
; } function ContactCard({ row, writable, onEdit }: { row: ContactRow; writable: boolean; onEdit(): void }) { return

{row.contact.fullName}

{row.contact.title ?? 'No title recorded'}

{row.contact.affiliation.replace(/_/g, ' ')}
} />
; } function RecordValue({ label, value }: { label: string; value: React.ReactNode }) { return

{label}

{value}
; } function SideBadge({ side }: { side: AccountRecord['side'] }) { return {side === 'supply' ? 'Buy-side' : side === 'demand' ? 'Sell-side' : 'Both sides'}; } function Confidence({ confidence }: { confidence: string }) { return confidence === 'confirmed' ? Confirmed : ; } function accountType(account: AccountRecord) { return account.supplierType ?? account.customerSegment; }