mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-05 01:22:06 +00:00
* fix(vendors): add accessible trade overflow tooltip * fix(vendors): add accessible trade overflow tooltip * refactor(vendors): satisfy row maintainability gate * feat(vendors): group directory rows by company * chore(vendors): trigger SH-281 review checks * test(vendors): align company directory visuals * test(vendors): complete company visual baselines * fix(vendors): keep empty states in viewport * test(vendors): align centered empty baseline * fix(vendors): use grouped directory endpoint * test(vendors): follow grouped directory endpoint * test(vendors): refresh combined visual snapshots --------- Co-authored-by: Codex Review Integration <codex-review@local.invalid>
152 lines
4.4 KiB
TypeScript
152 lines
4.4 KiB
TypeScript
import SearchOffOutlinedIcon from "@mui/icons-material/SearchOffOutlined";
|
|
import {
|
|
CircularProgress,
|
|
Paper,
|
|
Stack,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
} from "@mui/material";
|
|
import { VendorTableRow } from "./vendor-table-row";
|
|
import { Text } from "@/components/ui/text";
|
|
import type { VendorListItem } from "@/domain/vendors/types/vendor";
|
|
|
|
const TABLE_COLUMNS = [
|
|
"Company",
|
|
"Company Phone",
|
|
"Company Email",
|
|
"Street Address",
|
|
"City",
|
|
"State",
|
|
"Trade",
|
|
"Technician",
|
|
"Technician Phone",
|
|
"Total Jobs",
|
|
] as const;
|
|
|
|
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 (
|
|
<TableContainer
|
|
component={Paper}
|
|
variant="outlined"
|
|
sx={{
|
|
mt: 2,
|
|
mb: 2,
|
|
flex: "1 1 auto",
|
|
overflow: "auto",
|
|
borderRadius: 1.5,
|
|
fontSize: 13,
|
|
containerType: "inline-size",
|
|
}}
|
|
>
|
|
<Table
|
|
size="small"
|
|
stickyHeader
|
|
sx={{
|
|
minWidth: 2050,
|
|
tableLayout: "fixed",
|
|
"& .MuiTableCell-head": {
|
|
backgroundColor: "var(--color-bg-muted)",
|
|
color: "var(--color-foreground)",
|
|
fontSize: 10.5,
|
|
fontWeight: 700,
|
|
letterSpacing: "0.06em",
|
|
lineHeight: "24px",
|
|
px: 1.75,
|
|
py: 1.375,
|
|
textTransform: "uppercase",
|
|
whiteSpace: "nowrap",
|
|
},
|
|
"& .MuiTableCell-body": {
|
|
fontSize: 13,
|
|
px: 1.5,
|
|
py: 1,
|
|
height: 52,
|
|
lineHeight: 1.43,
|
|
whiteSpace: "nowrap",
|
|
},
|
|
"& .vendor-row-actions": { opacity: { xs: 1, md: 0 }, transition: "opacity 120ms ease" },
|
|
"& .MuiTableRow-root:hover .vendor-row-actions, & .MuiTableRow-root:focus-within .vendor-row-actions":
|
|
{ opacity: 1 },
|
|
}}
|
|
>
|
|
<colgroup>
|
|
{[190, 160, 220, 220, 140, 100, 190, 220, 170, 110, 120, 130].map((width, index) => (
|
|
<col key={`${width}-${index}`} style={{ width }} />
|
|
))}
|
|
</colgroup>
|
|
<TableHead>
|
|
<TableRow>
|
|
{TABLE_COLUMNS.map((column) => (
|
|
<TableCell key={column} align={column === "Total Jobs" ? "center" : "left"}>
|
|
{column}
|
|
</TableCell>
|
|
))}
|
|
<TableCell>Status</TableCell>
|
|
<TableCell align="right" aria-label="Vendor actions" />
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{isLoading ? (
|
|
<TableRow>
|
|
<TableCell colSpan={columnCount} className="py-10">
|
|
<Stack sx={{ width: "calc(100cqw - 24px)", alignItems: "center" }}>
|
|
<CircularProgress size={28} />
|
|
</Stack>
|
|
</TableCell>
|
|
</TableRow>
|
|
) : showEmptyRow ? (
|
|
<TableRow>
|
|
<TableCell colSpan={columnCount} className="py-10">
|
|
<Stack spacing={1} sx={{ width: "calc(100cqw - 24px)", alignItems: "center" }}>
|
|
<SearchOffOutlinedIcon sx={{ color: "text.secondary", fontSize: 32 }} />
|
|
<Text
|
|
variant="description"
|
|
className="font-semibold"
|
|
>{`No ${isActive ? "active" : "inactive"} companies found`}</Text>
|
|
<Text variant="caption" tone="muted">
|
|
{isFiltered
|
|
? "Try a different search, or add a new vendor."
|
|
: "Add a vendor to get started."}
|
|
</Text>
|
|
</Stack>
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
rows.map((row) => (
|
|
<VendorTableRow
|
|
key={String(row.id ?? `company-${row.companyId ?? ""}`)}
|
|
row={row}
|
|
onOpenDetail={onOpenDetail}
|
|
onOpenEdit={onOpenEdit}
|
|
/>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
);
|
|
}
|