From 2844d97a36e2c1be24548336a0f423cea3c6294c Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Thu, 10 Sep 2026 16:36:09 -0300 Subject: [PATCH] feat(work-orders): show the vendor location map in the Vendor dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last of SH-271's five acceptance bullets. The Work Order Vendor dialog gets one composed address line from the vendor dropdown payload, not the structured parts the form and detail drawer hold, so the preview takes the line directly — it is saved data either way, and the completeness rule exists to stop a map of half-typed input, not to reject a stored address. The dialog's hand-built maps URL now goes through the shared helper, so the link and the preview cannot drift apart. --- .../cells/vendor-dialog-company-card.tsx | 28 +++++++++++++------ src/components/common/address-map-preview.tsx | 25 +++++++++++++---- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card.tsx b/src/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card.tsx index b14f8a4a..e7bea00d 100644 --- a/src/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card.tsx +++ b/src/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card.tsx @@ -1,4 +1,6 @@ import { ExternalLink, Mail, MapPin } from "lucide-react"; +import { AddressMapPreview } from "@/components/common/address-map-preview"; +import { mapsSearchUrl } from "@/lib/address/vendor-address"; import { Detail } from "@/app/(protected)/workorders/_components/wizard/wizard-labels"; import type { VendorDropdownItem } from "@/domain/work-orders/types/work-order"; import { formatVendorDistance } from "@/domain/work-orders/utils/vendor-dropdown-label"; @@ -33,15 +35,23 @@ export function VendorDialogCompanyCard({ company, vendor }: VendorDialogCompany {Boolean(vendor?.address) && } {Boolean(email) && } {vendor != null && Boolean(vendor.address) && ( - - Open in Google Maps - + <> + + Open in Google Maps + + {/* + SH-271: the vendor dropdown payload carries one composed address + line, not the structured parts the form and detail drawer hold, so + the preview is given the line directly. It is saved data either way. + */} + + )} ); diff --git a/src/components/common/address-map-preview.tsx b/src/components/common/address-map-preview.tsx index a9b14ad9..91eee654 100644 --- a/src/components/common/address-map-preview.tsx +++ b/src/components/common/address-map-preview.tsx @@ -7,7 +7,14 @@ import { } from "@/lib/address/vendor-address"; type AddressMapPreviewProps = { - address: Partial; + /** Structured parts, as held by a form or a detail view. */ + address?: Partial; + /** + * An already-composed address line, for callers that hold saved data rather + * than live input — the Work Order vendor dialog gets one string from the + * vendor dropdown payload. Ignored when `address` is given. + */ + query?: string; height?: number; }; @@ -21,10 +28,16 @@ type AddressMapPreviewProps = { * Renders nothing until Street, City and State are all present — a preview of a * half-typed address points somewhere misleading, which is worse than no map. */ -export function AddressMapPreview({ address, height = 160 }: AddressMapPreviewProps) { - if (!hasCompleteAddress(address)) return null; - - const query = buildAddressQuery(address); +export function AddressMapPreview({ + address, + query: rawQuery, + height = 160, +}: AddressMapPreviewProps) { + // Structured input is held to the completeness rule; a pre-composed line is + // saved data and only has to be non-empty. + const query = address ? buildAddressQuery(address) : (rawQuery ?? "").trim(); + const renderable = address ? hasCompleteAddress(address) : Boolean(query); + if (!renderable) return null; return (