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) => (
))
)}
);
}