Show vendor address and distance in dispatch modal

- Pass locationZip to vendor dropdown API for distance calculation
- Display vendor address and distance in miles (blue, bold)
- Vendors sorted by proximity when site has a zip code
- Include locationZip in WO API response mapping
This commit is contained in:
Adam Moussa 2026-04-17 11:56:15 -04:00
parent 2a4cd5da75
commit c09ac4460b
3 changed files with 34 additions and 10 deletions

View file

@ -23,6 +23,7 @@ export const getWorkOrderById = async (id) => {
title: wo.title ?? wo.workerOrderTitle ?? "",
location: wo.location ?? "",
locationId: wo.locationId != null ? String(wo.locationId) : "",
locationZip: wo.locationZip ?? "",
assignedTo: wo.assignee ?? wo.assignedTo ?? "",
dueDate: wo.dueDate ? wo.dueDate.split("T")[0] : "",
createdDate: wo.createdDate ? wo.createdDate.split("T")[0] : "",
@ -111,11 +112,13 @@ export const fetchDropdownOptions = async (category, parent) => {
return (await apiGet(url)) || [];
};
export const fetchVendorsDropdown = async (trade) => {
export const fetchVendorsDropdown = async (trade, siteZip) => {
const { get: apiGet } = await import("../../apiUtil");
let url = `${API_URL}/Vendor/Dropdown`;
if (trade) url += `?trade=${encodeURIComponent(trade)}`;
return (await apiGet(url)) || [];
const params = new URLSearchParams();
if (trade) params.append("trade", trade);
if (siteZip) params.append("siteZip", siteZip);
const qs = params.toString();
return (await apiGet(`${API_URL}/Vendor/Dropdown${qs ? `?${qs}` : ""}`)) || [];
};
export const dispatchToVendor = async (workOrderId, payload) => {

View file

@ -524,16 +524,33 @@
background: #e6f0ff;
}
.woDispatchVendorInfo {
display: flex;
flex-direction: column;
gap: 1px;
min-width: 0;
}
.woDispatchVendorName {
font-size: 13px;
font-weight: 500;
display: block;
}
.woDispatchVendorTrades {
.woDispatchVendorAddr {
font-size: 11px;
color: #666;
}
.woDispatchVendorMeta {
display: flex;
gap: 8px;
font-size: 11px;
color: #999;
display: block;
}
.woDispatchVendorDist {
font-weight: 600;
color: #007bff;
}
.woDispatchLoading {

View file

@ -171,7 +171,7 @@ function DispatchModal({ wo, onClose, onDispatched }) {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
fetchVendorsDropdown(wo.trade).then((data) => {
fetchVendorsDropdown(wo.trade, wo.locationZip).then((data) => {
setVendors(data);
setLoaded(true);
}).catch(() => setLoaded(true));
@ -228,9 +228,13 @@ function DispatchModal({ wo, onClose, onDispatched }) {
{filteredVendors.map((v) => (
<label key={v.id} className={`woDispatchVendorItem ${selectedIds.includes(v.id) ? "selected" : ""}`}>
<input type="checkbox" checked={selectedIds.includes(v.id)} onChange={() => toggleVendor(v.id)} />
<div>
<div className="woDispatchVendorInfo">
<span className="woDispatchVendorName">{v.companyName}</span>
{v.tradeSpecialties && <span className="woDispatchVendorTrades">{v.tradeSpecialties}</span>}
{v.address && <span className="woDispatchVendorAddr">{v.address}</span>}
<span className="woDispatchVendorMeta">
{v.tradeSpecialties && <span>{v.tradeSpecialties}</span>}
{v.distanceMiles != null && <span className="woDispatchVendorDist">{v.distanceMiles} mi</span>}
</span>
</div>
</label>
))}