import type { FC } from 'react';
import { useMemo, useState } from 'react';
import { TableBody as AriaTableBody } from 'react-aria-components';
import type { SortDescriptor, Selection } from 'react-aria-components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faEllipsisVertical } from '@fortawesome/pro-duotone-svg-icons';
const DotsVertical: FC<{ className?: string }> = ({ className }) => ;
import { Badge } from '@/components/base/badges/badges';
import { Button } from '@/components/base/buttons/button';
import { Table } from '@/components/application/table/table';
import { LeadStatusBadge } from '@/components/shared/status-badge';
import { SourceTag } from '@/components/shared/source-tag';
import { AgeIndicator } from '@/components/shared/age-indicator';
import { formatPhone, formatShortDate } from '@/lib/format';
import { cx } from '@/utils/cx';
import type { Lead } from '@/types/entities';
type LeadTableProps = {
leads: Lead[];
onSelectionChange: (selectedIds: string[]) => void;
selectedIds: string[];
sortField: string;
sortDirection: 'asc' | 'desc';
onSort: (field: string) => void;
onViewActivity?: (lead: Lead) => void;
visibleColumns?: Set;
};
type TableRow = {
id: string;
type: 'lead' | 'dup-sub';
lead: Lead;
};
const SpamDisplay = ({ score }: { score: number }) => {
const colorClass =
score < 30
? 'text-success-primary'
: score < 60
? 'text-warning-primary'
: 'text-error-primary';
const bgClass = score >= 60 ? 'rounded px-1.5 py-0.5 bg-error-primary' : '';
return {score}%;
};
export const LeadTable = ({
leads,
onSelectionChange,
selectedIds,
sortField,
sortDirection,
onSort,
onViewActivity,
visibleColumns,
}: LeadTableProps) => {
const [expandedDupId, setExpandedDupId] = useState(null);
const selectedKeys: Selection = new Set(selectedIds);
const handleSelectionChange = (keys: Selection) => {
if (keys === 'all') {
// Only select actual lead rows, not dup sub-rows
onSelectionChange(leads.map((l) => l.id));
} else {
// Filter out dup sub-row IDs
const leadOnlyIds = [...keys].filter((k) => !String(k).endsWith('-dup')) as string[];
onSelectionChange(leadOnlyIds);
}
};
const sortDescriptor: SortDescriptor = {
column: sortField,
direction: sortDirection === 'asc' ? 'ascending' : 'descending',
};
const handleSortChange = (descriptor: SortDescriptor) => {
if (descriptor.column) {
onSort(String(descriptor.column));
}
};
// Flatten leads + expanded dup sub-rows into a single list
const tableRows = useMemo(() => {
const rows: TableRow[] = [];
for (const lead of leads) {
rows.push({ id: lead.id, type: 'lead', lead });
if (lead.isDuplicate === true && expandedDupId === lead.id) {
rows.push({ id: `${lead.id}-dup`, type: 'dup-sub', lead });
}
}
return rows;
}, [leads, expandedDupId]);
const allColumns = [
{ id: 'phone', label: 'Phone', allowsSorting: true, defaultWidth: 150 },
{ id: 'name', label: 'Name', allowsSorting: true, defaultWidth: 160 },
{ id: 'email', label: 'Email', allowsSorting: false, defaultWidth: 180 },
{ id: 'campaign', label: 'Campaign', allowsSorting: false, defaultWidth: 140 },
{ id: 'ad', label: 'Ad', allowsSorting: false, defaultWidth: 80 },
{ id: 'source', label: 'Source', allowsSorting: true, defaultWidth: 100 },
{ id: 'firstContactedAt', label: 'First Contact', allowsSorting: true, defaultWidth: 130 },
{ id: 'lastContactedAt', label: 'Last Contact', allowsSorting: true, defaultWidth: 130 },
{ id: 'status', label: 'Status', allowsSorting: true, defaultWidth: 100 },
{ id: 'createdAt', label: 'Age', allowsSorting: true, defaultWidth: 80 },
{ id: 'spamScore', label: 'Spam', allowsSorting: true, defaultWidth: 70 },
{ id: 'dups', label: 'Dups', allowsSorting: false, defaultWidth: 60 },
{ id: 'actions', label: '', allowsSorting: false, defaultWidth: 50 },
];
const columns = visibleColumns
? allColumns.filter(c => visibleColumns.has(c.id) || c.id === 'actions')
: allColumns;
return (
{(column) => (
)}
{(row) => {
const { lead } = row;
const firstName = lead.contactName?.firstName ?? '';
const lastName = lead.contactName?.lastName ?? '';
const name = `${firstName} ${lastName}`.trim() || '\u2014';
const phone = lead.contactPhone?.[0]
? formatPhone(lead.contactPhone[0])
: '\u2014';
const email = lead.contactEmail?.[0]?.address ?? '\u2014';
// Render duplicate sub-row
if (row.type === 'dup-sub') {
return (
{phone}
{name}
{email}
{lead.leadSource ? (
) : (
{'\u2014'}
)}
Same phone number
{lead.createdAt
? formatShortDate(lead.createdAt)
: '\u2014'}
);
}
// Render normal lead row
const isSpamRow = (lead.spamScore ?? 0) >= 60;
const isSelected = selectedIds.includes(lead.id);
const isDup = lead.isDuplicate === true;
const isExpanded = expandedDupId === lead.id;
const isCol = (id: string) => !visibleColumns || visibleColumns.has(id);
return (
{isCol('phone') &&
{phone}
}
{isCol('name') &&
{name}
}
{isCol('email') &&
{email}
}
{isCol('campaign') &&
{lead.utmCampaign ? (
{lead.utmCampaign}
) : (
{'\u2014'}
)}
}
{isCol('ad') &&
{lead.adId ? (
Ad
) : (
{'\u2014'}
)}
}
{isCol('source') &&
{lead.leadSource ? (
) : (
{'\u2014'}
)}
}
{isCol('firstContactedAt') &&
{lead.firstContactedAt
? formatShortDate(lead.firstContactedAt)
: '\u2014'}
}
{isCol('lastContactedAt') &&
{lead.lastContactedAt
? formatShortDate(lead.lastContactedAt)
: '\u2014'}
}
{isCol('status') &&
{lead.leadStatus ? (
) : (
{'\u2014'}
)}
}
{isCol('createdAt') &&
{lead.createdAt ? (
) : (
{'\u2014'}
)}
}
{isCol('spamScore') &&
{lead.spamScore != null ? (
) : (
0%
)}
}
{isCol('dups') &&
{isDup ? (
) : (
0
)}
}
);
}}
);
};