diff --git a/src/app/(app)/orders/[id]/page.tsx b/src/app/(app)/orders/[id]/page.tsx index 6708845..ef0c60a 100644 --- a/src/app/(app)/orders/[id]/page.tsx +++ b/src/app/(app)/orders/[id]/page.tsx @@ -103,6 +103,11 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri const [sendBcc, setSendBcc] = useState(""); const [sendSubject, setSendSubject] = useState(""); const [sendMessage, setSendMessage] = useState(""); + // True while the modal is seeding the default body. We have to wait + // for the PO-level line count to come back from PB before we can + // build a non-misleading body, so the textarea is disabled until + // the count is resolved. + const [sendMessageLoading, setSendMessageLoading] = useState(false); const [loading, setLoading] = useState(true); const [deleteOpen, setDeleteOpen] = useState(false); @@ -269,32 +274,64 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri // enough that they can hit Send immediately. Seeding from the same // source guarantees the previewed text matches what the supplier // actually receives. - const openSendPo = (po: PurchaseOrder) => { + // + // The line count must come from THIS PO, not from the parent order + // (an order can have multiple POs with disjoint line sets). The + // `items` state on this page is order-level, so we fetch the PO's + // items on demand here. The textarea + Send button stay disabled + // until the count resolves, so the operator never sees a body that + // promises a different line count than the supplier will receive. + const openSendPo = async (po: PurchaseOrder) => { setSendPo(po); setSendTo(po.expand?.supplier?.email || ""); setSendCc(""); setSendBcc(""); setSendSubject(`Purchase Order ${po.po_number}`); - // Seed the message textarea with the supplier-personalised default. - // We deliberately pass ONLY the supplier-facing context (PO - // number, supplier name, line count). End-customer context is - // not surfaced here even if we have it — see the confidentiality - // note in `po-email-template.ts`. Operators wanting to add - // specifics can still type over the textarea before sending. - // - // The textarea is plain-text (operator can edit freely), so we - // seed it with the plain-text form of the default body. The - // route converts it to HTML at send time via the operator's - // current text — operators who want to preserve the rich - // formatting should not edit it; operators who want to type - // their own message get a clean single-paragraph wrap. - setSendMessage( - buildDefaultPoEmailBody({ - poNumber: po.po_number || po.id, - supplierName: po.expand?.supplier?.suppliername || "", - lineCount: items.length, - }).text - ); + setSendMessage(""); + setSendMessageLoading(true); + try { + const poItems = await getPocketBase() + .collection("order_items") + .getFullList({ + filter: `purchase_order = "${po.id}"`, + fields: "id", + }); + // Seed the message textarea with the supplier-personalised default. + // We deliberately pass ONLY the supplier-facing context (PO + // number, supplier name, line count). End-customer context is + // not surfaced here even if we have it — see the confidentiality + // note in `po-email-template.ts`. Operators wanting to add + // specifics can still type over the textarea before sending. + // + // The textarea is plain-text (operator can edit freely), so we + // seed it with the plain-text form of the default body. The + // route converts it to HTML at send time via the operator's + // current text — operators who want to preserve the rich + // formatting should not edit it; operators who want to type + // their own message get a clean single-paragraph wrap. + setSendMessage( + buildDefaultPoEmailBody({ + poNumber: po.po_number || po.id, + supplierName: po.expand?.supplier?.suppliername || "", + lineCount: poItems.length, + }).text + ); + } catch { + // Fall back to a generic body if the count lookup fails. The + // route still uses its own (correct) count at send time, so + // the worst case is the operator sees a slightly misleading + // preview — not a wrong email. + setSendMessage( + buildDefaultPoEmailBody({ + poNumber: po.po_number || po.id, + supplierName: po.expand?.supplier?.suppliername || "", + lineCount: 0, + }).text + ); + toast.error("Couldn't load line count for preview; check the PO before sending."); + } finally { + setSendMessageLoading(false); + } }; const submitSendPo = async () => { @@ -988,7 +1025,10 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri { - if (!open && !sendBusy) setSendPo(null); + if (!open && !sendBusy) { + setSendPo(null); + setSendMessageLoading(false); + } }} > @@ -1052,9 +1092,12 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri placeholder="Hi Supplier, please see attached PO. Confirm receipt and ETA. Thanks — WFDash Orders" rows={5} maxLength={2000} + disabled={sendMessageLoading} />

- {sendMessage.length}/2000 + {sendMessageLoading + ? "Loading line count for preview…" + : `${sendMessage.length}/2000`}

{sendPo?.status === "mail_failed" && sendPo?.email_error && ( @@ -1074,7 +1117,12 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri