From 88216520d87e5e51bbb7cf63f11406b1079c8928 Mon Sep 17 00:00:00 2001 From: Arthur Bassi Date: Fri, 7 Aug 2026 11:12:45 -0300 Subject: [PATCH] fix(vendors): align Vendors list UI with design specs (SH-219) (#92) * docs(work-orders): add pending BE contract checklist for design parity gaps * fix(vendors): align list header, Total Jobs, and Filters with design (SH-219) * test(vendors): align e2e subtitle with SH-219 technicians copy Co-authored-by: Cursor --------- Co-authored-by: Cursor --- docs/work-orders/pending-be-contract.md | 58 ++++++++++++++ e2e/vendors/vendors.spec.ts | 2 +- .../_components/vendors-list-toolbar.tsx | 78 ++++++++----------- .../vendors/_components/vendors-table.tsx | 8 +- 4 files changed, 96 insertions(+), 50 deletions(-) create mode 100644 docs/work-orders/pending-be-contract.md diff --git a/docs/work-orders/pending-be-contract.md b/docs/work-orders/pending-be-contract.md new file mode 100644 index 00000000..a3a92f53 --- /dev/null +++ b/docs/work-orders/pending-be-contract.md @@ -0,0 +1,58 @@ +# Pending BE contract (WO design parity gaps) + +Local checklist for FE branches off `dev`. Confirm with backend before shipping PR3/PR5 to production. Until confirmed, FE may use typed clients + mocks. + +## Board row / PATCH + +| Field | Needed by | Notes | +| -------------------------------------------------------- | ------------- | ---------------------------------------------------------------------- | +| `completedDate` on GET `/board` and `/board/search` rows | PR1, PR4 | PATCH `field: "completedDate"` expected; confirm GET includes ISO date | +| `severity` (`1`–`5` \| `null`) on row, create, PATCH | PR2, PR5 | Required for Emergency/Reactive; PATCH `field: "severity"` | +| `upliftSummary` on row | PR3, PR4, PR5 | `{ hasUplift, pendingCount, primaryStatus? }` | +| `hasPendingUplift` on row/detail | PR4 | Prefer server 422 on Completed / completion-doc when pending | +| `additionalContacts[]` | PR6 | `{ name, phone, notes? }[]` on create/detail/PATCH | +| `isAddOn` | PR5 (filter) | Indicator, not a WO type (SH-126) | + +## Proposed WO-scoped uplift endpoints (confirm vs NTE `uplifts` queue) + +| Method | Path | Body | +| ------ | ------------------------------------------- | --------------------------------- | +| GET | `workorders/{id}/uplifts` | — | +| POST | `workorders/{id}/uplifts` | `{ amount, notes, attachments? }` | +| POST | `workorders/{id}/uplifts/{upliftId}/cancel` | — | +| POST | `workorders/{id}/uplifts/{upliftId}/revoke` | `{ reason? }` | + +Statuses (design): `pending` \| `approved` \| `auto_approved` \| `rejected` \| `cancelled` \| `revoked`. + +## Search query params (`GET workorders/board/search`) + +| Param | Meaning | +| ------------------------------ | ------------------------- | +| `severities` | multi `1`–`5` | +| `rescheduled` / `carriedOver` | boolean (≥2 counters) | +| `addOn` | boolean (`isAddOn`) | +| `flagColors` | multi `#RRGGBB` | +| `internalOnly` | WO# starts with `SH` | +| `avetaOnly` | if `avetaRequired` exists | +| `hasUplift` + `upliftStatuses` | uplift filters | + +Already documented in `board-search-api.md`: datePreset, sites, types, overdue, dispatchers, statuses, pmTypes, vendorIds, docStatuses. + +## Comments + +| Field | Needed by | +| ---------------------------------------- | --------- | +| POST `{ text, mentions?: string[] }` | PR9 | +| Response includes mentions for highlight | PR9 | + +## Vendor + +| Item | Needed by | +| -------------------------------------------------------------- | --------- | +| Create technician under company (confirm existing vendors API) | PR7 | + +## Optional + +| Item | Needed by | +| ----------------------------------- | -------------------------------- | +| 422 on duplicate WO number (global) | PR8 (FE dialog works without it) | diff --git a/e2e/vendors/vendors.spec.ts b/e2e/vendors/vendors.spec.ts index 8bbfdfb8..fb46f610 100644 --- a/e2e/vendors/vendors.spec.ts +++ b/e2e/vendors/vendors.spec.ts @@ -310,7 +310,7 @@ test.describe("Vendor directory prototype parity", () => { await page.goto("/vendors"); await expect(page.getByRole("heading", { name: "Vendors / Technicians" })).toBeVisible(); - await expect(page.getByText("3 vendor entries · 2 active")).toBeVisible(); + await expect(page.getByText("3 technicians · 2 active")).toBeVisible(); await expect(page.getByRole("tab", { name: "Active (2)" })).toHaveAttribute( "aria-selected", "true", diff --git a/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx b/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx index e3e7cfa5..a6ee3797 100644 --- a/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx +++ b/src/app/(protected)/vendors/_components/vendors-list-toolbar.tsx @@ -1,4 +1,3 @@ -import FilterListIcon from "@mui/icons-material/FilterList"; import { Button, Chip, @@ -8,6 +7,9 @@ import { TextField, Typography, } from "@mui/material"; +import { SlidersHorizontal } from "lucide-react"; +import { PageHeader } from "@/components/ui/page-header"; +import { ToolbarButton } from "@/components/ui/toolbar-button"; interface VendorsListToolbarProps { globalCount: number; @@ -33,52 +35,34 @@ export function VendorsListToolbar({ onAddVendor, }: VendorsListToolbarProps) { return ( - - - - Vendors / Technicians - - - {globalCount} vendor entries · {activeCount} active - - {isFetching && !isLoading && } - - - - onSearchChange(e.target.value)} - placeholder="Search name, company, trade..." - className="min-w-[260px]" - /> - - - - + + onSearchChange(e.target.value)} + placeholder="Search name, company, trade..." + className="min-w-[260px]" + /> + } + onClick={onOpenFilters} + aria-label="Open vendor filters" + badge={appliedFilterCount} + > + Filters + + + {Boolean(isFetching && !isLoading) && } + + } + /> ); } diff --git a/src/app/(protected)/vendors/_components/vendors-table.tsx b/src/app/(protected)/vendors/_components/vendors-table.tsx index 802fff8a..5296c5f6 100644 --- a/src/app/(protected)/vendors/_components/vendors-table.tsx +++ b/src/app/(protected)/vendors/_components/vendors-table.tsx @@ -134,7 +134,7 @@ function VendorTableRow({ row, onOpenDetail, onOpenDeactivation }: VendorTableRo "—" )} - {row.totalJobs ?? 0} + {row.totalJobs ?? 0} ( {column}