Technician (optional)
-
- }
- searchPlaceholder="Search technician…"
- options={technicianOptions.map((option) => ({
- key: option.key,
- label: option.name,
- }))}
- onPick={onPick}
- />
- {companyVendorForAdd != null && (
+ {frozenCompany != null ? (
+
+ {frozenCompany.contactName?.trim() || tech || "—"}
+
+ ) : (
+
+ }
+ searchPlaceholder="Search technician…"
+ options={technicianOptions.map((option) => ({
+ key: option.key,
+ label: option.name,
+ }))}
+ onPick={onPick}
+ />
+ )}
+ {frozenCompany == null && companyVendorForAdd != null && (
({
- name: contact.name ?? "",
- phone: contact.phone ?? "",
- }))}
+ additionalContacts={effectiveAdditionalContacts(row)}
sites={sites}
q={search}
viewOnly={viewOnly}
diff --git a/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts b/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts
index 07328e0c..01025099 100644
--- a/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts
+++ b/src/app/(protected)/workorders/_hooks/use-comp-doc-dialog.ts
@@ -105,8 +105,9 @@ export function useCompDocDialog({
setEditAddress(value);
};
+ const company = effectiveCompany(row);
const amazonRep = effectivePoc(row).contacts[0]?.name || "";
- const seaRep = effectiveCompany(row).contactName || "";
+ const seaRep = company.contactName || "";
const cfg = DOC_STATE_CFG[docState];
const isNotRequired = docState === "not-required";
const isGenerated = docState === "generated";
@@ -120,6 +121,9 @@ export function useCompDocDialog({
dateCompleted: editDateCompleted,
amazonRep,
seaRep,
+ siteCode: site.code,
+ tech: company.contactName || "",
+ company: company.name || "",
};
const previewSite = useMemo(
diff --git a/src/domain/work-orders/mappers/work-order-table-row-mapper.ts b/src/domain/work-orders/mappers/work-order-table-row-mapper.ts
index d7203502..d1d4afc3 100644
--- a/src/domain/work-orders/mappers/work-order-table-row-mapper.ts
+++ b/src/domain/work-orders/mappers/work-order-table-row-mapper.ts
@@ -314,6 +314,7 @@ function readFrozenPoc(item: Record): WorkOrderFrozenPoc | unde
? rawContacts.map(asRecord).map((contact) => ({
name: readString(contact, "name", "Name"),
phone: readString(contact, "phone", "Phone"),
+ notes: readString(contact, "notes", "Notes"),
}))
: [];
return {
diff --git a/src/domain/work-orders/types/completion-doc.ts b/src/domain/work-orders/types/completion-doc.ts
index 0c6377f5..e68685a4 100644
--- a/src/domain/work-orders/types/completion-doc.ts
+++ b/src/domain/work-orders/types/completion-doc.ts
@@ -14,6 +14,9 @@ export interface DocFields {
dateCompleted: string;
amazonRep: string;
seaRep: string;
+ siteCode: string;
+ tech: string;
+ company: string;
}
export type DocState = "draft" | "generated" | "not-required";
@@ -31,12 +34,15 @@ export interface CompletionDocStateConfig {
}
export interface CompDocSiteInfo {
+ code: string;
street: string;
city: string;
state: string;
zip: string;
label: string;
fullAddress: string;
+ tech: string;
+ company: string;
}
export type CompDocSaveStatus = Exclude;
diff --git a/src/domain/work-orders/types/work-order-table-row.ts b/src/domain/work-orders/types/work-order-table-row.ts
index ad6bb06e..d362f931 100644
--- a/src/domain/work-orders/types/work-order-table-row.ts
+++ b/src/domain/work-orders/types/work-order-table-row.ts
@@ -38,7 +38,7 @@ export type WorkOrderFrozenCompany = {
};
export type WorkOrderFrozenPoc = {
- contacts: { name?: string; phone?: string }[];
+ contacts: { name?: string; phone?: string; notes?: string }[];
notes?: string;
};
diff --git a/src/domain/work-orders/utils/completion-doc-html.ts b/src/domain/work-orders/utils/completion-doc-html.ts
index 87d3b30c..a1114090 100644
--- a/src/domain/work-orders/utils/completion-doc-html.ts
+++ b/src/domain/work-orders/utils/completion-doc-html.ts
@@ -75,11 +75,11 @@ export function buildDocHtml(
${esc(serviceTitle(row).toUpperCase())}
WO# ${esc(row.woNumber)}
- Site ID — ${esc(row.site)}
- Amazon.com Services LLC ${esc(row.site)}
+ Site ID — ${esc(fields.siteCode)}
+ Amazon.com Services LLC ${esc(fields.siteCode)}
${esc(fields.street)},
${esc(cityStateZip)}
- Vendor / Technician: ${esc(row.tech || "—")}${row.company ? ` · ${esc(row.company)}` : ""}
+ Vendor / Technician: ${esc(fields.tech || "—")}${fields.company ? ` · ${esc(fields.company)}` : ""}
${completedBlock}
${esc(proc.heading)}:
diff --git a/src/domain/work-orders/utils/completion-doc-site.ts b/src/domain/work-orders/utils/completion-doc-site.ts
index 5ff4d52b..1546fdcc 100644
--- a/src/domain/work-orders/utils/completion-doc-site.ts
+++ b/src/domain/work-orders/utils/completion-doc-site.ts
@@ -1,20 +1,53 @@
import type { Location } from "@/domain/locations/types/location";
import type { CompDocSiteInfo } from "@/domain/work-orders/types/completion-doc";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
-import { effectiveSite } from "@/domain/work-orders/utils/effective-completion-snapshot";
+import {
+ effectiveCompany,
+ effectiveSite,
+} from "@/domain/work-orders/utils/effective-completion-snapshot";
+
+type CompDocAddress = Pick<
+ CompDocSiteInfo,
+ "street" | "city" | "state" | "zip" | "label" | "fullAddress"
+>;
+
+type CompDocIdentityRow = Pick &
+ Partial>;
function buildLabel(city?: string, state?: string): string {
return [city, state].filter(Boolean).join(", ");
}
-function toCompDocSite(street: string, city: string, state: string, zip: string): CompDocSiteInfo {
+function toCompDocAddress(
+ street: string,
+ city: string,
+ state: string,
+ zip: string,
+): CompDocAddress {
const label = buildLabel(city, state);
const fullAddress = [street, label, zip].filter(Boolean).join(", ") || "—";
return { street, city, state, zip, label, fullAddress };
}
-function resolveLiveLocationSite(location: Location): CompDocSiteInfo {
- return toCompDocSite(
+function withCompDocIdentity(address: CompDocAddress, row: CompDocIdentityRow): CompDocSiteInfo {
+ const frozen = effectiveSite(row);
+ const company = effectiveCompany({
+ status: row.status,
+ company: row.company ?? "",
+ tech: row.tech ?? "",
+ techPhone: row.techPhone ?? "",
+ frozenCompany: row.frozenCompany,
+ });
+ return {
+ ...address,
+ code: frozen?.code ?? row.site,
+ tech: company.contactName ?? "",
+ company: company.name ?? "",
+ };
+}
+
+function resolveLiveLocationSite(location: Location): CompDocAddress {
+ return toCompDocAddress(
location.address ?? "",
location.city ?? "",
location.state ?? "",
@@ -22,30 +55,33 @@ function resolveLiveLocationSite(location: Location): CompDocSiteInfo {
);
}
-function resolveRowLocationSite(row: Pick): CompDocSiteInfo {
+function resolveRowLocationSite(row: Pick): CompDocAddress {
const locationParts = row.location.split(",").map((part) => part.trim());
const city = locationParts[0] ?? "";
const state = locationParts[1] ?? "";
const label = buildLabel(city, state);
const street = row.location && !label ? row.location : "";
- return toCompDocSite(street, city, state, "");
+ return toCompDocAddress(street, city, state, "");
}
export function resolveCompDocSite(
- row: Pick,
+ row: CompDocIdentityRow,
location?: Location | null,
): CompDocSiteInfo {
const frozen = effectiveSite(row);
if (frozen != null) {
- return toCompDocSite(
- frozen.address ?? "",
- frozen.city ?? "",
- frozen.state ?? "",
- frozen.zip ?? "",
+ return withCompDocIdentity(
+ toCompDocAddress(
+ frozen.address ?? "",
+ frozen.city ?? "",
+ frozen.state ?? "",
+ frozen.zip ?? "",
+ ),
+ row,
);
}
if (location?.address || location?.city) {
- return resolveLiveLocationSite(location);
+ return withCompDocIdentity(resolveLiveLocationSite(location), row);
}
- return resolveRowLocationSite(row);
+ return withCompDocIdentity(resolveRowLocationSite(row), row);
}
diff --git a/src/domain/work-orders/utils/effective-completion-snapshot.ts b/src/domain/work-orders/utils/effective-completion-snapshot.ts
index 59dc9827..b552b39c 100644
--- a/src/domain/work-orders/utils/effective-completion-snapshot.ts
+++ b/src/domain/work-orders/utils/effective-completion-snapshot.ts
@@ -1,3 +1,4 @@
+import type { WorkOrderAdditionalContact } from "@/domain/work-orders/types/work-order-additional-contact";
import type {
WorkOrderFrozenCompany,
WorkOrderFrozenPoc,
@@ -48,8 +49,20 @@ export function effectivePoc(row: PocSnapshotRow): WorkOrderFrozenPoc {
...(row.additionalContacts ?? []).map((contact) => ({
name: contact.name,
phone: contact.phone,
+ notes: contact.notes,
})),
],
notes: row.pocNotes,
};
}
+
+export function effectiveAdditionalContacts(row: PocSnapshotRow): WorkOrderAdditionalContact[] {
+ if (row.status === "Completed" && row.frozenPoc != null) {
+ return row.frozenPoc.contacts.slice(1).map((contact) => ({
+ name: contact.name ?? "",
+ phone: contact.phone ?? "",
+ notes: contact.notes ?? "",
+ }));
+ }
+ return row.additionalContacts ?? [];
+}
diff --git a/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx b/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx
index afe6f231..413eb2f1 100644
--- a/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx
+++ b/src/test/app/(protected)/workorders/vendor-dialog-frozen-company.test.tsx
@@ -1,5 +1,6 @@
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
+import { VendorDialogBody } from "@/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-body";
import { VendorDialogCompanyCard } from "@/app/(protected)/workorders/_components/list/table/cells/vendor-dialog-company-card";
import type { VendorDropdownItem } from "@/domain/work-orders/types/work-order";
@@ -48,4 +49,42 @@ describe("VendorDialogCompanyCard completion snapshot", () => {
expect(screen.getByText("200 Vendor Ave, Chicago, IL, 60602")).toBeInTheDocument();
expect(screen.queryByText("900 Live St")).not.toBeInTheDocument();
});
+
+ it("shows the frozen technician instead of a renamed live roster contact", () => {
+ render(
+ undefined}
+ onPick={() => undefined}
+ onPhoneChange={() => undefined}
+ onNotesChange={() => undefined}
+ onTechnicianCreated={() => undefined}
+ />,
+ );
+
+ expect(screen.getAllByText("Frozen Tech").length).toBeGreaterThan(0);
+ expect(screen.queryByText("Live Contact")).not.toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: /\+ add technician/i })).not.toBeInTheDocument();
+ });
});
diff --git a/src/test/domain/work-orders/utils/completion-doc-site.test.ts b/src/test/domain/work-orders/utils/completion-doc-site.test.ts
index 8abeae5d..4a2cb2f9 100644
--- a/src/test/domain/work-orders/utils/completion-doc-site.test.ts
+++ b/src/test/domain/work-orders/utils/completion-doc-site.test.ts
@@ -8,12 +8,16 @@ describe("resolveCompDocSite", () => {
site: "BK5",
location: "Live City, WI",
status: "Completed",
+ company: "Live Co",
+ tech: "Live Tech",
frozenSite: {
+ code: "FROZEN",
address: "100 Frozen Ave",
city: "Chicago",
state: "IL",
zip: "60601",
},
+ frozenCompany: { name: "Frozen Co", contactName: "Frozen Tech" },
},
{
address: "900 Live St",
@@ -24,12 +28,15 @@ describe("resolveCompDocSite", () => {
);
expect(result).toEqual({
+ code: "FROZEN",
street: "100 Frozen Ave",
city: "Chicago",
state: "IL",
zip: "60601",
label: "Chicago, IL",
fullAddress: "100 Frozen Ave, Chicago, IL, 60601",
+ tech: "Frozen Tech",
+ company: "Frozen Co",
});
});
diff --git a/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts b/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts
index 688e886f..5044a09a 100644
--- a/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts
+++ b/src/test/domain/work-orders/utils/effective-completion-snapshot.test.ts
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import {
+ effectiveAdditionalContacts,
effectiveCompany,
effectivePoc,
effectiveSite,
@@ -61,15 +62,42 @@ describe("effective completion snapshot", () => {
pocName: "Live POC",
pocPhone: "222",
pocNotes: "live",
- additionalContacts: [{ name: "Backup", phone: "333" }],
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
}),
).toEqual({
contacts: [
{ name: "Live POC", phone: "222" },
- { name: "Backup", phone: "333" },
+ { name: "Backup", phone: "333", notes: "gate hours" },
],
notes: "live",
});
+ expect(
+ effectiveAdditionalContacts({
+ status: "Scheduled",
+ pocName: "Live POC",
+ pocPhone: "222",
+ pocNotes: "live",
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
+ }),
+ ).toEqual([{ name: "Backup", phone: "333", notes: "gate hours" }]);
+ });
+
+ it("keeps extra-contact notes on the open work-order path", () => {
+ expect(
+ effectiveAdditionalContacts({
+ status: "Scheduled",
+ pocName: "Primary",
+ pocPhone: "111",
+ pocNotes: "",
+ additionalContacts: [{ name: "Backup", phone: "333", notes: "gate hours" }],
+ frozenPoc: {
+ contacts: [
+ { name: "Frozen", phone: "000" },
+ { name: "Frozen Extra", phone: "999", notes: "ignored while open" },
+ ],
+ },
+ }),
+ ).toEqual([{ name: "Backup", phone: "333", notes: "gate hours" }]);
});
it("treats missing additional contacts as an empty live contact list", () => {