fix(orders): seed Send PO body with this PO's line count, not the order's
This commit is contained in:
parent
44db71e6d7
commit
dfe52a62d9
1 changed files with 72 additions and 24 deletions
|
|
@ -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<OrderItem>({
|
||||
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
|
|||
<Dialog
|
||||
open={sendPo !== null}
|
||||
onOpenChange={(open) => {
|
||||
if (!open && !sendBusy) setSendPo(null);
|
||||
if (!open && !sendBusy) {
|
||||
setSendPo(null);
|
||||
setSendMessageLoading(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent>
|
||||
|
|
@ -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}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{sendMessage.length}/2000
|
||||
{sendMessageLoading
|
||||
? "Loading line count for preview…"
|
||||
: `${sendMessage.length}/2000`}
|
||||
</p>
|
||||
</div>
|
||||
{sendPo?.status === "mail_failed" && sendPo?.email_error && (
|
||||
|
|
@ -1074,7 +1117,12 @@ export default function OrderDetailPage({ params }: { params: Promise<{ id: stri
|
|||
</Button>
|
||||
<Button
|
||||
onClick={submitSendPo}
|
||||
disabled={sendBusy || !sendTo.trim() || !sendSubject.trim()}
|
||||
disabled={
|
||||
sendBusy ||
|
||||
sendMessageLoading ||
|
||||
!sendTo.trim() ||
|
||||
!sendSubject.trim()
|
||||
}
|
||||
>
|
||||
{sendBusy && <Loader2 className="mr-1.5 h-4 w-4 animate-spin" />}
|
||||
<Send className="mr-1.5 h-4 w-4" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue