diff --git a/e2e/vendors/vendors.spec.ts b/e2e/vendors/vendors.spec.ts
index 0ce999d2..e5c8bcf3 100644
--- a/e2e/vendors/vendors.spec.ts
+++ b/e2e/vendors/vendors.spec.ts
@@ -306,10 +306,17 @@ test.describe("Vendor directory prototype parity", () => {
test("covers status tabs, global counts, search, facets, table fields, and filtered footer", async ({
page,
}) => {
+ await page.setViewportSize({ width: 1440, height: 1000 });
const state = await mockVendorApi(page);
await page.goto("/vendors");
- await expect(page.getByRole("heading", { name: "Vendors / Technicians" })).toBeVisible();
+ const heading = page.getByRole("heading", { name: "Vendors / Technicians" });
+ await expect(heading).toBeVisible();
+ await expect(heading).toHaveCSS("font-family", /Montserrat/);
+ await expect(heading).toHaveCSS("font-size", "30px");
+ await expect(heading).toHaveCSS("font-weight", "800");
+ await expect(heading).toHaveCSS("line-height", "33px");
+ await expect(heading).toHaveCSS("letter-spacing", "-0.6px");
await expect(page.getByText("3 technicians · 2 active")).toBeVisible();
await expect(page.getByRole("tab", { name: "Active (2)" })).toHaveAttribute(
"aria-selected",
@@ -327,6 +334,16 @@ test.describe("Vendor directory prototype parity", () => {
await expect(page.getByRole("columnheader", { name: heading })).toBeVisible();
}
await expect(page.getByRole("columnheader", { name: "Status" })).toBeVisible();
+ const activeTabBox = await page.getByRole("tab", { name: "Active (2)" }).boundingBox();
+ expect(activeTabBox).toMatchObject({ x: 256, y: 159.5, height: 36 });
+ const tableContainerBox = await page.locator("table").locator("..").boundingBox();
+ expect(tableContainerBox).toMatchObject({ x: 260, y: 212.5, width: 1164 });
+ expect(tableContainerBox?.height).toBeCloseTo(738, 0);
+ expect((await page.locator("table").boundingBox())?.width).toBe(1550);
+ expect((await page.getByRole("columnheader", { name: "Company" }).boundingBox())?.height).toBe(
+ 47,
+ );
+ expect((await page.locator("tbody tr").first().boundingBox())?.height).toBe(52);
await expect(page.getByText("Gateway Plumbing")).toBeVisible();
await expect(page.getByText("+1")).toBeVisible();
await expect(page.getByText("2 active technicians")).toBeVisible();
diff --git a/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx b/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx
index ff5352ec..a8203ecb 100644
--- a/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx
+++ b/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx
@@ -31,6 +31,7 @@ export function VendorsListToolbar({
return (
}
+ icon={}
onClick={onOpenFilters}
aria-label="Open vendor filters"
badge={appliedFilterCount}
+ className="w-[88.609375px] text-[13px]"
>
Filters
- } onClick={onAddVendor}>
+ }
+ onClick={onAddVendor}
+ className="w-[115.546875px] border-0 px-3 text-[13px]"
+ >
Add Vendor
{isFetching && !isLoading && (
@@ -131,14 +139,18 @@ export function VendorsListFooter({
sx={{
alignItems: { xs: "stretch", sm: "center" },
justifyContent: "space-between",
+ minHeight: 33.5,
+ mx: -2,
+ px: 2,
+ backgroundColor: "var(--color-bg-surface)",
}}
>
-
+
{totalCount} {isActive ? "active" : "inactive"}{" "}
{totalCount === 1 ? "technician" : "technicians"}
{isFiltered && " · filtered"}
-
+
{activeCount} active · {inactiveCount} inactive
{totalCount > pageSize && (
diff --git a/src/app/(protected)/vendors/_components/vendors-table.tsx b/src/app/(protected)/vendors/_components/vendors-table.tsx
index 1e47a58a..db588cff 100644
--- a/src/app/(protected)/vendors/_components/vendors-table.tsx
+++ b/src/app/(protected)/vendors/_components/vendors-table.tsx
@@ -4,7 +4,7 @@ import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
import {
Avatar,
- Chip,
+ Box,
CircularProgress,
IconButton,
Link,
@@ -50,6 +50,23 @@ function stopPropagation(event: MouseEvent): void {
event.stopPropagation();
}
+function VendorStatus({ isActive }: { isActive: boolean }) {
+ return (
+
+
+ {isActive ? "Active" : "Inactive"}
+
+ );
+}
+
interface VendorTableRowProps {
row: VendorListItem;
onOpenDetail: (row: VendorListItem) => void;
@@ -82,12 +99,12 @@ function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps)
onKeyDown={handleKeyDown}
>
-
+
{row.companyName || "—"}
-
+
{getInitials(row.contactName)}
- {row.contactName || "—"}
+
+ {row.contactName || "—"}
+
- {locationText}
-
+ {locationText}
+
{primaryTrade === "—" ? (
"—"
) : (
{primaryTrade}
- {extraTrades > 0 && }
+ {extraTrades > 0 && (
+
+ +{extraTrades}
+
+ )}
)}
-
+
{row.phone ? (
-
+
{row.phone}
) : (
@@ -125,7 +163,12 @@ function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps)
{row.email ? (
-
+
{row.email}
) : (
@@ -134,11 +177,7 @@ function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps)
{row.totalJobs ?? 0}
-
+
+
+
+
+
+
+
+
+
+
+
+
{TABLE_COLUMNS.map((column) => (
diff --git a/src/app/(protected)/vendors/index.tsx b/src/app/(protected)/vendors/index.tsx
index c81ed17f..48447ad6 100644
--- a/src/app/(protected)/vendors/index.tsx
+++ b/src/app/(protected)/vendors/index.tsx
@@ -59,6 +59,7 @@ function VendorStatusTabs({
flexShrink: 0,
borderBottom: (theme) => `1px solid ${theme.palette.divider}`,
backgroundColor: "var(--color-bg-surface)",
+ mx: -2,
px: 1.5,
"& .MuiTab-root": {
minHeight: 36,
@@ -68,6 +69,7 @@ function VendorStatusTabs({
fontWeight: 500,
textTransform: "none",
},
+ "& .MuiTab-root:first-of-type": { width: 102.84375 },
"& .Mui-selected": { fontWeight: 600 },
"& .MuiTabs-indicator": { height: 2 },
}}
@@ -139,7 +141,7 @@ export default function VendorsListPage() {
const appliedChips = describeChips(filters.appliedFilters, jobBucketLabels);
return (
-
+
{title}
-
+
{subtitle}