import { type KeyboardEvent, type MouseEvent } from "react"; import SearchOffOutlinedIcon from "@mui/icons-material/SearchOffOutlined"; import EditOutlinedIcon from "@mui/icons-material/EditOutlined"; import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import { Avatar, Box, CircularProgress, IconButton, Link, Paper, Stack, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, } from "@mui/material"; import { VendorStatusBadge } from "./vendor-status-badge"; import { Text } from "@/components/ui/text"; import type { VendorListItem } from "@/domain/vendors/types/vendor"; const TABLE_COLUMNS = [ "Company", "Technician", "Location", "Trade", "Phone", "Email", "Total Jobs", ] as const; function getInitials(name: string): string { const parts = name.trim().split(/\s+/).filter(Boolean); if (parts.length === 0) return "?"; const first = parts[0]?.[0] ?? ""; const last = parts.length > 1 ? (parts[parts.length - 1]?.[0] ?? "") : ""; return (first + last).toUpperCase(); } function splitTrades(value: string | undefined): string[] { return (value ?? "") .split(",") .map((trade) => trade.trim()) .filter(Boolean); } function stopPropagation(event: MouseEvent): void { event.stopPropagation(); } function stopTradeBadgeKeyDown(event: KeyboardEvent): void { if (event.key !== "Enter" && event.key !== " ") return; event.preventDefault(); event.stopPropagation(); } interface TruncatedTextProps { value: string; testId: string; focusable?: boolean; href?: string; color?: string; variant?: "description"; } const truncatedTextSx = { display: "block", minWidth: 0, maxWidth: "100%", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", }; function TruncatedText({ value, testId, focusable = false, href, color = "inherit", variant, }: TruncatedTextProps) { const hasValue = value !== "—"; const content = href ? ( {value} ) : variant ? ( {value} ) : ( {value} ); return ( {content} ); } function getVendorDisplayValues(row: VendorListItem) { const cityStateText = [row.city.trim(), row.state.trim()].filter(Boolean).join(", "); return { locationText: cityStateText || row.address.trim() || "—", phoneText: row.phone.trim() || row.companyPhone.trim(), }; } interface VendorTableRowProps { row: VendorListItem; onOpenDetail: (row: VendorListItem) => void; onOpenEdit: (row: VendorListItem) => void; } function VendorTradeCell({ primaryTrade, omittedTrades, }: { primaryTrade: string; omittedTrades: string[]; }) { const extraTrades = omittedTrades.length; return ( {primaryTrade === "—" ? ( "—" ) : ( {primaryTrade} {extraTrades > 0 && ( +{extraTrades} )} )} ); } function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps) { const trades = splitTrades(row.tradeSpecialties); const primaryTrade = trades[0] ?? "—"; const omittedTrades = trades.slice(1); const { locationText, phoneText } = getVendorDisplayValues(row); const rowKey = String(row.id ?? `company-${row.companyId ?? ""}`); const handleKeyDown = (event: KeyboardEvent) => { if (event.key !== "Enter" && event.key !== " ") return; event.preventDefault(); event.stopPropagation(); onOpenDetail(row); }; return ( onOpenDetail(row)} onKeyDown={handleKeyDown} > {getInitials(row.contactName)} {row.totalJobs ?? 0} { event.stopPropagation(); onOpenDetail(row); }} > { event.stopPropagation(); onOpenEdit(row); }} > ); } interface VendorsTableProps { rows: VendorListItem[]; isActive: boolean; isFiltered: boolean; isLoading: boolean; showError: boolean; onOpenDetail: (row: VendorListItem) => void; onOpenEdit: (row: VendorListItem) => void; } export function VendorsTable({ rows, isActive, isFiltered, isLoading, showError, onOpenDetail, onOpenEdit, }: VendorsTableProps) { const showEmptyRow = !isLoading && !showError && rows.length === 0; const columnCount = TABLE_COLUMNS.length + 2; return ( {TABLE_COLUMNS.map((column) => ( {column} ))} Status {isLoading ? ( ) : showEmptyRow ? ( {`No ${isActive ? "active" : "inactive"} technicians found`} {isFiltered ? "Try a different search, or add a new vendor." : "Add a vendor to get started."} ) : ( rows.map((row) => ( )) )}
); }