diff --git a/src/app/(protected)/vendors/_components/vendors-table.tsx b/src/app/(protected)/vendors/_components/vendors-table.tsx
index e524630c..57e8a243 100644
--- a/src/app/(protected)/vendors/_components/vendors-table.tsx
+++ b/src/app/(protected)/vendors/_components/vendors-table.tsx
@@ -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) => {
diff --git a/src/domain/vendors/mappers/vendor-mapper.ts b/src/domain/vendors/mappers/vendor-mapper.ts
index 33a31223..321e4542 100644
--- a/src/domain/vendors/mappers/vendor-mapper.ts
+++ b/src/domain/vendors/mappers/vendor-mapper.ts
@@ -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"),
diff --git a/src/domain/vendors/types/vendor.ts b/src/domain/vendors/types/vendor.ts
index d4140afa..74e6519e 100644
--- a/src/domain/vendors/types/vendor.ts
+++ b/src/domain/vendors/types/vendor.ts
@@ -13,6 +13,7 @@ export interface VendorListItem {
googleMapsUrl: string;
notes: string;
totalJobs: number;
+ address: string;
city: string;
state: string;
tradeSpecialties: string;
diff --git a/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx b/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx
index a882b91f..cf38b5bb 100644
--- a/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx
+++ b/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx
@@ -33,6 +33,7 @@ const vendor: VendorListItem = {
googleMapsUrl: "",
notes: "",
totalJobs: 0,
+ address: "",
city: "",
state: "",
tradeSpecialties: "",
diff --git a/src/test/app/(protected)/vendors/vendors-list.test.tsx b/src/test/app/(protected)/vendors/vendors-list.test.tsx
index ae5a18d3..ec0b1389 100644
--- a/src/test/app/(protected)/vendors/vendors-list.test.tsx
+++ b/src/test/app/(protected)/vendors/vendors-list.test.tsx
@@ -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(, { 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(, { 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({