feat(notifications): email notifications via Resend with React Email templates
7 event templates: po-submitted, po-approved, po-rejected, edits-requested, vendor-id-needed, payment-processed, receipt-confirmed. Notifier uses Resend in production and console.log in development.
This commit is contained in:
parent
c67afb2fff
commit
92b80dd278
9 changed files with 410 additions and 0 deletions
29
App/pelagia-portal/emails/edits-requested.tsx
Normal file
29
App/pelagia-portal/emails/edits-requested.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EditsRequestedEmail({ po, note }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
Your manager has requested edits on purchase order{" "}
|
||||||
|
<strong>{po.poNumber}</strong>.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#fffbeb", borderRadius: "8px", padding: "16px", border: "1px solid #fef3c7" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px" }}>{po.title}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Requested changes</Text>
|
||||||
|
<Text style={{ margin: 0, fontStyle: "italic" }}>{note}</Text>
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
Please log in to Pelagia Portal, make the requested edits, and resubmit the order.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditsRequestedEmail;
|
||||||
47
App/pelagia-portal/emails/layout.tsx
Normal file
47
App/pelagia-portal/emails/layout.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import {
|
||||||
|
Html,
|
||||||
|
Head,
|
||||||
|
Body,
|
||||||
|
Container,
|
||||||
|
Section,
|
||||||
|
Text,
|
||||||
|
Hr,
|
||||||
|
} from "@react-email/components";
|
||||||
|
|
||||||
|
interface EmailLayoutProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
previewText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmailLayout({ children, previewText }: EmailLayoutProps) {
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head />
|
||||||
|
<Body style={{ backgroundColor: "#f9fafb", fontFamily: "Inter, -apple-system, sans-serif" }}>
|
||||||
|
<Container style={{ maxWidth: "560px", margin: "0 auto", padding: "32px 16px" }}>
|
||||||
|
<Section
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
borderRadius: "12px",
|
||||||
|
border: "1px solid #e5e7eb",
|
||||||
|
padding: "32px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Section style={{ borderBottom: "2px solid #2563eb", paddingBottom: "12px", marginBottom: "24px" }}>
|
||||||
|
<Text style={{ fontSize: "20px", fontWeight: "700", color: "#1d4ed8", margin: 0 }}>
|
||||||
|
Pelagia Portal
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
{children}
|
||||||
|
</Section>
|
||||||
|
<Section style={{ padding: "16px 0" }}>
|
||||||
|
<Text style={{ fontSize: "12px", color: "#9ca3af", textAlign: "center" }}>
|
||||||
|
This message was sent by Pelagia Portal. You received it because you are a
|
||||||
|
stakeholder on this purchase order.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
</Container>
|
||||||
|
</Body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
32
App/pelagia-portal/emails/payment-processed.tsx
Normal file
32
App/pelagia-portal/emails/payment-processed.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string; paymentRef?: string | null };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PaymentProcessedEmail({ po }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
Payment has been confirmed for purchase order <strong>{po.poNumber}</strong>.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#f0fdf4", borderRadius: "8px", padding: "16px", border: "1px solid #dcfce7" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px" }}>{po.title}</Text>
|
||||||
|
{po.paymentRef && (
|
||||||
|
<>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Payment Reference</Text>
|
||||||
|
<Text style={{ margin: 0, fontFamily: "monospace" }}>{po.paymentRef}</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
Please confirm receipt in Pelagia Portal once you have received the goods or services.
|
||||||
|
This will close the purchase order.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentProcessedEmail;
|
||||||
36
App/pelagia-portal/emails/po-approved.tsx
Normal file
36
App/pelagia-portal/emails/po-approved.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
note?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PoApprovedEmail({ po, note }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
Your purchase order has been{" "}
|
||||||
|
<span style={{ color: "#16a34a", fontWeight: "600" }}>approved</span>.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#f0fdf4", borderRadius: "8px", padding: "16px", marginTop: "16px", border: "1px solid #dcfce7" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>PO Number</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontWeight: "600", fontFamily: "monospace" }}>{po.poNumber}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: 0 }}>{po.title}</Text>
|
||||||
|
</Section>
|
||||||
|
{note && (
|
||||||
|
<Section style={{ marginTop: "16px", borderLeft: "3px solid #2563eb", paddingLeft: "12px" }}>
|
||||||
|
<Text style={{ margin: 0, fontSize: "14px", color: "#374151", fontStyle: "italic" }}>
|
||||||
|
"{note}"
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
)}
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
Accounts have been notified and will process payment shortly.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PoApprovedEmail;
|
||||||
33
App/pelagia-portal/emails/po-rejected.tsx
Normal file
33
App/pelagia-portal/emails/po-rejected.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
note: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PoRejectedEmail({ po, note }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
Your purchase order has been{" "}
|
||||||
|
<span style={{ color: "#dc2626", fontWeight: "600" }}>rejected</span>.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#fef2f2", borderRadius: "8px", padding: "16px", border: "1px solid #fee2e2" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>PO Number</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontWeight: "600", fontFamily: "monospace" }}>{po.poNumber}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: 0 }}>{po.title}</Text>
|
||||||
|
</Section>
|
||||||
|
<Section style={{ marginTop: "16px", borderLeft: "3px solid #dc2626", paddingLeft: "12px" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Reason</Text>
|
||||||
|
<Text style={{ margin: 0, fontSize: "14px", color: "#374151" }}>{note}</Text>
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
If you have questions, please contact your manager directly.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PoRejectedEmail;
|
||||||
39
App/pelagia-portal/emails/po-submitted.tsx
Normal file
39
App/pelagia-portal/emails/po-submitted.tsx
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
submitterName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PoSubmittedEmail({ po, submitterName }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
A new purchase order has been submitted for your review.
|
||||||
|
</Text>
|
||||||
|
<Section
|
||||||
|
style={{
|
||||||
|
backgroundColor: "#f3f4f6",
|
||||||
|
borderRadius: "8px",
|
||||||
|
padding: "16px",
|
||||||
|
marginTop: "16px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>PO Number</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontSize: "15px", fontWeight: "600", color: "#111827", fontFamily: "monospace" }}>
|
||||||
|
{po.poNumber}
|
||||||
|
</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontSize: "15px", color: "#111827" }}>{po.title}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Submitted by</Text>
|
||||||
|
<Text style={{ margin: 0, fontSize: "15px", color: "#111827" }}>{submitterName}</Text>
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
Please log in to Pelagia Portal to review and take action on this order.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PoSubmittedEmail;
|
||||||
31
App/pelagia-portal/emails/receipt-confirmed.tsx
Normal file
31
App/pelagia-portal/emails/receipt-confirmed.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
confirmedBy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReceiptConfirmedEmail({ po, confirmedBy }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
Receipt has been confirmed for purchase order <strong>{po.poNumber}</strong>. The order is now{" "}
|
||||||
|
<span style={{ color: "#16a34a", fontWeight: "600" }}>closed</span>.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#f3f4f6", borderRadius: "8px", padding: "16px" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>PO Number</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontWeight: "600", fontFamily: "monospace" }}>{po.poNumber}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px" }}>{po.title}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Confirmed by</Text>
|
||||||
|
<Text style={{ margin: 0 }}>{confirmedBy}</Text>
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
No further action is required. The purchase order has been fully completed and archived.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReceiptConfirmedEmail;
|
||||||
28
App/pelagia-portal/emails/vendor-id-needed.tsx
Normal file
28
App/pelagia-portal/emails/vendor-id-needed.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Text, Section } from "@react-email/components";
|
||||||
|
import { EmailLayout } from "./layout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
po: { poNumber: string; title: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function VendorIdNeededEmail({ po }: Props) {
|
||||||
|
return (
|
||||||
|
<EmailLayout>
|
||||||
|
<Text style={{ fontSize: "16px", color: "#111827", marginTop: 0 }}>
|
||||||
|
A vendor ID is required before purchase order <strong>{po.poNumber}</strong> can proceed.
|
||||||
|
</Text>
|
||||||
|
<Section style={{ backgroundColor: "#fffbeb", borderRadius: "8px", padding: "16px", border: "1px solid #fef3c7" }}>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>PO Number</Text>
|
||||||
|
<Text style={{ margin: "0 0 12px", fontWeight: "600", fontFamily: "monospace" }}>{po.poNumber}</Text>
|
||||||
|
<Text style={{ margin: "0 0 4px", fontSize: "13px", color: "#6b7280" }}>Title</Text>
|
||||||
|
<Text style={{ margin: 0 }}>{po.title}</Text>
|
||||||
|
</Section>
|
||||||
|
<Text style={{ fontSize: "14px", color: "#374151" }}>
|
||||||
|
Please log in to Pelagia Portal, update the vendor details on this order, and the manager
|
||||||
|
will be notified to continue the review.
|
||||||
|
</Text>
|
||||||
|
</EmailLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VendorIdNeededEmail;
|
||||||
135
App/pelagia-portal/lib/notifier.ts
Normal file
135
App/pelagia-portal/lib/notifier.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import { Resend } from "resend";
|
||||||
|
import { db } from "@/lib/db";
|
||||||
|
import type { PurchaseOrder, User } from "@prisma/client";
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV === "development";
|
||||||
|
const resend = isDev ? null : new Resend(process.env.RESEND_API_KEY);
|
||||||
|
const FROM = `${process.env.EMAIL_FROM_NAME ?? "Pelagia Portal"} <${process.env.EMAIL_FROM ?? "noreply@pelagiaportal.com"}>`;
|
||||||
|
|
||||||
|
export type NotificationEvent =
|
||||||
|
| "PO_SUBMITTED"
|
||||||
|
| "PO_APPROVED"
|
||||||
|
| "PO_APPROVED_WITH_NOTE"
|
||||||
|
| "PO_REJECTED"
|
||||||
|
| "EDITS_REQUESTED"
|
||||||
|
| "VENDOR_ID_REQUESTED"
|
||||||
|
| "VENDOR_ID_PROVIDED"
|
||||||
|
| "PAYMENT_PROCESSING"
|
||||||
|
| "PAYMENT_SENT"
|
||||||
|
| "RECEIPT_CONFIRMED";
|
||||||
|
|
||||||
|
interface NotifyParams {
|
||||||
|
event: NotificationEvent;
|
||||||
|
po: PurchaseOrder & { submitter: User };
|
||||||
|
recipients: User[];
|
||||||
|
note?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function notify({ event, po, recipients, note }: NotifyParams) {
|
||||||
|
const subject = buildSubject(event, po.poNumber);
|
||||||
|
if (!subject) return;
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
recipients.map(async (recipient) => {
|
||||||
|
let status = "sent";
|
||||||
|
if (isDev) {
|
||||||
|
console.log(
|
||||||
|
`\n📧 [DEV EMAIL] To: ${recipient.email}\n Subject: ${subject}\n Body: ${buildBody(event, po, note)}\n`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const { error } = await resend!.emails.send({
|
||||||
|
from: FROM,
|
||||||
|
to: recipient.email,
|
||||||
|
subject,
|
||||||
|
html: buildHtml(event, po, recipient, note),
|
||||||
|
});
|
||||||
|
if (error) status = "failed";
|
||||||
|
} catch {
|
||||||
|
status = "failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await db.notification.create({
|
||||||
|
data: { subject, body: subject, status, poId: po.id, userId: recipient.id },
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSubject(event: NotificationEvent, poNumber: string): string | null {
|
||||||
|
const base = `PO ${poNumber}`;
|
||||||
|
const map: Record<NotificationEvent, string> = {
|
||||||
|
PO_SUBMITTED: `${base} submitted for review`,
|
||||||
|
PO_APPROVED: `${base} has been approved`,
|
||||||
|
PO_APPROVED_WITH_NOTE: `${base} has been approved`,
|
||||||
|
PO_REJECTED: `${base} has been rejected`,
|
||||||
|
EDITS_REQUESTED: `Edits requested on ${base}`,
|
||||||
|
VENDOR_ID_REQUESTED: `Vendor ID needed for ${base}`,
|
||||||
|
VENDOR_ID_PROVIDED: `Vendor ID provided for ${base}`,
|
||||||
|
PAYMENT_PROCESSING: `Payment initiated for ${base}`,
|
||||||
|
PAYMENT_SENT: `Payment confirmed for ${base}`,
|
||||||
|
RECEIPT_CONFIRMED: `Receipt confirmed — ${base} closed`,
|
||||||
|
};
|
||||||
|
return map[event] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildHtml(
|
||||||
|
event: NotificationEvent,
|
||||||
|
po: PurchaseOrder & { submitter: User },
|
||||||
|
recipient: User,
|
||||||
|
note?: string
|
||||||
|
): string {
|
||||||
|
return `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body style="font-family:Inter,sans-serif;max-width:560px;margin:0 auto;padding:32px 24px;color:#111827;">
|
||||||
|
<div style="border-bottom:2px solid #2563eb;padding-bottom:12px;margin-bottom:24px;">
|
||||||
|
<span style="font-size:20px;font-weight:700;color:#1d4ed8;">Pelagia Portal</span>
|
||||||
|
</div>
|
||||||
|
<p style="margin:0 0 16px;">Hi ${recipient.name},</p>
|
||||||
|
<p style="margin:0 0 24px;">${buildBody(event, po, note)}</p>
|
||||||
|
<div style="background:#f3f4f6;border-radius:8px;padding:16px;font-size:13px;color:#374151;">
|
||||||
|
<strong>PO Number:</strong> ${po.poNumber}<br/>
|
||||||
|
<strong>Title:</strong> ${po.title}<br/>
|
||||||
|
<strong>Submitted by:</strong> ${po.submitter.name}
|
||||||
|
</div>
|
||||||
|
<p style="margin-top:32px;font-size:12px;color:#9ca3af;">
|
||||||
|
You received this message because you are a stakeholder on this purchase order.
|
||||||
|
</p>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBody(
|
||||||
|
event: NotificationEvent,
|
||||||
|
po: PurchaseOrder & { submitter: User },
|
||||||
|
note?: string
|
||||||
|
): string {
|
||||||
|
const noteHtml = note
|
||||||
|
? `<br/><br/><em style="color:#374151;">"${note}"</em>`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
case "PO_SUBMITTED":
|
||||||
|
return `<strong>${po.submitter.name}</strong> has submitted purchase order <strong>${po.poNumber}</strong> for your review.`;
|
||||||
|
case "PO_APPROVED":
|
||||||
|
case "PO_APPROVED_WITH_NOTE":
|
||||||
|
return `Your purchase order <strong>${po.poNumber}</strong> has been <span style="color:#16a34a;font-weight:600;">approved</span>.${noteHtml}`;
|
||||||
|
case "PO_REJECTED":
|
||||||
|
return `Your purchase order <strong>${po.poNumber}</strong> has been <span style="color:#dc2626;font-weight:600;">rejected</span>.${noteHtml}`;
|
||||||
|
case "EDITS_REQUESTED":
|
||||||
|
return `Edits have been requested on <strong>${po.poNumber}</strong>. Please update the order and resubmit.${noteHtml}`;
|
||||||
|
case "VENDOR_ID_REQUESTED":
|
||||||
|
return `A vendor ID is required before <strong>${po.poNumber}</strong> can be approved. Please update the PO with the correct vendor details.`;
|
||||||
|
case "VENDOR_ID_PROVIDED":
|
||||||
|
return `The vendor ID has been provided for <strong>${po.poNumber}</strong>. It is ready for your review.`;
|
||||||
|
case "PAYMENT_PROCESSING":
|
||||||
|
return `Payment is being processed for <strong>${po.poNumber}</strong>.`;
|
||||||
|
case "PAYMENT_SENT":
|
||||||
|
return `Payment has been confirmed for <strong>${po.poNumber}</strong>. Please confirm delivery/receipt when the goods arrive.`;
|
||||||
|
case "RECEIPT_CONFIRMED":
|
||||||
|
return `Receipt has been confirmed for <strong>${po.poNumber}</strong>. The order is now closed.`;
|
||||||
|
default:
|
||||||
|
return `There has been an update on purchase order ${po.poNumber}.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue