fix(vendors): show free-text address in the list Location column

The Location column rendered City/State only, so a vendor whose location lives
in the free-text address field showed an em dash while the detail drawer, which
already composes address with city, state and zip, showed the value.

Carry address through the list item type and mapper, which previously dropped
it, and fall back to it in the Location cell when City/State are blank.
Structured City/State still wins when both are present.
This commit is contained in:
Alexandre Brandizzi 2026-08-25 13:33:53 -03:00
parent 11488b484d
commit cd3633e2b4
5 changed files with 50 additions and 1 deletions

View file

@ -61,7 +61,10 @@ function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps)
const trades = splitTrades(row.tradeSpecialties);
const primaryTrade = trades[0] ?? "—";
const extraTrades = trades.length > 1 ? trades.length - 1 : 0;
const locationText = [row.city, row.state].filter(Boolean).join(", ") || "—";
// Vendors captured through the roster form keep their location in the free-text
// address field, so fall back to it the way the detail drawer already does.
const locationText =
[row.city, row.state].filter(Boolean).join(", ").trim() || (row.address ?? "").trim() || "—";
const rowKey = String(row.id ?? `company-${row.companyId ?? ""}`);
const handleKeyDown = (event: KeyboardEvent) => {

View file

@ -119,6 +119,7 @@ export function mapVendorListItem(raw: unknown): VendorListItem {
googleMapsUrl: readString(item, "googleMapsUrl", "GoogleMapsUrl"),
notes: readString(item, "notes", "Notes"),
totalJobs: readNumber(item, "totalJobs", "TotalJobs"),
address: readString(item, "address", "Address"),
city: readString(item, "city", "City"),
state: readString(item, "state", "State"),
tradeSpecialties: readString(item, "tradeSpecialties", "TradeSpecialties"),

View file

@ -13,6 +13,7 @@ export interface VendorListItem {
googleMapsUrl: string;
notes: string;
totalJobs: number;
address: string;
city: string;
state: string;
tradeSpecialties: string;

View file

@ -33,6 +33,7 @@ const vendor: VendorListItem = {
googleMapsUrl: "",
notes: "",
totalJobs: 0,
address: "",
city: "",
state: "",
tradeSpecialties: "",

View file

@ -40,6 +40,7 @@ const activeVendor: VendorListItem = {
googleMapsUrl: "",
notes: "",
totalJobs: 0,
address: "",
city: "St. Louis",
state: "MO",
tradeSpecialties: "Backflow Preventers",
@ -152,6 +153,48 @@ describe("VendorsListPage", () => {
expect(screen.queryByText(/3 active technicians/)).not.toBeInTheDocument();
});
it("falls back to the free-text address when city and state are blank", () => {
setupDefaults();
const addressOnly = {
...activeVendor,
id: 900,
companyName: "Vinewood LLC",
address: "Santa Monica, CA",
city: "",
state: "",
};
useVendorsList.mockImplementation((params: { isActive?: boolean; pageSize?: number }) => {
if (params.pageSize === 1) return result([], params.isActive ? 1 : 0);
return result([addressOnly], 1);
});
renderWithProviders(<VendorsListPage />, { route: "/vendors", withAuth: false });
expect(screen.getByText("Santa Monica, CA")).toBeInTheDocument();
});
it("prefers structured city and state over the free-text address", () => {
setupDefaults();
const both = {
...activeVendor,
id: 901,
address: "Santa Monica, CA",
city: "St. Louis",
state: "MO",
};
useVendorsList.mockImplementation((params: { isActive?: boolean; pageSize?: number }) => {
if (params.pageSize === 1) return result([], params.isActive ? 1 : 0);
return result([both], 1);
});
renderWithProviders(<VendorsListPage />, { route: "/vendors", withAuth: false });
expect(screen.getByText("St. Louis, MO")).toBeInTheDocument();
expect(screen.queryByText("Santa Monica, CA")).not.toBeInTheDocument();
});
it("opens the detail drawer when a row is clicked or activated by keyboard", async () => {
setupDefaults();
useVendorCompanyRoster.mockReturnValue({