mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 18:33:12 +00:00
119 lines
5.1 KiB
TypeScript
119 lines
5.1 KiB
TypeScript
import type { CompDocData, DocFields } from "@/domain/work-orders/types/completion-doc";
|
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
|
import { procedureFor, SAFETY_BULLETS } from "@/domain/work-orders/utils/completion-doc-procedures";
|
|
import { serviceTitle } from "@/domain/work-orders/utils/completion-doc-service-title";
|
|
|
|
export const SEAHAVEN_HEADER = {
|
|
name: "Seahaven Industries, Inc.",
|
|
address: "710 Koehler Avenue, Ronkonkoma, NY 11779",
|
|
phone: "(631) 776-5102",
|
|
fax: "(631) 677-3934",
|
|
email: "work-orders@seahavenind.com",
|
|
};
|
|
|
|
function esc(s: string): string {
|
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
}
|
|
|
|
export function buildDocHtml(
|
|
row: Pick<WorkOrderTableRow, "woNumber" | "site" | "pm" | "tech" | "company">,
|
|
data: CompDocData,
|
|
fields: DocFields,
|
|
): string {
|
|
const cityStateZip =
|
|
[fields.city, fields.state].filter(Boolean).join(", ") + (fields.zip ? ` ${fields.zip}` : "");
|
|
const proc = procedureFor(row.pm);
|
|
const completedBlock = `<h3 style="margin:16px 0 4px;">Completion Details:</h3>
|
|
<p style="margin:2px 0;"><b>Date Work Completed:</b> ${esc(fields.dateCompleted || "—")}</p>
|
|
<p style="margin:2px 0;"><b>Amazon Representative:</b> ${esc(fields.amazonRep || "—")}</p>
|
|
<p style="margin:2px 0;"><b>Seahaven Representative:</b> ${esc(fields.seaRep || "—")}</p>`;
|
|
const procHtml = proc.steps
|
|
.map((st) => {
|
|
const sub = st.sub
|
|
? `<ol type="a" style="margin:4px 0 0 0;padding-left:22px;">${st.sub.map((x) => `<li style="margin:2px 0;">${esc(x)}</li>`).join("")}</ol>`
|
|
: "";
|
|
return `<li style="margin:6px 0;">${esc(st.text)}${sub}</li>`;
|
|
})
|
|
.join("");
|
|
const safety = SAFETY_BULLETS.map((b) => `<li style="margin:4px 0;">${esc(b)}</li>`).join("");
|
|
const notesBlock = data.notes
|
|
? `<h3 style="margin:16px 0 4px;">PM Comments:</h3><p style="margin:0;">${esc(data.notes)}</p>`
|
|
: "";
|
|
|
|
return `<!doctype html><html><head><meta charset="utf-8"><title>WO ${esc(row.woNumber)} — Completion</title>
|
|
<style>
|
|
@page { margin: 28px; }
|
|
* { box-sizing: border-box; }
|
|
body { font-family: 'DM Sans', system-ui, sans-serif; color: #161E38; font-size: 13px; line-height: 1.5; margin: 0; }
|
|
.band-top { height: 14px; background: linear-gradient(100deg,#27AAE1,#1C75BC); }
|
|
.band-bottom { height: 26px; margin-top: 28px; background: linear-gradient(100deg,#1C75BC,#27AAE1); clip-path: polygon(0 40%,100% 0,100% 100%,0 100%); }
|
|
.wrap { padding: 0 36px; }
|
|
.head { display: flex; justify-content: space-between; align-items: flex-start; padding: 20px 0 12px; }
|
|
.brand { font-weight: 800; font-size: 22px; letter-spacing: .04em; color: #1C75BC; }
|
|
.brand small { display:block; font-size: 9px; letter-spacing: .22em; color: #58595B; font-weight:600; }
|
|
.co { text-align: right; font-size: 12px; color: #161E38; }
|
|
.co b { color:#161E38; }
|
|
.rule { height: 3px; background: linear-gradient(90deg,#27AAE1,#1C75BC); border-radius: 2px; }
|
|
h1 { font-size: 14px; letter-spacing: .04em; margin: 18px 0 4px; }
|
|
.wo { color: #1C75BC; font-weight: 700; }
|
|
.muted { color: #58595B; }
|
|
ol { padding-left: 22px; }
|
|
</style></head>
|
|
<body>
|
|
<div class="band-top"></div>
|
|
<div class="wrap">
|
|
<div class="head">
|
|
<div class="brand">SEAHAVEN<small>INDUSTRIES</small></div>
|
|
<div class="co">
|
|
<div><b>${esc(SEAHAVEN_HEADER.name)}</b></div>
|
|
<div>${esc(SEAHAVEN_HEADER.address)}</div>
|
|
<div>${esc(SEAHAVEN_HEADER.phone)}</div>
|
|
<div>${esc(SEAHAVEN_HEADER.fax)}</div>
|
|
<div>${esc(SEAHAVEN_HEADER.email)}</div>
|
|
</div>
|
|
</div>
|
|
<div class="rule"></div>
|
|
<h1>${esc(serviceTitle(row).toUpperCase())}</h1>
|
|
<p style="margin:2px 0;" class="wo">WO# ${esc(row.woNumber)}</p>
|
|
<p style="margin:14px 0 0;">Site ID — ${esc(fields.siteCode)}</p>
|
|
<p style="margin:2px 0;">Amazon.com Services LLC ${esc(fields.siteCode)}</p>
|
|
<p style="margin:2px 0;" class="muted">${esc(fields.street)},</p>
|
|
<p style="margin:2px 0;" class="muted">${esc(cityStateZip)}</p>
|
|
<p style="margin:14px 0 4px;"><b>Vendor / Technician:</b> ${esc(fields.tech || "—")}${fields.company ? ` · ${esc(fields.company)}` : ""}</p>
|
|
<ul style="padding-left:20px;">${safety}</ul>
|
|
${completedBlock}
|
|
<h3 style="margin:16px 0 4px;">${esc(proc.heading)}:</h3>
|
|
<p style="margin:0 0 8px;">${esc(proc.intro)}</p>
|
|
<ol>${procHtml}</ol>
|
|
${notesBlock}
|
|
</div>
|
|
<div class="band-bottom"></div>
|
|
</body></html>`;
|
|
}
|
|
|
|
export function printDoc(html: string): void {
|
|
const iframe = document.createElement("iframe");
|
|
iframe.style.position = "fixed";
|
|
iframe.style.right = "0";
|
|
iframe.style.bottom = "0";
|
|
iframe.style.width = "0";
|
|
iframe.style.height = "0";
|
|
iframe.style.border = "0";
|
|
document.body.appendChild(iframe);
|
|
const idoc = iframe.contentWindow?.document;
|
|
if (!idoc) {
|
|
document.body.removeChild(iframe);
|
|
return;
|
|
}
|
|
idoc.open();
|
|
idoc.write(html);
|
|
idoc.close();
|
|
const win = iframe.contentWindow!;
|
|
setTimeout(() => {
|
|
win.focus();
|
|
win.print();
|
|
setTimeout(() => {
|
|
if (iframe.parentNode) document.body.removeChild(iframe);
|
|
}, 1200);
|
|
}, 300);
|
|
}
|