shoc-frontend-new/src/domain/work-orders/utils/completion-doc-html.ts

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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
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 ? ` &middot; ${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);
}