@infuro/cms-core 1.0.61 → 1.0.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin.cjs +3624 -2642
- package/dist/admin.js +2415 -1433
- package/dist/api.cjs +41 -37
- package/dist/api.js +9 -5
- package/dist/chunk-3VZAAQUV.js +106 -0
- package/dist/{chunk-UHT4INVW.js → chunk-77YWLP4X.js} +625 -252
- package/dist/{chunk-BXYZDMTZ.cjs → chunk-7L556RGL.cjs} +3 -94
- package/dist/{chunk-T3HPSF7K.js → chunk-AYWDQFJZ.js} +3 -0
- package/dist/{chunk-URN7GS23.js → chunk-BWOITUNI.js} +203 -1062
- package/dist/{chunk-CNAHVRRU.cjs → chunk-CJEOH6QC.cjs} +676 -312
- package/dist/{chunk-O2642WVS.js → chunk-DTUL3Q5L.js} +17 -3
- package/dist/{chunk-YSYX4GUD.cjs → chunk-EWXG4E74.cjs} +213 -1082
- package/dist/chunk-GWDI752Q.js +83 -0
- package/dist/chunk-H3IVLIPR.cjs +86 -0
- package/dist/chunk-JLDHK2QE.cjs +109 -0
- package/dist/chunk-KAOHOHCA.js +901 -0
- package/dist/{chunk-2KUCQVAQ.js → chunk-MWEK7N3F.js} +2 -91
- package/dist/{chunk-V25RDUOU.cjs → chunk-MXABMVF7.cjs} +38 -4
- package/dist/chunk-OVTOP42W.js +199 -0
- package/dist/{chunk-73WVHINE.cjs → chunk-RHYVNRYG.cjs} +18 -4
- package/dist/{chunk-L3525VXI.js → chunk-RXTUSF3R.js} +36 -2
- package/dist/{chunk-77I3KS7V.cjs → chunk-UEE4PTTB.cjs} +3 -0
- package/dist/chunk-WFMVD567.cjs +209 -0
- package/dist/chunk-YVP4NQII.cjs +914 -0
- package/dist/{email-queue-6NJY6HNM.cjs → email-queue-VTYL2KFA.cjs} +7 -6
- package/dist/{email-queue-OI2HSCGE.js → email-queue-YPRSVG56.js} +3 -2
- package/dist/email-service-ATCPJFWG.cjs +28 -0
- package/dist/email-service-DPKVQOKP.js +3 -0
- package/dist/{emit-order-notification-trigger-TNEZOYEE.cjs → emit-order-notification-trigger-WI4HGMKH.cjs} +4 -4
- package/dist/{emit-order-notification-trigger-CZU3V2WE.js → emit-order-notification-trigger-XVG2B5ZF.js} +2 -2
- package/dist/{erp-order-invoice-3GXDE443.js → erp-order-invoice-3UGWLICU.js} +2 -1
- package/dist/{erp-order-invoice-EYYNR526.cjs → erp-order-invoice-KJYJNWMG.cjs} +6 -5
- package/dist/index.cjs +366 -316
- package/dist/index.d.cts +61 -21
- package/dist/index.d.ts +61 -21
- package/dist/index.js +79 -29
- package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +56 -0
- package/dist/migrations/1783100000000-AddSmsToOrderNotificationBindingsChannelEnum.ts +17 -0
- package/dist/migrations/1783200000000-AddOrderAssigneeColumns.ts +30 -0
- package/dist/order-assignees-handlers-LKMAGLG6.js +150 -0
- package/dist/order-assignees-handlers-NPKD64P6.cjs +152 -0
- package/dist/order-completion-otp-handlers-IMMQGHDR.cjs +363 -0
- package/dist/order-completion-otp-handlers-WGM7HWG4.js +361 -0
- package/dist/order-notification-dispatcher-5TUFCDU7.cjs +35 -0
- package/dist/order-notification-dispatcher-JKFQJYGG.js +10 -0
- package/dist/sms-notification-templates-handlers-SJLO7JGZ.js +47 -0
- package/dist/sms-notification-templates-handlers-YSB3L2PK.cjs +49 -0
- package/package.json +1 -1
- package/dist/order-notification-dispatcher-KJVGLK5E.cjs +0 -24
- package/dist/order-notification-dispatcher-ZSFKEYMP.js +0 -7
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunkH3IVLIPR_cjs = require('./chunk-H3IVLIPR.cjs');
|
|
4
|
+
var chunkUEE4PTTB_cjs = require('./chunk-UEE4PTTB.cjs');
|
|
5
|
+
var chunkYVP4NQII_cjs = require('./chunk-YVP4NQII.cjs');
|
|
6
|
+
var chunkMXABMVF7_cjs = require('./chunk-MXABMVF7.cjs');
|
|
7
|
+
var chunkJLDHK2QE_cjs = require('./chunk-JLDHK2QE.cjs');
|
|
6
8
|
var chunkOOTLNH63_cjs = require('./chunk-OOTLNH63.cjs');
|
|
7
9
|
var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
|
|
8
|
-
var clientSes = require('@aws-sdk/client-ses');
|
|
9
|
-
var module$1 = require('module');
|
|
10
|
-
var nodemailer = require('nodemailer');
|
|
11
10
|
var crypto = require('crypto');
|
|
12
11
|
|
|
13
|
-
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
14
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
-
|
|
16
12
|
function _interopNamespace(e) {
|
|
17
13
|
if (e && e.__esModule) return e;
|
|
18
14
|
var n = Object.create(null);
|
|
@@ -31,7 +27,6 @@ function _interopNamespace(e) {
|
|
|
31
27
|
return Object.freeze(n);
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
var nodemailer__default = /*#__PURE__*/_interopDefault(nodemailer);
|
|
35
30
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
36
31
|
|
|
37
32
|
// src/lib/event-order-template-context.ts
|
|
@@ -332,86 +327,6 @@ async function loadOrderNotificationContexts(orderId, deps) {
|
|
|
332
327
|
}
|
|
333
328
|
chunkUSNT2KNT_cjs.__name(loadOrderNotificationContexts, "loadOrderNotificationContexts");
|
|
334
329
|
|
|
335
|
-
// src/plugins/whatsapp/whatsapp-queue.ts
|
|
336
|
-
var WHATSAPP_QUEUE_NAME = "whatsapp";
|
|
337
|
-
function maskPhone(raw) {
|
|
338
|
-
const digits = raw.replace(/\D/g, "");
|
|
339
|
-
if (digits.length <= 4) return "****";
|
|
340
|
-
return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
|
|
341
|
-
}
|
|
342
|
-
chunkUSNT2KNT_cjs.__name(maskPhone, "maskPhone");
|
|
343
|
-
function registerWhatsAppQueueProcessor(cms) {
|
|
344
|
-
const queue = cms.getPlugin("queue");
|
|
345
|
-
const whatsapp = cms.getPlugin("whatsapp");
|
|
346
|
-
if (!queue) {
|
|
347
|
-
console.warn("[whatsapp] queue processor not registered: queue plugin missing");
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
|
-
if (!whatsapp || typeof whatsapp.send !== "function") {
|
|
351
|
-
console.warn("[whatsapp] queue processor not registered: whatsapp plugin missing or not configured");
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
queue.registerProcessor(WHATSAPP_QUEUE_NAME, async (data) => {
|
|
355
|
-
const payload = data;
|
|
356
|
-
if (!payload.to) {
|
|
357
|
-
console.warn("[whatsapp] queue job skipped: missing recipient");
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
console.log("[whatsapp] queue job processing", {
|
|
361
|
-
to: maskPhone(payload.to),
|
|
362
|
-
templateKey: payload.templateKey ?? null,
|
|
363
|
-
externalTemplateRef: payload.externalTemplateRef ?? null
|
|
364
|
-
});
|
|
365
|
-
try {
|
|
366
|
-
const ok = await whatsapp.send(payload);
|
|
367
|
-
if (ok) {
|
|
368
|
-
console.log("[whatsapp] queue job completed", {
|
|
369
|
-
to: maskPhone(payload.to),
|
|
370
|
-
sent: true
|
|
371
|
-
});
|
|
372
|
-
} else {
|
|
373
|
-
console.warn("[whatsapp] queue job completed without delivery", {
|
|
374
|
-
to: maskPhone(payload.to),
|
|
375
|
-
sent: false
|
|
376
|
-
});
|
|
377
|
-
}
|
|
378
|
-
} catch (err) {
|
|
379
|
-
console.error("[whatsapp] queue job failed", {
|
|
380
|
-
to: maskPhone(payload.to),
|
|
381
|
-
error: err instanceof Error ? err.message : String(err)
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
console.log("[whatsapp] queue processor registered", {
|
|
386
|
-
queue: WHATSAPP_QUEUE_NAME
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
chunkUSNT2KNT_cjs.__name(registerWhatsAppQueueProcessor, "registerWhatsAppQueueProcessor");
|
|
390
|
-
async function queueWhatsApp(cms, payload) {
|
|
391
|
-
const queue = cms.getPlugin("queue");
|
|
392
|
-
if (queue) {
|
|
393
|
-
console.log("[whatsapp] enqueue", {
|
|
394
|
-
to: maskPhone(payload.to),
|
|
395
|
-
templateKey: payload.templateKey ?? null,
|
|
396
|
-
externalTemplateRef: payload.externalTemplateRef ?? null
|
|
397
|
-
});
|
|
398
|
-
await queue.add(WHATSAPP_QUEUE_NAME, payload);
|
|
399
|
-
return true;
|
|
400
|
-
}
|
|
401
|
-
const whatsapp = cms.getPlugin("whatsapp");
|
|
402
|
-
if (whatsapp && typeof whatsapp.send === "function") {
|
|
403
|
-
console.log("[whatsapp] no queue plugin \u2014 sending immediately", {
|
|
404
|
-
to: maskPhone(payload.to)
|
|
405
|
-
});
|
|
406
|
-
return whatsapp.send(payload);
|
|
407
|
-
}
|
|
408
|
-
console.warn("[whatsapp] send skipped: neither queue nor whatsapp plugin available", {
|
|
409
|
-
to: maskPhone(payload.to)
|
|
410
|
-
});
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
chunkUSNT2KNT_cjs.__name(queueWhatsApp, "queueWhatsApp");
|
|
414
|
-
|
|
415
330
|
// src/plugins/whatsapp/whatsapp-service.ts
|
|
416
331
|
function norm(v) {
|
|
417
332
|
const t = v?.trim();
|
|
@@ -461,11 +376,11 @@ function resolveMetaWhatsAppTemplateName(opts) {
|
|
|
461
376
|
return ref || void 0;
|
|
462
377
|
}
|
|
463
378
|
chunkUSNT2KNT_cjs.__name(resolveMetaWhatsAppTemplateName, "resolveMetaWhatsAppTemplateName");
|
|
464
|
-
function
|
|
379
|
+
function maskPhone(digits) {
|
|
465
380
|
if (digits.length <= 4) return "****";
|
|
466
381
|
return `${"*".repeat(Math.max(0, digits.length - 4))}${digits.slice(-4)}`;
|
|
467
382
|
}
|
|
468
|
-
chunkUSNT2KNT_cjs.__name(
|
|
383
|
+
chunkUSNT2KNT_cjs.__name(maskPhone, "maskPhone");
|
|
469
384
|
function logWhatsApp(level, message, extra) {
|
|
470
385
|
const line = extra ? `${message} ${JSON.stringify(extra)}` : message;
|
|
471
386
|
if (level === "info") console.log(`[whatsapp] ${line}`);
|
|
@@ -507,7 +422,7 @@ var WhatsAppService = class {
|
|
|
507
422
|
return false;
|
|
508
423
|
}
|
|
509
424
|
logWhatsApp("info", "send started", {
|
|
510
|
-
to:
|
|
425
|
+
to: maskPhone(to),
|
|
511
426
|
templateKey: opts.templateKey ?? null,
|
|
512
427
|
externalTemplateRef: opts.externalTemplateRef ?? null,
|
|
513
428
|
phoneNumberId: cfg.phoneNumberId
|
|
@@ -550,7 +465,7 @@ var WhatsAppService = class {
|
|
|
550
465
|
body
|
|
551
466
|
] : [];
|
|
552
467
|
logWhatsApp("info", "sending template message", {
|
|
553
|
-
to:
|
|
468
|
+
to: maskPhone(to),
|
|
554
469
|
templateName,
|
|
555
470
|
paramCount: paramValues.length,
|
|
556
471
|
paramOrder: opts.templateParamOrder ?? null
|
|
@@ -559,12 +474,12 @@ var WhatsAppService = class {
|
|
|
559
474
|
}
|
|
560
475
|
if (!body) {
|
|
561
476
|
logWhatsApp("warn", "send skipped: no template name and empty body", {
|
|
562
|
-
to:
|
|
477
|
+
to: maskPhone(to)
|
|
563
478
|
});
|
|
564
479
|
return false;
|
|
565
480
|
}
|
|
566
481
|
logWhatsApp("info", "sending text message", {
|
|
567
|
-
to:
|
|
482
|
+
to: maskPhone(to),
|
|
568
483
|
bodyLength: body.length
|
|
569
484
|
});
|
|
570
485
|
return this.sendTextMessage(cfg, to, body);
|
|
@@ -590,14 +505,14 @@ var WhatsAppService = class {
|
|
|
590
505
|
const errText = await res.text().catch(() => "");
|
|
591
506
|
logWhatsApp("error", "text send failed", {
|
|
592
507
|
status: res.status,
|
|
593
|
-
to:
|
|
508
|
+
to: maskPhone(to),
|
|
594
509
|
error: errText
|
|
595
510
|
});
|
|
596
511
|
return false;
|
|
597
512
|
}
|
|
598
513
|
const data = await res.json().catch(() => ({}));
|
|
599
514
|
logWhatsApp("info", "text send succeeded", {
|
|
600
|
-
to:
|
|
515
|
+
to: maskPhone(to),
|
|
601
516
|
messageId: data.messages?.[0]?.id ?? null
|
|
602
517
|
});
|
|
603
518
|
return true;
|
|
@@ -639,7 +554,7 @@ var WhatsAppService = class {
|
|
|
639
554
|
const errText = await res.text().catch(() => "");
|
|
640
555
|
logWhatsApp("error", "template send failed", {
|
|
641
556
|
status: res.status,
|
|
642
|
-
to:
|
|
557
|
+
to: maskPhone(to),
|
|
643
558
|
templateName,
|
|
644
559
|
paramCount: paramValues.length,
|
|
645
560
|
error: errText
|
|
@@ -648,7 +563,7 @@ var WhatsAppService = class {
|
|
|
648
563
|
}
|
|
649
564
|
const data = await res.json().catch(() => ({}));
|
|
650
565
|
logWhatsApp("info", "template send succeeded", {
|
|
651
|
-
to:
|
|
566
|
+
to: maskPhone(to),
|
|
652
567
|
templateName,
|
|
653
568
|
messageId: data.messages?.[0]?.id ?? null
|
|
654
569
|
});
|
|
@@ -656,886 +571,6 @@ var WhatsAppService = class {
|
|
|
656
571
|
}
|
|
657
572
|
};
|
|
658
573
|
|
|
659
|
-
// src/plugins/email/templates/layout.ts
|
|
660
|
-
function escapeHtml(s) {
|
|
661
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
662
|
-
}
|
|
663
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml, "escapeHtml");
|
|
664
|
-
function socialLinkInnerHtml(s) {
|
|
665
|
-
if (s.iconUrl?.trim()) {
|
|
666
|
-
return `<img src="${escapeHtml(s.iconUrl.trim())}" alt="" width="20" height="20" style="display:inline-block;vertical-align:middle;border:0;" />`;
|
|
667
|
-
}
|
|
668
|
-
const label = s.icon && s.icon.trim() || "\u{1F517}";
|
|
669
|
-
return escapeHtml(label);
|
|
670
|
-
}
|
|
671
|
-
chunkUSNT2KNT_cjs.__name(socialLinkInnerHtml, "socialLinkInnerHtml");
|
|
672
|
-
function supportLinesHtml(supportEmail, supportPhone, opts) {
|
|
673
|
-
const parts = [];
|
|
674
|
-
const top = opts?.tightTop ? "margin-top:0;" : "margin-top:4px;";
|
|
675
|
-
if (supportEmail) {
|
|
676
|
-
parts.push(`<div style="font-size:13px;color:#444;${top}">📧 ${escapeHtml(supportEmail)}</div>`);
|
|
677
|
-
}
|
|
678
|
-
if (supportPhone) {
|
|
679
|
-
parts.push(`<div style="font-size:13px;color:#444;margin-top:2px;">📞 ${escapeHtml(supportPhone)}</div>`);
|
|
680
|
-
}
|
|
681
|
-
return parts.join("");
|
|
682
|
-
}
|
|
683
|
-
chunkUSNT2KNT_cjs.__name(supportLinesHtml, "supportLinesHtml");
|
|
684
|
-
function renderLayout(options) {
|
|
685
|
-
const { bodyHtml, companyDetails } = options;
|
|
686
|
-
const { logoUrl, companyName, supportEmail, supportPhone, socialLinks, footerDisclaimer, followUsTitle = "Follow Us" } = companyDetails;
|
|
687
|
-
const supportFooterHtml = supportLinesHtml(supportEmail, supportPhone, {
|
|
688
|
-
tightTop: true
|
|
689
|
-
});
|
|
690
|
-
const hasSocial = Boolean(socialLinks?.length);
|
|
691
|
-
const brandFooterInner = [];
|
|
692
|
-
if (logoUrl) {
|
|
693
|
-
brandFooterInner.push(`<img src="${escapeHtml(logoUrl)}" alt="" style="max-height:28px;max-width:160px;height:auto;width:auto;display:inline-block;vertical-align:middle;margin-right:10px;border:0;" />`);
|
|
694
|
-
}
|
|
695
|
-
if (companyName) {
|
|
696
|
-
brandFooterInner.push(`<span style="font-size:14px;font-weight:600;color:#111;vertical-align:middle;">${escapeHtml(companyName)}</span>`);
|
|
697
|
-
}
|
|
698
|
-
const brandFooterHtml = brandFooterInner.length ? `<div style="line-height:1.4;">${brandFooterInner.join("")}</div>` : "";
|
|
699
|
-
const followTitleHtml = hasSocial && followUsTitle ? `<div class="footer-follow-title" style="font-size:13px;font-weight:600;color:#555;margin:0;">${escapeHtml(followUsTitle)}</div>` : "";
|
|
700
|
-
const socialRowHtml = hasSocial && socialLinks ? `<div class="footer-social-icons" style="margin-top:6px;text-align:left;font-size:0;line-height:0;">${socialLinks.map((s) => `<a href="${escapeHtml(s.url)}" style="display:inline-block;margin-right:12px;text-decoration:none;color:#333;font-size:18px;line-height:1;vertical-align:middle;">${socialLinkInnerHtml(s)}</a>`).join("")}</div>` : "";
|
|
701
|
-
const rowSupport = supportFooterHtml ? `<tr><td class="footer-support-cell" colspan="2" valign="top" style="padding:0 0 10px 0;">${supportFooterHtml}</td></tr>` : "";
|
|
702
|
-
let footerBlock = "";
|
|
703
|
-
if (brandFooterHtml || followTitleHtml || socialRowHtml || footerDisclaimer || rowSupport) {
|
|
704
|
-
const rowBrand = brandFooterHtml ? `<tr><td class="footer-brand-cell" colspan="2" valign="top" style="padding:0 0 10px 0;">${brandFooterHtml}</td></tr>` : "";
|
|
705
|
-
const rowFollowTitle = followTitleHtml ? `<tr><td class="footer-follow-cell" colspan="2" valign="top" style="padding:14px 0 0 0;text-align:left;">${followTitleHtml}</td></tr>` : "";
|
|
706
|
-
const rowSocial = socialRowHtml ? `<tr><td class="footer-social-cell" colspan="2" style="padding:0;text-align:left;">${socialRowHtml}</td></tr>` : "";
|
|
707
|
-
footerBlock = `<table role="presentation" class="footer-main" width="100%" cellpadding="0" cellspacing="0" style="margin-top:28px;padding-top:16px;">${rowBrand}${rowSupport}${rowFollowTitle}${rowSocial}</table>`;
|
|
708
|
-
}
|
|
709
|
-
const disclaimerBlock = footerDisclaimer ? `<div style="margin-top:20px;padding-top:12px;font-size:11px;line-height:1.5;color:#888;">${escapeHtml(footerDisclaimer).replace(/\n/g, "<br/>")}</div>` : "";
|
|
710
|
-
const responsiveCss = `
|
|
711
|
-
@media only screen and (max-width: 600px) {
|
|
712
|
-
.email-wrap { padding-left: 12px !important; padding-right: 12px !important; }
|
|
713
|
-
.footer-follow-cell { padding-top: 12px !important; }
|
|
714
|
-
}
|
|
715
|
-
`;
|
|
716
|
-
return `<!DOCTYPE html>
|
|
717
|
-
<html>
|
|
718
|
-
<head>
|
|
719
|
-
<meta charset="utf-8">
|
|
720
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
721
|
-
<style type="text/css">${responsiveCss}</style>
|
|
722
|
-
</head>
|
|
723
|
-
<body style="margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;font-size:14px;line-height:1.5;color:#333;background:#fff;">
|
|
724
|
-
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#fff;">
|
|
725
|
-
<tr><td class="email-wrap" style="padding:16px;">
|
|
726
|
-
<div>
|
|
727
|
-
${bodyHtml}
|
|
728
|
-
</div>
|
|
729
|
-
${footerBlock}
|
|
730
|
-
${disclaimerBlock}
|
|
731
|
-
</td></tr>
|
|
732
|
-
</table>
|
|
733
|
-
</body>
|
|
734
|
-
</html>`;
|
|
735
|
-
}
|
|
736
|
-
chunkUSNT2KNT_cjs.__name(renderLayout, "renderLayout");
|
|
737
|
-
|
|
738
|
-
// src/plugins/email/templates/inline-cta.ts
|
|
739
|
-
function escapeHtml2(s) {
|
|
740
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
741
|
-
}
|
|
742
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml2, "escapeHtml");
|
|
743
|
-
function escapeAttr(s) {
|
|
744
|
-
return escapeHtml2(s);
|
|
745
|
-
}
|
|
746
|
-
chunkUSNT2KNT_cjs.__name(escapeAttr, "escapeAttr");
|
|
747
|
-
function primaryCtaButton(href, label) {
|
|
748
|
-
return `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">
|
|
749
|
-
<tr><td style="border-radius:6px;background:#1a1a1a;">
|
|
750
|
-
<a href="${escapeAttr(href)}" style="display:inline-block;padding:12px 22px;font-size:14px;font-weight:600;color:#ffffff;text-decoration:none;">${escapeHtml2(label)}</a>
|
|
751
|
-
</td></tr>
|
|
752
|
-
</table>`;
|
|
753
|
-
}
|
|
754
|
-
chunkUSNT2KNT_cjs.__name(primaryCtaButton, "primaryCtaButton");
|
|
755
|
-
|
|
756
|
-
// src/plugins/email/templates/signup.ts
|
|
757
|
-
function render(ctx) {
|
|
758
|
-
const { name, loginUrl, verifyEmailUrl, companyDetails } = ctx;
|
|
759
|
-
if (verifyEmailUrl) {
|
|
760
|
-
const subject2 = "Confirm your email";
|
|
761
|
-
const greeting = name && name.trim() ? `Hi ${escapeHtml2(name.trim())},` : "Hi,";
|
|
762
|
-
const bodyHtml2 = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
763
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Thanks for signing up. Please confirm your email address to activate your account. Until you confirm, you won\u2019t be able to sign in.</p>
|
|
764
|
-
${primaryCtaButton(verifyEmailUrl, "Confirm email address")}
|
|
765
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">This link expires in a few days. If you didn\u2019t create an account, you can ignore this message.<br/><span style="word-break:break-all;">${escapeHtml2(verifyEmailUrl)}</span></p>`;
|
|
766
|
-
const text2 = [
|
|
767
|
-
greeting,
|
|
768
|
-
"",
|
|
769
|
-
"Confirm your email to activate your account:",
|
|
770
|
-
verifyEmailUrl,
|
|
771
|
-
"",
|
|
772
|
-
"You cannot sign in until your email is confirmed."
|
|
773
|
-
].join("\n");
|
|
774
|
-
const html2 = renderLayout({
|
|
775
|
-
bodyHtml: bodyHtml2,
|
|
776
|
-
companyDetails
|
|
777
|
-
});
|
|
778
|
-
return {
|
|
779
|
-
subject: subject2,
|
|
780
|
-
html: html2,
|
|
781
|
-
text: text2
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
const subject = "Welcome";
|
|
785
|
-
const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Welcome${name ? `, ${escapeHtml2(name)}` : ""}.</p><p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your account has been created.</p>${loginUrl ? `${primaryCtaButton(loginUrl, "Sign in")}` : ""}`;
|
|
786
|
-
const text = `Welcome${name ? `, ${name}` : ""}. Your account has been created.${loginUrl ? ` Sign in: ${loginUrl}` : ""}`;
|
|
787
|
-
const html = renderLayout({
|
|
788
|
-
bodyHtml,
|
|
789
|
-
companyDetails
|
|
790
|
-
});
|
|
791
|
-
return {
|
|
792
|
-
subject,
|
|
793
|
-
html,
|
|
794
|
-
text
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
chunkUSNT2KNT_cjs.__name(render, "render");
|
|
798
|
-
|
|
799
|
-
// src/plugins/email/templates/passwordReset.ts
|
|
800
|
-
function render2(ctx) {
|
|
801
|
-
const { resetLink, companyDetails } = ctx;
|
|
802
|
-
const subject = "Reset your password";
|
|
803
|
-
const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Hello,</p>
|
|
804
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">We received a request to reset the password for your account. If you made this request, use the button below to choose a new password.</p>
|
|
805
|
-
<p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;">For your security, this link will stop working after a short time. If it expires, request a new reset from the sign-in page.</p>
|
|
806
|
-
${primaryCtaButton(resetLink, "Reset password")}
|
|
807
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If you did not request a password reset, you can safely ignore this email\u2014your password will stay the same.</p>
|
|
808
|
-
<p style="margin:12px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(resetLink)}</span></p>`;
|
|
809
|
-
const text = [
|
|
810
|
-
"Hello,",
|
|
811
|
-
"",
|
|
812
|
-
"We received a request to reset your password. Open the link below to set a new password:",
|
|
813
|
-
resetLink,
|
|
814
|
-
"",
|
|
815
|
-
"This link expires after a limited time.",
|
|
816
|
-
"",
|
|
817
|
-
"If you did not request this, you can ignore this email."
|
|
818
|
-
].join("\n");
|
|
819
|
-
const html = renderLayout({
|
|
820
|
-
bodyHtml,
|
|
821
|
-
companyDetails
|
|
822
|
-
});
|
|
823
|
-
return {
|
|
824
|
-
subject,
|
|
825
|
-
html,
|
|
826
|
-
text
|
|
827
|
-
};
|
|
828
|
-
}
|
|
829
|
-
chunkUSNT2KNT_cjs.__name(render2, "render");
|
|
830
|
-
|
|
831
|
-
// src/plugins/email/templates/passwordChange.ts
|
|
832
|
-
function render3(ctx) {
|
|
833
|
-
const { name, companyDetails } = ctx;
|
|
834
|
-
const subject = "Password changed";
|
|
835
|
-
const bodyHtml = `<h2>Password changed</h2><p>Your password has been updated successfully${name ? `, ${escapeHtml3(name)}` : ""}.</p>`;
|
|
836
|
-
const text = `Your password has been updated successfully${name ? `, ${name}` : ""}.`;
|
|
837
|
-
const html = renderLayout({
|
|
838
|
-
bodyHtml,
|
|
839
|
-
companyDetails
|
|
840
|
-
});
|
|
841
|
-
return {
|
|
842
|
-
subject,
|
|
843
|
-
html,
|
|
844
|
-
text
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
chunkUSNT2KNT_cjs.__name(render3, "render");
|
|
848
|
-
function escapeHtml3(s) {
|
|
849
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
850
|
-
}
|
|
851
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml3, "escapeHtml");
|
|
852
|
-
|
|
853
|
-
// src/plugins/email/templates/orderPlaced.ts
|
|
854
|
-
function formatMoney(amount, currency) {
|
|
855
|
-
const v = typeof amount === "string" ? parseFloat(amount) : amount;
|
|
856
|
-
const num = Number.isFinite(v) ? Number(v).toFixed(2) : String(amount);
|
|
857
|
-
return currency ? `${num} ${currency}` : num;
|
|
858
|
-
}
|
|
859
|
-
chunkUSNT2KNT_cjs.__name(formatMoney, "formatMoney");
|
|
860
|
-
function renderLineItemsHtml(items, currency) {
|
|
861
|
-
if (!items.length) {
|
|
862
|
-
return '<p style="margin:12px 0 0 0;font-size:14px;color:#666;">No line items.</p>';
|
|
863
|
-
}
|
|
864
|
-
const rows = items.map((it) => {
|
|
865
|
-
const name = escapeHtml2(it.productName);
|
|
866
|
-
const sku = it.sku && String(it.sku).trim() ? `<span style="font-size:12px;color:#888;"> (${escapeHtml2(String(it.sku).trim())})</span>` : "";
|
|
867
|
-
const hsn = it.hsn && String(it.hsn).trim() ? `<br/><span style="font-size:11px;color:#888;">HSN: ${escapeHtml2(String(it.hsn).trim())}</span>` : "";
|
|
868
|
-
const taxNote = it.tax != null && String(it.tax).trim() && Number(it.tax) !== 0 ? `<br/><span style="font-size:11px;color:#888;">Tax: ${escapeHtml2(formatMoney(it.tax, currency))}</span>` : "";
|
|
869
|
-
return `<tr>
|
|
870
|
-
<td style="padding:10px 8px 10px 0;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;">${name}${sku}${hsn}${taxNote}</td>
|
|
871
|
-
<td align="right" style="padding:10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(String(it.quantity))}</td>
|
|
872
|
-
<td align="right" style="padding:10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(formatMoney(it.unitPrice, currency))}</td>
|
|
873
|
-
<td align="right" style="padding:10px 0 10px 8px;border-bottom:1px solid #eee;vertical-align:top;font-size:14px;color:#333;white-space:nowrap;">${escapeHtml2(formatMoney(it.lineTotal, currency))}</td>
|
|
874
|
-
</tr>`;
|
|
875
|
-
}).join("");
|
|
876
|
-
return `<p style="margin:16px 0 8px 0;font-size:14px;font-weight:600;color:#111;">Order items</p>
|
|
877
|
-
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
|
|
878
|
-
<tr>
|
|
879
|
-
<td style="padding:0 8px 8px 0;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;letter-spacing:0.02em;">Item</td>
|
|
880
|
-
<td align="right" style="padding:0 8px 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Qty</td>
|
|
881
|
-
<td align="right" style="padding:0 8px 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Price</td>
|
|
882
|
-
<td align="right" style="padding:0 0 8px 8px;font-size:12px;font-weight:600;color:#555;text-transform:uppercase;">Total</td>
|
|
883
|
-
</tr>
|
|
884
|
-
${rows}
|
|
885
|
-
</table>`;
|
|
886
|
-
}
|
|
887
|
-
chunkUSNT2KNT_cjs.__name(renderLineItemsHtml, "renderLineItemsHtml");
|
|
888
|
-
function renderLineItemsText(items, currency) {
|
|
889
|
-
if (!items.length) return "";
|
|
890
|
-
const lines = items.map((it) => {
|
|
891
|
-
let s = `- ${it.productName} \xD7 ${it.quantity} @ ${formatMoney(it.unitPrice, currency)} = ${formatMoney(it.lineTotal, currency)}`;
|
|
892
|
-
if (it.sku) s += ` [${it.sku}]`;
|
|
893
|
-
if (it.hsn && String(it.hsn).trim()) s += ` HSN:${it.hsn}`;
|
|
894
|
-
if (it.tax != null && Number(it.tax) !== 0) s += ` tax:${formatMoney(it.tax, currency)}`;
|
|
895
|
-
return s;
|
|
896
|
-
});
|
|
897
|
-
return [
|
|
898
|
-
"Items:",
|
|
899
|
-
...lines
|
|
900
|
-
].join("\n");
|
|
901
|
-
}
|
|
902
|
-
chunkUSNT2KNT_cjs.__name(renderLineItemsText, "renderLineItemsText");
|
|
903
|
-
function formatAddressLines(addr) {
|
|
904
|
-
if (!addr || typeof addr !== "object") return [];
|
|
905
|
-
const parts = [
|
|
906
|
-
addr.line1,
|
|
907
|
-
addr.line2,
|
|
908
|
-
[
|
|
909
|
-
addr.city,
|
|
910
|
-
addr.state
|
|
911
|
-
].filter(Boolean).join(", "),
|
|
912
|
-
addr.postalCode,
|
|
913
|
-
addr.country
|
|
914
|
-
].filter((x) => x != null && String(x).trim() !== "");
|
|
915
|
-
return parts.map((p) => String(p).trim()).filter(Boolean);
|
|
916
|
-
}
|
|
917
|
-
chunkUSNT2KNT_cjs.__name(formatAddressLines, "formatAddressLines");
|
|
918
|
-
function renderAddressesHtml(billing, shipping) {
|
|
919
|
-
const bLines = formatAddressLines(billing);
|
|
920
|
-
const sLines = formatAddressLines(shipping);
|
|
921
|
-
if (!bLines.length && !sLines.length) return "";
|
|
922
|
-
let html = '<p style="margin:16px 0 8px 0;font-size:14px;font-weight:600;color:#111;">Addresses</p>';
|
|
923
|
-
if (bLines.length) {
|
|
924
|
-
html += `<p style="margin:0 0 4px 0;font-size:12px;font-weight:600;color:#555;">Billing</p><p style="margin:0 0 12px 0;font-size:14px;line-height:1.5;color:#333;">${bLines.map((l) => escapeHtml2(l)).join("<br/>")}</p>`;
|
|
925
|
-
}
|
|
926
|
-
if (sLines.length) {
|
|
927
|
-
html += `<p style="margin:0 0 4px 0;font-size:12px;font-weight:600;color:#555;">Shipping</p><p style="margin:0 0 0 0;font-size:14px;line-height:1.5;color:#333;">${sLines.map((l) => escapeHtml2(l)).join("<br/>")}</p>`;
|
|
928
|
-
}
|
|
929
|
-
return html;
|
|
930
|
-
}
|
|
931
|
-
chunkUSNT2KNT_cjs.__name(renderAddressesHtml, "renderAddressesHtml");
|
|
932
|
-
function renderAddressesText(billing, shipping) {
|
|
933
|
-
const bLines = formatAddressLines(billing);
|
|
934
|
-
const sLines = formatAddressLines(shipping);
|
|
935
|
-
const out = [];
|
|
936
|
-
if (bLines.length) out.push("Billing:", ...bLines.map((l) => ` ${l}`));
|
|
937
|
-
if (sLines.length) out.push("Shipping:", ...sLines.map((l) => ` ${l}`));
|
|
938
|
-
return out.length ? out.join("\n") : "";
|
|
939
|
-
}
|
|
940
|
-
chunkUSNT2KNT_cjs.__name(renderAddressesText, "renderAddressesText");
|
|
941
|
-
function render4(ctx) {
|
|
942
|
-
const { orderNumber, total, subtotal, tax, currency, customerName, companyDetails, audience = "customer", internalCustomerEmail, lineItems = [], billingAddress, shippingAddress, invoiceAttached = false } = ctx;
|
|
943
|
-
const itemsHtml = renderLineItemsHtml(lineItems, currency);
|
|
944
|
-
const itemsText = renderLineItemsText(lineItems, currency);
|
|
945
|
-
const addrHtml = renderAddressesHtml(billingAddress, shippingAddress);
|
|
946
|
-
const addrText = renderAddressesText(billingAddress, shippingAddress);
|
|
947
|
-
const subtotalLine = subtotal != null && String(subtotal).trim() !== "" ? `<p style="margin:8px 0 0 0;font-size:14px;line-height:1.5;color:#333;"><strong>Subtotal:</strong> ${escapeHtml2(String(subtotal))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
|
|
948
|
-
const taxLine = tax != null && String(tax).trim() !== "" && Number(tax) !== 0 ? `<p style="margin:4px 0 0 0;font-size:14px;line-height:1.5;color:#333;"><strong>Tax:</strong> ${escapeHtml2(String(tax))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
|
|
949
|
-
const totalLine = total != null && String(total).trim() !== "" ? `<p style="margin:12px 0 0 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order total:</strong> ${escapeHtml2(String(total))}${currency ? ` ${escapeHtml2(currency)}` : ""}</p>` : "";
|
|
950
|
-
let subject;
|
|
951
|
-
let bodyHtml;
|
|
952
|
-
let text;
|
|
953
|
-
if (audience === "sales") {
|
|
954
|
-
subject = `New order #${orderNumber}`;
|
|
955
|
-
const who = customerName || internalCustomerEmail ? `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Customer:</strong> ${escapeHtml2(customerName || "\u2014")}${internalCustomerEmail ? ` <span style="color:#555;">(${escapeHtml2(internalCustomerEmail)})</span>` : ""}</p>` : "";
|
|
956
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">A new order has been placed and payment completed.</p>
|
|
957
|
-
<p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order number:</strong> ${escapeHtml2(orderNumber)}</p>
|
|
958
|
-
${who}
|
|
959
|
-
${addrHtml}
|
|
960
|
-
${itemsHtml}
|
|
961
|
-
${subtotalLine}${taxLine}${totalLine}`;
|
|
962
|
-
text = [
|
|
963
|
-
`New order #${orderNumber}`,
|
|
964
|
-
customerName ? `Customer: ${customerName}` : "",
|
|
965
|
-
internalCustomerEmail ? `Email: ${internalCustomerEmail}` : "",
|
|
966
|
-
addrText,
|
|
967
|
-
itemsText,
|
|
968
|
-
subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
|
|
969
|
-
tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
|
|
970
|
-
total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : ""
|
|
971
|
-
].filter(Boolean).join("\n\n");
|
|
972
|
-
} else {
|
|
973
|
-
subject = `Order confirmed #${orderNumber}`;
|
|
974
|
-
const thanksPlain = customerName && customerName.trim() ? `Thank you, ${customerName.trim()}.` : "Thank you for your order.";
|
|
975
|
-
const thanksHtml = `${escapeHtml2(thanksPlain)} We\u2019ve received your order and will process it shortly.`;
|
|
976
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${thanksHtml}</p>
|
|
977
|
-
<p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Order number:</strong> ${escapeHtml2(orderNumber)}</p>
|
|
978
|
-
${addrHtml}
|
|
979
|
-
${itemsHtml}
|
|
980
|
-
${subtotalLine}${taxLine}${totalLine}
|
|
981
|
-
${invoiceAttached ? `<p style="margin:16px 0 0 0;font-size:14px;line-height:1.5;color:#333;">Your order invoice is attached as a PDF.</p>` : ""}
|
|
982
|
-
<p style="margin:16px 0 0 0;font-size:13px;line-height:1.5;color:#666;">If you have questions, reply to this email or contact us using the details below.</p>`;
|
|
983
|
-
text = [
|
|
984
|
-
`Order confirmed #${orderNumber}`,
|
|
985
|
-
`${thanksPlain} We\u2019ve received your order and will process it shortly.`,
|
|
986
|
-
"",
|
|
987
|
-
addrText,
|
|
988
|
-
itemsText,
|
|
989
|
-
subtotal != null ? `Subtotal: ${subtotal}${currency ? ` ${currency}` : ""}` : "",
|
|
990
|
-
tax != null && Number(tax) !== 0 ? `Tax: ${tax}${currency ? ` ${currency}` : ""}` : "",
|
|
991
|
-
total != null ? `Order total: ${total}${currency ? ` ${currency}` : ""}` : "",
|
|
992
|
-
invoiceAttached ? "Your order invoice is attached as a PDF." : "",
|
|
993
|
-
"",
|
|
994
|
-
"We will process your order shortly."
|
|
995
|
-
].filter((line, i, arr) => !(line === "" && arr[i - 1] === "")).join("\n");
|
|
996
|
-
}
|
|
997
|
-
const html = renderLayout({
|
|
998
|
-
bodyHtml,
|
|
999
|
-
companyDetails
|
|
1000
|
-
});
|
|
1001
|
-
return {
|
|
1002
|
-
subject,
|
|
1003
|
-
html,
|
|
1004
|
-
text
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
chunkUSNT2KNT_cjs.__name(render4, "render");
|
|
1008
|
-
|
|
1009
|
-
// src/plugins/email/templates/returnInitiated.ts
|
|
1010
|
-
function render5(ctx) {
|
|
1011
|
-
const { returnId, orderNumber, companyDetails } = ctx;
|
|
1012
|
-
const subject = "Return initiated";
|
|
1013
|
-
const bodyHtml = `<h2>Return initiated</h2><p>Your return request has been received.</p>${orderNumber ? `<p><strong>Order:</strong> ${escapeHtml4(orderNumber)}</p>` : ""}${returnId ? `<p><strong>Return ID:</strong> ${escapeHtml4(returnId)}</p>` : ""}`;
|
|
1014
|
-
const text = `Return initiated.${orderNumber ? ` Order: ${orderNumber}` : ""}${returnId ? ` Return ID: ${returnId}` : ""}`;
|
|
1015
|
-
const html = renderLayout({
|
|
1016
|
-
bodyHtml,
|
|
1017
|
-
companyDetails
|
|
1018
|
-
});
|
|
1019
|
-
return {
|
|
1020
|
-
subject,
|
|
1021
|
-
html,
|
|
1022
|
-
text
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
chunkUSNT2KNT_cjs.__name(render5, "render");
|
|
1026
|
-
function escapeHtml4(s) {
|
|
1027
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1028
|
-
}
|
|
1029
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml4, "escapeHtml");
|
|
1030
|
-
|
|
1031
|
-
// src/plugins/email/templates/shippingUpdate.ts
|
|
1032
|
-
function render6(ctx) {
|
|
1033
|
-
const { orderNumber, status, trackingUrl, companyDetails } = ctx;
|
|
1034
|
-
const subject = status ? `Shipping update: ${status}` : "Shipping update";
|
|
1035
|
-
const bodyHtml = `<h2>Shipping update</h2>${status ? `<p><strong>Status:</strong> ${escapeHtml5(status)}</p>` : ""}${orderNumber ? `<p><strong>Order:</strong> ${escapeHtml5(orderNumber)}</p>` : ""}${trackingUrl ? `<p><a href="${escapeHtml5(trackingUrl)}">Track your order</a></p>` : ""}`;
|
|
1036
|
-
const text = `Shipping update.${status ? ` Status: ${status}` : ""}${orderNumber ? ` Order: ${orderNumber}` : ""}${trackingUrl ? ` Track: ${trackingUrl}` : ""}`;
|
|
1037
|
-
const html = renderLayout({
|
|
1038
|
-
bodyHtml,
|
|
1039
|
-
companyDetails
|
|
1040
|
-
});
|
|
1041
|
-
return {
|
|
1042
|
-
subject,
|
|
1043
|
-
html,
|
|
1044
|
-
text
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
chunkUSNT2KNT_cjs.__name(render6, "render");
|
|
1048
|
-
function escapeHtml5(s) {
|
|
1049
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1050
|
-
}
|
|
1051
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml5, "escapeHtml");
|
|
1052
|
-
|
|
1053
|
-
// src/plugins/email/templates/invite.ts
|
|
1054
|
-
function render7(ctx) {
|
|
1055
|
-
const { inviteLink, email, inviteeName, companyDetails } = ctx;
|
|
1056
|
-
const subject = "You're invited";
|
|
1057
|
-
const greeting = inviteeName && inviteeName.trim() ? `Hello ${escapeHtml2(inviteeName.trim())},` : "Hello,";
|
|
1058
|
-
const bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
1059
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to create your account for <strong>${escapeHtml2(email)}</strong>. Use the secure link below to choose your password and activate access.</p>
|
|
1060
|
-
<p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;">This link is personal to you. If you did not expect this invitation, you can ignore this message.</p>
|
|
1061
|
-
${primaryCtaButton(inviteLink, "Accept invitation & set password")}
|
|
1062
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
|
|
1063
|
-
const text = [
|
|
1064
|
-
inviteeName?.trim() ? `Hello ${inviteeName.trim()},` : "Hello,",
|
|
1065
|
-
"",
|
|
1066
|
-
`You have been invited to create your account (${email}).`,
|
|
1067
|
-
"Open this link to set your password:",
|
|
1068
|
-
inviteLink,
|
|
1069
|
-
"",
|
|
1070
|
-
"If you did not expect this invitation, you can ignore this email."
|
|
1071
|
-
].join("\n");
|
|
1072
|
-
const html = renderLayout({
|
|
1073
|
-
bodyHtml,
|
|
1074
|
-
companyDetails
|
|
1075
|
-
});
|
|
1076
|
-
return {
|
|
1077
|
-
subject,
|
|
1078
|
-
html,
|
|
1079
|
-
text
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
chunkUSNT2KNT_cjs.__name(render7, "render");
|
|
1083
|
-
|
|
1084
|
-
// src/plugins/email/templates/formSubmission.ts
|
|
1085
|
-
function render8(ctx) {
|
|
1086
|
-
const { formName, contactName, contactEmail, formData, formFieldRows, companyDetails } = ctx;
|
|
1087
|
-
const subject = `New Form Submission: ${formName}`;
|
|
1088
|
-
const fieldsBlock = formFieldRows && formFieldRows.length > 0 ? formFieldRows.map((r) => `<p style="margin:10px 0 0 0;line-height:1.5;"><strong>${escapeHtml6(r.label)}</strong><br/>${escapeHtml6(r.value)}</p>`).join("") : `<p style="margin:10px 0 0 0;font-size:13px;white-space:pre-wrap;">${escapeHtml6(JSON.stringify(formData, null, 2))}</p>`;
|
|
1089
|
-
const bodyHtml = `<p style="margin:0 0 6px 0;font-size:18px;font-weight:600;color:#111;">New Form Submission</p>
|
|
1090
|
-
<p style="margin:0 0 4px 0;"><strong>Form:</strong> ${escapeHtml6(formName)}</p>
|
|
1091
|
-
<p style="margin:0 0 8px 0;"><strong>Contact:</strong> ${escapeHtml6(contactName)}${contactEmail ? ` (${escapeHtml6(contactEmail)})` : ""}</p>
|
|
1092
|
-
${fieldsBlock}`;
|
|
1093
|
-
const textLines = [
|
|
1094
|
-
"New Form Submission",
|
|
1095
|
-
`Form: ${formName}`,
|
|
1096
|
-
`Contact: ${contactName}${contactEmail ? ` (${contactEmail})` : ""}`,
|
|
1097
|
-
"",
|
|
1098
|
-
...formFieldRows?.length ? formFieldRows.map((r) => `${r.label}: ${r.value}`) : [
|
|
1099
|
-
JSON.stringify(formData, null, 2)
|
|
1100
|
-
]
|
|
1101
|
-
];
|
|
1102
|
-
const text = textLines.join("\n");
|
|
1103
|
-
const html = renderLayout({
|
|
1104
|
-
bodyHtml,
|
|
1105
|
-
companyDetails
|
|
1106
|
-
});
|
|
1107
|
-
return {
|
|
1108
|
-
subject,
|
|
1109
|
-
html,
|
|
1110
|
-
text
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
chunkUSNT2KNT_cjs.__name(render8, "render");
|
|
1114
|
-
function escapeHtml6(s) {
|
|
1115
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1116
|
-
}
|
|
1117
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml6, "escapeHtml");
|
|
1118
|
-
|
|
1119
|
-
// src/plugins/email/templates/otp.ts
|
|
1120
|
-
function render9(ctx) {
|
|
1121
|
-
const { code, companyDetails } = ctx;
|
|
1122
|
-
const name = companyDetails.companyName?.trim() || "Our store";
|
|
1123
|
-
const bodyHtml = `
|
|
1124
|
-
<p>Your verification code is:</p>
|
|
1125
|
-
<p style="font-size:24px;font-weight:bold;letter-spacing:4px;">${escapeHtml7(code)}</p>
|
|
1126
|
-
<p style="color:#666;font-size:14px;">This code expires in 10 minutes. If you did not request it, you can ignore this email.</p>
|
|
1127
|
-
`;
|
|
1128
|
-
const html = renderLayout({
|
|
1129
|
-
bodyHtml,
|
|
1130
|
-
companyDetails
|
|
1131
|
-
});
|
|
1132
|
-
return {
|
|
1133
|
-
subject: `${name}: your sign-in code`,
|
|
1134
|
-
html,
|
|
1135
|
-
text: `Your code is ${code}. It expires in 10 minutes.`
|
|
1136
|
-
};
|
|
1137
|
-
}
|
|
1138
|
-
chunkUSNT2KNT_cjs.__name(render9, "render");
|
|
1139
|
-
function escapeHtml7(s) {
|
|
1140
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1141
|
-
}
|
|
1142
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml7, "escapeHtml");
|
|
1143
|
-
|
|
1144
|
-
// src/plugins/email/templates/chatLead.ts
|
|
1145
|
-
function render10(ctx) {
|
|
1146
|
-
const { contactName, contactEmail, contactPhone, conversationId, latestMessage, intentCode, intentReason, transcript, companyDetails } = ctx;
|
|
1147
|
-
const intentDisplay = intentCode?.trim() ? `${intentCode.trim()}${intentReason?.trim() ? ` \u2014 ${intentReason.trim()}` : ""}` : intentReason;
|
|
1148
|
-
const subject = `Chat lead${intentCode?.trim() ? ` [${intentCode.trim()}]` : ""}: ${contactName || contactEmail || "Visitor"}`;
|
|
1149
|
-
const transcriptBlock = transcript?.trim() ? `<div style="margin-top:16px;padding:12px;background:#f9fafb;border-radius:6px;border:1px solid #e5e7eb;">
|
|
1150
|
-
<p style="margin:0 0 8px 0;font-size:13px;font-weight:600;color:#374151;">Recent conversation</p>
|
|
1151
|
-
<pre style="margin:0;font-size:12px;white-space:pre-wrap;font-family:inherit;color:#4b5563;">${escapeHtml8(transcript.trim())}</pre>
|
|
1152
|
-
</div>` : "";
|
|
1153
|
-
const bodyHtml = `<p style="margin:0 0 6px 0;font-size:18px;font-weight:600;color:#111;">New chat lead</p>
|
|
1154
|
-
<p style="margin:0 0 12px 0;font-size:14px;color:#374151;">A visitor showed interest based on your intent rules.</p>
|
|
1155
|
-
<table style="width:100%;border-collapse:collapse;font-size:14px;">
|
|
1156
|
-
<tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Name</td><td>${escapeHtml8(contactName || "\u2014")}</td></tr>
|
|
1157
|
-
<tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Email</td><td><a href="mailto:${escapeHtml8(contactEmail)}">${escapeHtml8(contactEmail)}</a></td></tr>
|
|
1158
|
-
<tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Phone</td><td>${escapeHtml8(contactPhone?.trim() || "\u2014")}</td></tr>
|
|
1159
|
-
<tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Conversation</td><td>#${conversationId}</td></tr>
|
|
1160
|
-
<tr><td style="padding:4px 8px 4px 0;color:#6b7280;vertical-align:top;">Intent</td><td>${escapeHtml8(intentReason)}</td></tr>
|
|
1161
|
-
</table>
|
|
1162
|
-
<p style="margin:16px 0 6px 0;font-size:13px;font-weight:600;color:#374151;">Latest message</p>
|
|
1163
|
-
<p style="margin:0;font-size:14px;white-space:pre-wrap;">${escapeHtml8(latestMessage)}</p>
|
|
1164
|
-
${transcriptBlock}`;
|
|
1165
|
-
const text = [
|
|
1166
|
-
"New chat lead",
|
|
1167
|
-
`Name: ${contactName || "\u2014"}`,
|
|
1168
|
-
`Email: ${contactEmail}`,
|
|
1169
|
-
`Phone: ${contactPhone?.trim() || "\u2014"}`,
|
|
1170
|
-
`Conversation: #${conversationId}`,
|
|
1171
|
-
`Intent: ${intentDisplay}`,
|
|
1172
|
-
"",
|
|
1173
|
-
"Latest message:",
|
|
1174
|
-
latestMessage,
|
|
1175
|
-
transcript?.trim() ? `
|
|
1176
|
-
Recent conversation:
|
|
1177
|
-
${transcript.trim()}` : ""
|
|
1178
|
-
].join("\n");
|
|
1179
|
-
const html = renderLayout({
|
|
1180
|
-
bodyHtml,
|
|
1181
|
-
companyDetails
|
|
1182
|
-
});
|
|
1183
|
-
return {
|
|
1184
|
-
subject,
|
|
1185
|
-
html,
|
|
1186
|
-
text
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
|
-
chunkUSNT2KNT_cjs.__name(render10, "render");
|
|
1190
|
-
function escapeHtml8(s) {
|
|
1191
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1192
|
-
}
|
|
1193
|
-
chunkUSNT2KNT_cjs.__name(escapeHtml8, "escapeHtml");
|
|
1194
|
-
|
|
1195
|
-
// src/plugins/email/templates/vendorOnboarded.ts
|
|
1196
|
-
function render11(ctx) {
|
|
1197
|
-
const { audience, activation, kind = "vendor_onboard", vendorName, vendorSlug, ownerName, ownerEmail, inviteLink, signInLink, companyDetails } = ctx;
|
|
1198
|
-
const storeLabel = vendorName.trim() || "your store";
|
|
1199
|
-
const greeting = ownerName && ownerName.trim() ? `Hello ${escapeHtml2(ownerName.trim())},` : "Hello,";
|
|
1200
|
-
const slugNote = vendorSlug && vendorSlug.trim() ? `<p style="margin:0 0 8px 0;font-size:14px;line-height:1.5;color:#555;"><strong>Store URL slug:</strong> ${escapeHtml2(vendorSlug.trim())}</p>` : "";
|
|
1201
|
-
let subject;
|
|
1202
|
-
let bodyHtml;
|
|
1203
|
-
let text;
|
|
1204
|
-
if (audience === "internal") {
|
|
1205
|
-
const action = kind === "team_invite" ? "A team member was invited to a vendor store." : activation === "invite" ? "A new vendor store was created and the owner was invited." : "A new vendor store was created with an active owner account.";
|
|
1206
|
-
subject = kind === "team_invite" ? `Team invite: ${storeLabel}` : `New vendor: ${storeLabel}`;
|
|
1207
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${action}</p>
|
|
1208
|
-
<p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Store:</strong> ${escapeHtml2(storeLabel)}</p>
|
|
1209
|
-
${slugNote}
|
|
1210
|
-
<p style="margin:0 0 8px 0;font-size:15px;line-height:1.5;color:#333;"><strong>Owner / invitee:</strong> ${escapeHtml2(ownerName?.trim() || "\u2014")} <span style="color:#555;">(${escapeHtml2(ownerEmail)})</span></p>
|
|
1211
|
-
<p style="margin:0 0 0 0;font-size:14px;line-height:1.5;color:#555;"><strong>Activation:</strong> ${escapeHtml2(activation === "invite" ? "Invitation" : "Password set")}</p>`;
|
|
1212
|
-
text = [
|
|
1213
|
-
subject,
|
|
1214
|
-
"",
|
|
1215
|
-
action,
|
|
1216
|
-
`Store: ${storeLabel}`,
|
|
1217
|
-
vendorSlug ? `Slug: ${vendorSlug}` : "",
|
|
1218
|
-
`Owner / invitee: ${ownerName?.trim() || "\u2014"} (${ownerEmail})`,
|
|
1219
|
-
`Activation: ${activation === "invite" ? "Invitation" : "Password set"}`
|
|
1220
|
-
].filter(Boolean).join("\n");
|
|
1221
|
-
} else if (kind === "team_invite" && activation === "membership" && inviteLink) {
|
|
1222
|
-
subject = `You're invited to ${storeLabel}`;
|
|
1223
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
1224
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to join the vendor team for <strong>${escapeHtml2(storeLabel)}</strong>. You already have an account \u2014 accept below to join this store. You will not need to set a new password.</p>
|
|
1225
|
-
${primaryCtaButton(inviteLink, "Accept invitation")}
|
|
1226
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
|
|
1227
|
-
text = [
|
|
1228
|
-
ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
|
|
1229
|
-
"",
|
|
1230
|
-
`You have been invited to join ${storeLabel}.`,
|
|
1231
|
-
"Open this link to accept (no new password required):",
|
|
1232
|
-
inviteLink
|
|
1233
|
-
].join("\n");
|
|
1234
|
-
} else if (kind === "team_invite" && activation === "invite" && inviteLink) {
|
|
1235
|
-
subject = `You're invited to ${storeLabel}`;
|
|
1236
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
1237
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">You have been invited to join the vendor team for <strong>${escapeHtml2(storeLabel)}</strong>. Use the secure link below to set your password and activate your account.</p>
|
|
1238
|
-
${primaryCtaButton(inviteLink, "Accept invitation & set password")}
|
|
1239
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
|
|
1240
|
-
text = [
|
|
1241
|
-
ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
|
|
1242
|
-
"",
|
|
1243
|
-
`You have been invited to join ${storeLabel}.`,
|
|
1244
|
-
"Open this link to set your password:",
|
|
1245
|
-
inviteLink
|
|
1246
|
-
].join("\n");
|
|
1247
|
-
} else if (activation === "invite" && inviteLink) {
|
|
1248
|
-
subject = `Your vendor store "${storeLabel}" is ready`;
|
|
1249
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
1250
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your vendor store <strong>${escapeHtml2(storeLabel)}</strong> has been created. Accept the invitation below to set your password and access the admin dashboard.</p>
|
|
1251
|
-
${slugNote}
|
|
1252
|
-
${primaryCtaButton(inviteLink, "Accept invitation & set password")}
|
|
1253
|
-
<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">If the button does not work, copy and paste this URL into your browser:<br/><span style="word-break:break-all;">${escapeHtml2(inviteLink)}</span></p>`;
|
|
1254
|
-
text = [
|
|
1255
|
-
ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
|
|
1256
|
-
"",
|
|
1257
|
-
`Your vendor store "${storeLabel}" has been created.`,
|
|
1258
|
-
"Open this link to set your password:",
|
|
1259
|
-
inviteLink
|
|
1260
|
-
].join("\n");
|
|
1261
|
-
} else {
|
|
1262
|
-
subject = `Welcome to ${storeLabel}`;
|
|
1263
|
-
const loginUrl = signInLink?.trim() || inviteLink?.trim() || "";
|
|
1264
|
-
bodyHtml = `<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">${greeting}</p>
|
|
1265
|
-
<p style="margin:0 0 12px 0;font-size:15px;line-height:1.5;color:#333;">Your vendor store <strong>${escapeHtml2(storeLabel)}</strong> has been created and your owner account is ready. Sign in to the admin dashboard to manage your store.</p>
|
|
1266
|
-
${slugNote}
|
|
1267
|
-
${loginUrl ? primaryCtaButton(loginUrl, "Sign in to admin") : ""}
|
|
1268
|
-
${loginUrl ? `<p style="margin:16px 0 0 0;font-size:12px;line-height:1.5;color:#888;">Sign-in URL:<br/><span style="word-break:break-all;">${escapeHtml2(loginUrl)}</span></p>` : ""}`;
|
|
1269
|
-
text = [
|
|
1270
|
-
ownerName?.trim() ? `Hello ${ownerName.trim()},` : "Hello,",
|
|
1271
|
-
"",
|
|
1272
|
-
`Your vendor store "${storeLabel}" has been created.`,
|
|
1273
|
-
loginUrl ? `Sign in: ${loginUrl}` : ""
|
|
1274
|
-
].filter(Boolean).join("\n");
|
|
1275
|
-
}
|
|
1276
|
-
const html = renderLayout({
|
|
1277
|
-
bodyHtml,
|
|
1278
|
-
companyDetails
|
|
1279
|
-
});
|
|
1280
|
-
return {
|
|
1281
|
-
subject,
|
|
1282
|
-
html,
|
|
1283
|
-
text
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1286
|
-
chunkUSNT2KNT_cjs.__name(render11, "render");
|
|
1287
|
-
|
|
1288
|
-
// src/plugins/email/templates/index.ts
|
|
1289
|
-
var templateRenderMap = {
|
|
1290
|
-
signup: render,
|
|
1291
|
-
passwordReset: render2,
|
|
1292
|
-
passwordChange: render3,
|
|
1293
|
-
orderPlaced: render4,
|
|
1294
|
-
returnInitiated: render5,
|
|
1295
|
-
shippingUpdate: render6,
|
|
1296
|
-
invite: render7,
|
|
1297
|
-
formSubmission: render8,
|
|
1298
|
-
otp: render9,
|
|
1299
|
-
chatLead: render10,
|
|
1300
|
-
vendorOnboarded: render11
|
|
1301
|
-
};
|
|
1302
|
-
function getTemplateRenderer(name) {
|
|
1303
|
-
return templateRenderMap[name];
|
|
1304
|
-
}
|
|
1305
|
-
chunkUSNT2KNT_cjs.__name(getTemplateRenderer, "getTemplateRenderer");
|
|
1306
|
-
|
|
1307
|
-
// src/plugins/email/renderer.ts
|
|
1308
|
-
function renderEmail(templateName, ctx, options) {
|
|
1309
|
-
const layoutFn = options?.renderLayout ?? renderLayout;
|
|
1310
|
-
const getBody = options?.getBody;
|
|
1311
|
-
if (getBody) {
|
|
1312
|
-
const custom = getBody(templateName, ctx);
|
|
1313
|
-
if (custom != null) {
|
|
1314
|
-
const html = layoutFn({
|
|
1315
|
-
bodyHtml: custom.bodyHtml,
|
|
1316
|
-
companyDetails: ctx.companyDetails
|
|
1317
|
-
});
|
|
1318
|
-
return {
|
|
1319
|
-
subject: custom.subject,
|
|
1320
|
-
html,
|
|
1321
|
-
text: custom.text
|
|
1322
|
-
};
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
const render12 = getTemplateRenderer(templateName);
|
|
1326
|
-
if (!render12) {
|
|
1327
|
-
throw new Error(`Unknown email template: ${templateName}`);
|
|
1328
|
-
}
|
|
1329
|
-
return render12(ctx);
|
|
1330
|
-
}
|
|
1331
|
-
chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
|
|
1332
|
-
|
|
1333
|
-
// src/plugins/email/email-service.ts
|
|
1334
|
-
var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-YSYX4GUD.cjs', document.baseURI).href)));
|
|
1335
|
-
var MailComposer = require2("nodemailer/lib/mail-composer");
|
|
1336
|
-
var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
|
|
1337
|
-
"af-south-1",
|
|
1338
|
-
"ap-south-2",
|
|
1339
|
-
"ap-southeast-3",
|
|
1340
|
-
"ap-southeast-5",
|
|
1341
|
-
"ca-west-1",
|
|
1342
|
-
"eu-central-2",
|
|
1343
|
-
"eu-south-1",
|
|
1344
|
-
"il-central-1",
|
|
1345
|
-
"me-central-1",
|
|
1346
|
-
"me-south-1"
|
|
1347
|
-
]);
|
|
1348
|
-
function sesSmtpHost(region) {
|
|
1349
|
-
return `email-smtp.${region}.amazonaws.com`;
|
|
1350
|
-
}
|
|
1351
|
-
chunkUSNT2KNT_cjs.__name(sesSmtpHost, "sesSmtpHost");
|
|
1352
|
-
function sesRegionSupportsSmtp(region) {
|
|
1353
|
-
return !SES_REGIONS_WITHOUT_SMTP.has(region.trim());
|
|
1354
|
-
}
|
|
1355
|
-
chunkUSNT2KNT_cjs.__name(sesRegionSupportsSmtp, "sesRegionSupportsSmtp");
|
|
1356
|
-
function toBuffer(content) {
|
|
1357
|
-
return Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
1358
|
-
}
|
|
1359
|
-
chunkUSNT2KNT_cjs.__name(toBuffer, "toBuffer");
|
|
1360
|
-
var EmailService = class {
|
|
1361
|
-
static {
|
|
1362
|
-
chunkUSNT2KNT_cjs.__name(this, "EmailService");
|
|
1363
|
-
}
|
|
1364
|
-
config;
|
|
1365
|
-
templateOptions;
|
|
1366
|
-
sesClient;
|
|
1367
|
-
transporter;
|
|
1368
|
-
/** AWS via SMTP (nodemailer) vs SES API SDK. */
|
|
1369
|
-
awsViaSmtp = false;
|
|
1370
|
-
constructor(config) {
|
|
1371
|
-
this.config = config;
|
|
1372
|
-
this.templateOptions = config.templateOptions;
|
|
1373
|
-
if (config.type === "AWS") {
|
|
1374
|
-
if (!config.region) {
|
|
1375
|
-
throw new Error("AWS SES configuration incomplete: AWS_REGION is required");
|
|
1376
|
-
}
|
|
1377
|
-
if (config.user && config.password) {
|
|
1378
|
-
this.awsViaSmtp = true;
|
|
1379
|
-
const host = config.host || sesSmtpHost(config.region);
|
|
1380
|
-
const port = config.port ?? 587;
|
|
1381
|
-
const secure = config.secure ?? false;
|
|
1382
|
-
this.transporter = nodemailer__default.default.createTransport({
|
|
1383
|
-
host,
|
|
1384
|
-
port,
|
|
1385
|
-
secure,
|
|
1386
|
-
auth: {
|
|
1387
|
-
user: config.user,
|
|
1388
|
-
pass: config.password
|
|
1389
|
-
}
|
|
1390
|
-
});
|
|
1391
|
-
} else if (config.accessKeyId && config.secretAccessKey) {
|
|
1392
|
-
this.sesClient = new clientSes.SESClient({
|
|
1393
|
-
region: config.region,
|
|
1394
|
-
credentials: {
|
|
1395
|
-
accessKeyId: config.accessKeyId,
|
|
1396
|
-
secretAccessKey: config.secretAccessKey
|
|
1397
|
-
}
|
|
1398
|
-
});
|
|
1399
|
-
} else {
|
|
1400
|
-
throw new Error("AWS SES configuration incomplete: set SMTP_USER and SMTP_PASSWORD (SES SMTP) or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY (SES API)");
|
|
1401
|
-
}
|
|
1402
|
-
} else if (config.type === "SMTP" || config.type === "GMAIL") {
|
|
1403
|
-
if (!config.user || !config.password) throw new Error("SMTP configuration incomplete");
|
|
1404
|
-
const host = config.type === "GMAIL" ? "smtp.gmail.com" : config.host;
|
|
1405
|
-
if (!host) {
|
|
1406
|
-
throw new Error("SMTP configuration incomplete: SMTP_HOST is required when SMTP_TYPE=SMTP (nodemailer otherwise connects to 127.0.0.1)");
|
|
1407
|
-
}
|
|
1408
|
-
const port = config.port ?? 587;
|
|
1409
|
-
const secure = config.secure ?? false;
|
|
1410
|
-
this.transporter = nodemailer__default.default.createTransport({
|
|
1411
|
-
host,
|
|
1412
|
-
port,
|
|
1413
|
-
secure,
|
|
1414
|
-
auth: {
|
|
1415
|
-
user: config.user,
|
|
1416
|
-
pass: config.password
|
|
1417
|
-
}
|
|
1418
|
-
});
|
|
1419
|
-
} else {
|
|
1420
|
-
throw new Error(`Unsupported email type: ${config.type}`);
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
nodemailerAttachments(attachments) {
|
|
1424
|
-
if (!attachments?.length) return void 0;
|
|
1425
|
-
return attachments.map((a) => ({
|
|
1426
|
-
filename: a.filename,
|
|
1427
|
-
content: toBuffer(a.content),
|
|
1428
|
-
contentType: a.contentType || "application/octet-stream"
|
|
1429
|
-
}));
|
|
1430
|
-
}
|
|
1431
|
-
async send(emailData) {
|
|
1432
|
-
try {
|
|
1433
|
-
const from = emailData.from || this.config.from;
|
|
1434
|
-
const to = emailData.to || this.config.to;
|
|
1435
|
-
const attachments = this.nodemailerAttachments(emailData.attachments);
|
|
1436
|
-
if (this.config.type === "AWS" && this.sesClient) {
|
|
1437
|
-
if (attachments?.length) {
|
|
1438
|
-
const composer = new MailComposer({
|
|
1439
|
-
from,
|
|
1440
|
-
to,
|
|
1441
|
-
subject: emailData.subject,
|
|
1442
|
-
html: emailData.html,
|
|
1443
|
-
text: emailData.text,
|
|
1444
|
-
attachments
|
|
1445
|
-
});
|
|
1446
|
-
const raw = await new Promise((resolve, reject) => {
|
|
1447
|
-
composer.compile().build((err, message) => {
|
|
1448
|
-
if (err) reject(err);
|
|
1449
|
-
else resolve(message);
|
|
1450
|
-
});
|
|
1451
|
-
});
|
|
1452
|
-
await this.sesClient.send(new clientSes.SendRawEmailCommand({
|
|
1453
|
-
RawMessage: {
|
|
1454
|
-
Data: raw
|
|
1455
|
-
}
|
|
1456
|
-
}));
|
|
1457
|
-
return true;
|
|
1458
|
-
}
|
|
1459
|
-
await this.sesClient.send(new clientSes.SendEmailCommand({
|
|
1460
|
-
Source: from,
|
|
1461
|
-
Destination: {
|
|
1462
|
-
ToAddresses: [
|
|
1463
|
-
to
|
|
1464
|
-
]
|
|
1465
|
-
},
|
|
1466
|
-
Message: {
|
|
1467
|
-
Subject: {
|
|
1468
|
-
Data: emailData.subject,
|
|
1469
|
-
Charset: "UTF-8"
|
|
1470
|
-
},
|
|
1471
|
-
Body: {
|
|
1472
|
-
Html: {
|
|
1473
|
-
Data: emailData.html,
|
|
1474
|
-
Charset: "UTF-8"
|
|
1475
|
-
},
|
|
1476
|
-
...emailData.text && {
|
|
1477
|
-
Text: {
|
|
1478
|
-
Data: emailData.text,
|
|
1479
|
-
Charset: "UTF-8"
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
}));
|
|
1485
|
-
return true;
|
|
1486
|
-
}
|
|
1487
|
-
if (this.transporter && (this.config.type === "SMTP" || this.config.type === "GMAIL" || this.config.type === "AWS" && this.awsViaSmtp)) {
|
|
1488
|
-
await this.transporter.sendMail({
|
|
1489
|
-
from,
|
|
1490
|
-
to,
|
|
1491
|
-
subject: emailData.subject,
|
|
1492
|
-
html: emailData.html,
|
|
1493
|
-
text: emailData.text,
|
|
1494
|
-
attachments
|
|
1495
|
-
});
|
|
1496
|
-
return true;
|
|
1497
|
-
}
|
|
1498
|
-
return false;
|
|
1499
|
-
} catch (error) {
|
|
1500
|
-
console.error("Email sending failed:", error);
|
|
1501
|
-
return false;
|
|
1502
|
-
}
|
|
1503
|
-
}
|
|
1504
|
-
renderTemplate(templateName, ctx) {
|
|
1505
|
-
return renderEmail(templateName, ctx, this.templateOptions);
|
|
1506
|
-
}
|
|
1507
|
-
/** Default notification recipient from plugin config (SMTP_TO / plugin `to`). */
|
|
1508
|
-
getDefaultTo() {
|
|
1509
|
-
return this.config.to;
|
|
1510
|
-
}
|
|
1511
|
-
};
|
|
1512
|
-
var emailTemplates = {
|
|
1513
|
-
formSubmission: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
|
|
1514
|
-
subject: `New Form Submission: ${data.formName}`,
|
|
1515
|
-
html: `<h2>New Form Submission</h2><p><strong>Form:</strong> ${data.formName}</p><p><strong>Contact:</strong> ${data.contactName} (${data.contactEmail})</p><pre>${JSON.stringify(data.formData, null, 2)}</pre>`,
|
|
1516
|
-
text: `New Form Submission
|
|
1517
|
-
Form: ${data.formName}
|
|
1518
|
-
Contact: ${data.contactName} (${data.contactEmail})
|
|
1519
|
-
${JSON.stringify(data.formData, null, 2)}`
|
|
1520
|
-
}), "formSubmission"),
|
|
1521
|
-
contactSubmission: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
|
|
1522
|
-
subject: `New Contact Form Submission from ${data.name}`,
|
|
1523
|
-
html: `<h2>New Contact Form Submission</h2><p><strong>Name:</strong> ${data.name}</p><p><strong>Email:</strong> ${data.email}</p>${data.phone ? `<p><strong>Phone:</strong> ${data.phone}</p>` : ""}${data.message ? `<p><strong>Message:</strong></p><p>${data.message}</p>` : ""}`,
|
|
1524
|
-
text: `New Contact Form Submission
|
|
1525
|
-
Name: ${data.name}
|
|
1526
|
-
Email: ${data.email}
|
|
1527
|
-
${data.phone ? `Phone: ${data.phone}
|
|
1528
|
-
` : ""}${data.message ? `Message: ${data.message}` : ""}`
|
|
1529
|
-
}), "contactSubmission"),
|
|
1530
|
-
passwordReset: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((data) => ({
|
|
1531
|
-
subject: "Reset your password",
|
|
1532
|
-
html: `<h2>Reset your password</h2><p>Click the link below to set a new password. This link expires in 1 hour.</p><p><a href="${data.resetLink}">${data.resetLink}</a></p>`,
|
|
1533
|
-
text: `Reset your password: ${data.resetLink}
|
|
1534
|
-
|
|
1535
|
-
This link expires in 1 hour.`
|
|
1536
|
-
}), "passwordReset")
|
|
1537
|
-
};
|
|
1538
|
-
|
|
1539
574
|
// src/lib/email-recipients.ts
|
|
1540
575
|
function parseEmailRecipientsFromConfig(raw) {
|
|
1541
576
|
if (raw == null || raw === "") return [];
|
|
@@ -1975,14 +1010,14 @@ async function sendChatLeadEmail(cms, emailSettings, brandingSettings, input) {
|
|
|
1975
1010
|
recipients,
|
|
1976
1011
|
source: input.recipients?.length ? "intent_email_to" : "crm_or_smtp_fallback"
|
|
1977
1012
|
});
|
|
1978
|
-
const companyDetails =
|
|
1013
|
+
const companyDetails = chunkJLDHK2QE_cjs.mergeEmailLayoutCompanyDetails(brandingSettings, emailSettings);
|
|
1979
1014
|
const ctx = {
|
|
1980
1015
|
...input,
|
|
1981
1016
|
companyDetails: input.companyDetails ?? companyDetails
|
|
1982
1017
|
};
|
|
1983
1018
|
let anySent = false;
|
|
1984
1019
|
for (const to of recipients) {
|
|
1985
|
-
await
|
|
1020
|
+
await chunkMXABMVF7_cjs.queueEmail(cms, {
|
|
1986
1021
|
to,
|
|
1987
1022
|
templateName: "chatLead",
|
|
1988
1023
|
ctx
|
|
@@ -2057,7 +1092,7 @@ function emailPlugin(config) {
|
|
|
2057
1092
|
context.logger.warn("Email plugin skipped: AWS SES requires AWS_SES_REGION or AWS_REGION");
|
|
2058
1093
|
return void 0;
|
|
2059
1094
|
}
|
|
2060
|
-
if (type === "AWS" && merged.region && merged.user && merged.password && !sesRegionSupportsSmtp(merged.region)) {
|
|
1095
|
+
if (type === "AWS" && merged.region && merged.user && merged.password && !chunkYVP4NQII_cjs.sesRegionSupportsSmtp(merged.region)) {
|
|
2061
1096
|
context.logger.warn(`Email plugin: region ${merged.region} has no SES SMTP endpoint (ENOTFOUND). Set AWS_SES_REGION=ap-south-1 and create SMTP credentials in that SES region.`);
|
|
2062
1097
|
}
|
|
2063
1098
|
if (type === "AWS" && !(merged.user && merged.password || merged.accessKeyId && merged.secretAccessKey)) {
|
|
@@ -2072,13 +1107,13 @@ function emailPlugin(config) {
|
|
|
2072
1107
|
context.logger.warn("Email plugin skipped: SMTP_TYPE=SMTP requires SMTP_HOST (or use SMTP_TYPE=AWS with AWS_REGION and SES SMTP credentials)");
|
|
2073
1108
|
return void 0;
|
|
2074
1109
|
}
|
|
2075
|
-
const resolvedHost = type === "AWS" && merged.region ? merged.host || sesSmtpHost(merged.region) : merged.host;
|
|
1110
|
+
const resolvedHost = type === "AWS" && merged.region ? merged.host || chunkYVP4NQII_cjs.sesSmtpHost(merged.region) : merged.host;
|
|
2076
1111
|
context.logger.info("Email plugin initialized", {
|
|
2077
1112
|
type,
|
|
2078
1113
|
host: resolvedHost,
|
|
2079
1114
|
region: merged.region
|
|
2080
1115
|
});
|
|
2081
|
-
return new EmailService(merged);
|
|
1116
|
+
return new chunkYVP4NQII_cjs.EmailService(merged);
|
|
2082
1117
|
}
|
|
2083
1118
|
};
|
|
2084
1119
|
}
|
|
@@ -2237,14 +1272,108 @@ async function validateFcmCredentials(dataSource, entityMap) {
|
|
|
2237
1272
|
}
|
|
2238
1273
|
chunkUSNT2KNT_cjs.__name(validateFcmCredentials, "validateFcmCredentials");
|
|
2239
1274
|
|
|
1275
|
+
// src/lib/order-notification-defaults.ts
|
|
1276
|
+
var ORDER_NOTIFICATION_DEFAULTS = {
|
|
1277
|
+
email: {
|
|
1278
|
+
pending: {
|
|
1279
|
+
subject: "Your Order #{{orderNumber}} is Pending",
|
|
1280
|
+
body: "<p>Thank you for your order! It is currently pending.</p>"
|
|
1281
|
+
},
|
|
1282
|
+
order_pending: {
|
|
1283
|
+
subject: "Your Order #{{orderNumber}} is Pending",
|
|
1284
|
+
body: "<p>Thank you for your order! It is currently pending.</p>"
|
|
1285
|
+
},
|
|
1286
|
+
confirmed: {
|
|
1287
|
+
subject: "Your Order #{{orderNumber}} is Confirmed",
|
|
1288
|
+
body: "<p>Your order has been confirmed!</p>"
|
|
1289
|
+
},
|
|
1290
|
+
order_confirmed: {
|
|
1291
|
+
subject: "Your Order #{{orderNumber}} is Confirmed",
|
|
1292
|
+
body: "<p>Your order has been confirmed!</p>"
|
|
1293
|
+
},
|
|
1294
|
+
order_placed: {
|
|
1295
|
+
subject: "Your Order #{{orderNumber}} is Placed",
|
|
1296
|
+
body: "<p>Thank you for your order! Your order #{{orderNumber}} has been placed.</p>"
|
|
1297
|
+
},
|
|
1298
|
+
processing: {
|
|
1299
|
+
subject: "Your Order #{{orderNumber}} is Processing",
|
|
1300
|
+
body: "<p>Your order is now being processed by our team.</p>"
|
|
1301
|
+
},
|
|
1302
|
+
order_processing: {
|
|
1303
|
+
subject: "Your Order #{{orderNumber}} is Processing",
|
|
1304
|
+
body: "<p>Your order is now being processed by our team.</p>"
|
|
1305
|
+
},
|
|
1306
|
+
completed: {
|
|
1307
|
+
subject: "Your Order #{{orderNumber}} is Completed",
|
|
1308
|
+
body: "<p>Your order has been completed. Thank you!</p>"
|
|
1309
|
+
},
|
|
1310
|
+
order_completed: {
|
|
1311
|
+
subject: "Your Order #{{orderNumber}} is Completed",
|
|
1312
|
+
body: "<p>Your order has been completed. Thank you!</p>"
|
|
1313
|
+
},
|
|
1314
|
+
cancelled: {
|
|
1315
|
+
subject: "Your Order #{{orderNumber}} is Cancelled",
|
|
1316
|
+
body: "<p>Your order has been cancelled.</p>"
|
|
1317
|
+
},
|
|
1318
|
+
order_cancelled: {
|
|
1319
|
+
subject: "Your Order #{{orderNumber}} is Cancelled",
|
|
1320
|
+
body: "<p>Your order has been cancelled.</p>"
|
|
1321
|
+
},
|
|
1322
|
+
payment_success: {
|
|
1323
|
+
subject: "Payment Received for Order #{{orderNumber}}",
|
|
1324
|
+
body: "<p>Payment of {{currency}} {{total}} has been successfully received for order #{{orderNumber}}.</p>"
|
|
1325
|
+
},
|
|
1326
|
+
payment_failed: {
|
|
1327
|
+
subject: "Payment Failed for Order #{{orderNumber}}",
|
|
1328
|
+
body: "<p>Payment for order #{{orderNumber}} could not be completed.</p>"
|
|
1329
|
+
},
|
|
1330
|
+
payment_refund_initiated: {
|
|
1331
|
+
subject: "Refund Initiated for Order #{{orderNumber}}",
|
|
1332
|
+
body: "<p>A refund of {{currency}} {{refundAmount}} has been initiated for order #{{orderNumber}}.</p>"
|
|
1333
|
+
}
|
|
1334
|
+
},
|
|
1335
|
+
sms: {
|
|
1336
|
+
pending: "Your Order #{{orderNumber}} is pending. Total: {{total}}.",
|
|
1337
|
+
order_pending: "Your Order #{{orderNumber}} is pending. Total: {{total}}.",
|
|
1338
|
+
confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
|
|
1339
|
+
order_confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
|
|
1340
|
+
order_placed: "Your Order #{{orderNumber}} is placed. Total: {{total}}.",
|
|
1341
|
+
processing: "Your Order #{{orderNumber}} is now processing.",
|
|
1342
|
+
order_processing: "Your Order #{{orderNumber}} is now processing.",
|
|
1343
|
+
completed: "Your Order #{{orderNumber}} has been completed. Thank you!",
|
|
1344
|
+
order_completed: "Your Order #{{orderNumber}} has been completed. Thank you!",
|
|
1345
|
+
cancelled: "Your Order #{{orderNumber}} is cancelled.",
|
|
1346
|
+
order_cancelled: "Your Order #{{orderNumber}} is cancelled.",
|
|
1347
|
+
payment_success: "Payment received for Order #{{orderNumber}}. Amount: {{currency}} {{total}}.",
|
|
1348
|
+
payment_failed: "Payment failed for Order #{{orderNumber}}.",
|
|
1349
|
+
payment_refund_initiated: "Refund of {{currency}} {{refundAmount}} initiated for Order #{{orderNumber}}."
|
|
1350
|
+
},
|
|
1351
|
+
whatsapp: {
|
|
1352
|
+
pending: "Hi {{customerName}}, your Order #{{orderNumber}} is pending.",
|
|
1353
|
+
order_pending: "Hi {{customerName}}, your Order #{{orderNumber}} is pending.",
|
|
1354
|
+
confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
|
|
1355
|
+
order_confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
|
|
1356
|
+
order_placed: "Hi {{customerName}}, your Order #{{orderNumber}} is placed.",
|
|
1357
|
+
processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
|
|
1358
|
+
order_processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
|
|
1359
|
+
completed: "Hi {{customerName}}, your Order #{{orderNumber}} is completed.",
|
|
1360
|
+
order_completed: "Hi {{customerName}}, your Order #{{orderNumber}} is completed.",
|
|
1361
|
+
cancelled: "Hi {{customerName}}, your Order #{{orderNumber}} is cancelled.",
|
|
1362
|
+
order_cancelled: "Hi {{customerName}}, your Order #{{orderNumber}} is cancelled.",
|
|
1363
|
+
payment_success: "Hi {{customerName}}, payment for Order #{{orderNumber}} was successful.",
|
|
1364
|
+
payment_failed: "Hi {{customerName}}, payment for Order #{{orderNumber}} has failed.",
|
|
1365
|
+
payment_refund_initiated: "Hi {{customerName}}, a refund has been initiated for Order #{{orderNumber}}."
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
|
|
2240
1369
|
// src/lib/order-notification-dispatcher.ts
|
|
2241
1370
|
var initialized = false;
|
|
2242
|
-
function
|
|
1371
|
+
function maskPhone2(raw) {
|
|
2243
1372
|
const digits = String(raw ?? "").replace(/\D/g, "");
|
|
2244
1373
|
if (digits.length <= 4) return "(none)";
|
|
2245
1374
|
return `***${digits.slice(-4)}`;
|
|
2246
1375
|
}
|
|
2247
|
-
chunkUSNT2KNT_cjs.__name(
|
|
1376
|
+
chunkUSNT2KNT_cjs.__name(maskPhone2, "maskPhone");
|
|
2248
1377
|
async function getSettingsGroup(dataSource, entityMap, group) {
|
|
2249
1378
|
if (!entityMap.configs) return {};
|
|
2250
1379
|
const rows = await dataSource.getRepository(entityMap.configs).find({
|
|
@@ -2273,28 +1402,20 @@ async function isWhatsappNotificationsEnabled(dataSource, entityMap) {
|
|
|
2273
1402
|
chunkUSNT2KNT_cjs.__name(isWhatsappNotificationsEnabled, "isWhatsappNotificationsEnabled");
|
|
2274
1403
|
async function isTriggerCatalogEnabled(triggerKey, dataSource, entityMap) {
|
|
2275
1404
|
if (!entityMap.order_notification_triggers) {
|
|
2276
|
-
|
|
2277
|
-
return false;
|
|
1405
|
+
return true;
|
|
2278
1406
|
}
|
|
2279
1407
|
try {
|
|
2280
1408
|
const row = await dataSource.getRepository(entityMap.order_notification_triggers).findOne({
|
|
2281
1409
|
where: {
|
|
2282
|
-
triggerKey
|
|
2283
|
-
enabled: true
|
|
1410
|
+
triggerKey
|
|
2284
1411
|
}
|
|
2285
1412
|
});
|
|
2286
1413
|
if (!row) {
|
|
2287
|
-
|
|
2288
|
-
triggerKey
|
|
2289
|
-
});
|
|
1414
|
+
return true;
|
|
2290
1415
|
}
|
|
2291
|
-
return
|
|
1416
|
+
return row.enabled !== false;
|
|
2292
1417
|
} catch (err) {
|
|
2293
|
-
|
|
2294
|
-
triggerKey,
|
|
2295
|
-
err
|
|
2296
|
-
});
|
|
2297
|
-
return false;
|
|
1418
|
+
return true;
|
|
2298
1419
|
}
|
|
2299
1420
|
}
|
|
2300
1421
|
chunkUSNT2KNT_cjs.__name(isTriggerCatalogEnabled, "isTriggerCatalogEnabled");
|
|
@@ -2322,8 +1443,24 @@ async function loadBoundTemplate(triggerKey, channel, audienceType = "customers"
|
|
|
2322
1443
|
}
|
|
2323
1444
|
}).catch(() => null);
|
|
2324
1445
|
}
|
|
2325
|
-
if (!binding) {
|
|
2326
|
-
|
|
1446
|
+
if (!binding || binding.messageTemplateId == null) {
|
|
1447
|
+
const defChannel = ORDER_NOTIFICATION_DEFAULTS[channel];
|
|
1448
|
+
const def = defChannel ? defChannel[triggerKey] : null;
|
|
1449
|
+
if (def) {
|
|
1450
|
+
if (typeof def === "string") {
|
|
1451
|
+
return {
|
|
1452
|
+
body: def,
|
|
1453
|
+
externalTemplateRef: null,
|
|
1454
|
+
subject: "Order Notification"
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
return {
|
|
1458
|
+
body: def.body,
|
|
1459
|
+
subject: def.subject,
|
|
1460
|
+
externalTemplateRef: null
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
console.warn("[order-notifications] no order_notification_bindings row or default template", {
|
|
2327
1464
|
triggerKey,
|
|
2328
1465
|
channel,
|
|
2329
1466
|
audienceType
|
|
@@ -2331,14 +1468,6 @@ async function loadBoundTemplate(triggerKey, channel, audienceType = "customers"
|
|
|
2331
1468
|
return null;
|
|
2332
1469
|
}
|
|
2333
1470
|
const templateId = binding.messageTemplateId;
|
|
2334
|
-
if (templateId == null) {
|
|
2335
|
-
console.warn("[order-notifications] binding exists but no template selected", {
|
|
2336
|
-
triggerKey,
|
|
2337
|
-
channel,
|
|
2338
|
-
audienceType
|
|
2339
|
-
});
|
|
2340
|
-
return null;
|
|
2341
|
-
}
|
|
2342
1471
|
const template = await dataSource.getRepository(entityMap.message_templates).findOne({
|
|
2343
1472
|
where: {
|
|
2344
1473
|
id: templateId,
|
|
@@ -2348,6 +1477,22 @@ async function loadBoundTemplate(triggerKey, channel, audienceType = "customers"
|
|
|
2348
1477
|
}
|
|
2349
1478
|
}).catch(() => null);
|
|
2350
1479
|
if (!template) {
|
|
1480
|
+
const defChannel = ORDER_NOTIFICATION_DEFAULTS[channel];
|
|
1481
|
+
const def = defChannel ? defChannel[triggerKey] : null;
|
|
1482
|
+
if (def) {
|
|
1483
|
+
if (typeof def === "string") {
|
|
1484
|
+
return {
|
|
1485
|
+
body: def,
|
|
1486
|
+
externalTemplateRef: null,
|
|
1487
|
+
subject: "Order Notification"
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
return {
|
|
1491
|
+
body: def.body,
|
|
1492
|
+
subject: def.subject,
|
|
1493
|
+
externalTemplateRef: null
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
2351
1496
|
console.warn("[order-notifications] bound template not found or disabled", {
|
|
2352
1497
|
triggerKey,
|
|
2353
1498
|
channel,
|
|
@@ -2358,6 +1503,22 @@ async function loadBoundTemplate(triggerKey, channel, audienceType = "customers"
|
|
|
2358
1503
|
}
|
|
2359
1504
|
return template;
|
|
2360
1505
|
} catch (err) {
|
|
1506
|
+
const defChannel = ORDER_NOTIFICATION_DEFAULTS[channel];
|
|
1507
|
+
const def = defChannel ? defChannel[triggerKey] : null;
|
|
1508
|
+
if (def) {
|
|
1509
|
+
if (typeof def === "string") {
|
|
1510
|
+
return {
|
|
1511
|
+
body: def,
|
|
1512
|
+
externalTemplateRef: null,
|
|
1513
|
+
subject: "Order Notification"
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
return {
|
|
1517
|
+
body: def.body,
|
|
1518
|
+
subject: def.subject,
|
|
1519
|
+
externalTemplateRef: null
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
2361
1522
|
console.warn("[order-notifications] loadBoundTemplate query failed", {
|
|
2362
1523
|
triggerKey,
|
|
2363
1524
|
channel,
|
|
@@ -2618,11 +1779,12 @@ chunkUSNT2KNT_cjs.__name(formatBodyForEmailHtml, "formatBodyForEmailHtml");
|
|
|
2618
1779
|
async function sendEmailForTrigger(triggerKey, orderId, audienceType, variables, contactEmail, dataSource, entityMap, getCms) {
|
|
2619
1780
|
const template = await loadBoundTemplate(triggerKey, "email", audienceType, dataSource, entityMap);
|
|
2620
1781
|
if (!template) return;
|
|
2621
|
-
const [branding, emailSettings] = await Promise.all([
|
|
1782
|
+
const [branding, emailSettings, storeSettings] = await Promise.all([
|
|
2622
1783
|
getSettingsGroup(dataSource, entityMap, "branding"),
|
|
2623
|
-
getSettingsGroup(dataSource, entityMap, "email")
|
|
1784
|
+
getSettingsGroup(dataSource, entityMap, "email"),
|
|
1785
|
+
getSettingsGroup(dataSource, entityMap, "store")
|
|
2624
1786
|
]);
|
|
2625
|
-
const companyDetails =
|
|
1787
|
+
const companyDetails = chunkJLDHK2QE_cjs.mergeEmailLayoutCompanyDetails(branding, emailSettings, storeSettings);
|
|
2626
1788
|
const templateContent = (template.body ?? "") + (template.subject ?? "");
|
|
2627
1789
|
const shouldAttachInvoice = /\{\{\s*(invoiceAttachment|invoicePdf|invoice|attachInvoice)\s*\}\}/i.test(templateContent);
|
|
2628
1790
|
const cleanVariables = {
|
|
@@ -2636,12 +1798,12 @@ async function sendEmailForTrigger(triggerKey, orderId, audienceType, variables,
|
|
|
2636
1798
|
const subject = chunkOOTLNH63_cjs.applyTemplateVars(template.subject ?? "Order update", cleanVariables);
|
|
2637
1799
|
const rawBody = chunkOOTLNH63_cjs.applyTemplateVars(template.body, cleanVariables);
|
|
2638
1800
|
const bodyHtml = formatBodyForEmailHtml(rawBody);
|
|
2639
|
-
const html = renderLayout({
|
|
1801
|
+
const html = chunkYVP4NQII_cjs.renderLayout({
|
|
2640
1802
|
bodyHtml,
|
|
2641
1803
|
companyDetails
|
|
2642
1804
|
});
|
|
2643
1805
|
const cms = await getCms();
|
|
2644
|
-
await
|
|
1806
|
+
await chunkMXABMVF7_cjs.queueEmail(cms, {
|
|
2645
1807
|
to: contactEmail,
|
|
2646
1808
|
subject,
|
|
2647
1809
|
html,
|
|
@@ -2681,7 +1843,7 @@ async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dat
|
|
|
2681
1843
|
});
|
|
2682
1844
|
return;
|
|
2683
1845
|
}
|
|
2684
|
-
const sent = await queueWhatsApp(cms, {
|
|
1846
|
+
const sent = await chunkH3IVLIPR_cjs.queueWhatsApp(cms, {
|
|
2685
1847
|
to: phone.trim(),
|
|
2686
1848
|
templateKey: triggerKey,
|
|
2687
1849
|
externalTemplateRef: metaTemplateName,
|
|
@@ -2700,72 +1862,17 @@ async function sendWhatsAppForTrigger(triggerKey, orderId, phone, variables, dat
|
|
|
2700
1862
|
console.log("[order-notifications] WhatsApp queued/sent", {
|
|
2701
1863
|
triggerKey,
|
|
2702
1864
|
orderId,
|
|
2703
|
-
phone:
|
|
1865
|
+
phone: maskPhone2(phone)
|
|
2704
1866
|
});
|
|
2705
1867
|
}
|
|
2706
1868
|
}
|
|
2707
1869
|
chunkUSNT2KNT_cjs.__name(sendWhatsAppForTrigger, "sendWhatsAppForTrigger");
|
|
2708
|
-
async function isOrderPaymentConfirmed(orderId, dataSource, entityMap) {
|
|
2709
|
-
if (!entityMap.orders) return true;
|
|
2710
|
-
try {
|
|
2711
|
-
const orderRepo = dataSource.getRepository(entityMap.orders);
|
|
2712
|
-
const order = await orderRepo.findOne({
|
|
2713
|
-
where: {
|
|
2714
|
-
id: orderId
|
|
2715
|
-
}
|
|
2716
|
-
});
|
|
2717
|
-
if (!order) return false;
|
|
2718
|
-
const o = order;
|
|
2719
|
-
const status = String(o.status ?? "").toLowerCase().trim();
|
|
2720
|
-
const total = Number(o.total ?? 0);
|
|
2721
|
-
if (total <= 0) return true;
|
|
2722
|
-
const confirmedStatuses = [
|
|
2723
|
-
"confirmed",
|
|
2724
|
-
"completed",
|
|
2725
|
-
"processing",
|
|
2726
|
-
"paid",
|
|
2727
|
-
"fulfilled",
|
|
2728
|
-
"shipped"
|
|
2729
|
-
];
|
|
2730
|
-
if (confirmedStatuses.includes(status)) {
|
|
2731
|
-
return true;
|
|
2732
|
-
}
|
|
2733
|
-
if (entityMap.payments) {
|
|
2734
|
-
const paymentRepo = dataSource.getRepository(entityMap.payments);
|
|
2735
|
-
const payment = await paymentRepo.findOne({
|
|
2736
|
-
where: {
|
|
2737
|
-
orderId
|
|
2738
|
-
}
|
|
2739
|
-
});
|
|
2740
|
-
if (payment) {
|
|
2741
|
-
const pStatus = String(payment.status ?? "").toLowerCase().trim();
|
|
2742
|
-
if (pStatus === "paid" || pStatus === "captured" || pStatus === "completed") {
|
|
2743
|
-
return true;
|
|
2744
|
-
}
|
|
2745
|
-
}
|
|
2746
|
-
}
|
|
2747
|
-
return false;
|
|
2748
|
-
} catch (err) {
|
|
2749
|
-
console.error("[order-notifications] failed to check order payment confirmation", err);
|
|
2750
|
-
return false;
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
chunkUSNT2KNT_cjs.__name(isOrderPaymentConfirmed, "isOrderPaymentConfirmed");
|
|
2754
1870
|
async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms) {
|
|
2755
1871
|
console.log("[order-notifications] received", {
|
|
2756
1872
|
triggerKey,
|
|
2757
1873
|
orderId: payload.orderId,
|
|
2758
|
-
phone:
|
|
1874
|
+
phone: maskPhone2(payload.customerPhone)
|
|
2759
1875
|
});
|
|
2760
|
-
if (triggerKey === "order_placed") {
|
|
2761
|
-
const isConfirmed = await isOrderPaymentConfirmed(payload.orderId, dataSource, entityMap);
|
|
2762
|
-
if (!isConfirmed) {
|
|
2763
|
-
console.log("[order-notifications] skip order_placed: order payment not confirmed yet", {
|
|
2764
|
-
orderId: payload.orderId
|
|
2765
|
-
});
|
|
2766
|
-
return;
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
1876
|
const [emailChannelEnabled, whatsappChannelEnabled, pushChannelEnabled] = await Promise.all([
|
|
2770
1877
|
isEmailNotificationsEnabled(dataSource, entityMap),
|
|
2771
1878
|
isWhatsappNotificationsEnabled(dataSource, entityMap),
|
|
@@ -2842,13 +1949,15 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
|
|
|
2842
1949
|
}
|
|
2843
1950
|
}
|
|
2844
1951
|
chunkUSNT2KNT_cjs.__name(handleTrigger, "handleTrigger");
|
|
1952
|
+
var globalGetCms = null;
|
|
2845
1953
|
function initWhatsappTriggerDispatcher(deps) {
|
|
1954
|
+
if (deps?.getCms) globalGetCms = deps.getCms;
|
|
2846
1955
|
if (initialized) return;
|
|
2847
1956
|
initialized = true;
|
|
2848
1957
|
const { dataSource, entityMap, getCms } = deps;
|
|
2849
1958
|
void dataSource.query(`ALTER TYPE "order_notification_bindings_channel_enum" ADD VALUE IF NOT EXISTS 'mobile'`).catch(() => {
|
|
2850
1959
|
});
|
|
2851
|
-
|
|
1960
|
+
chunkUEE4PTTB_cjs.notificationTriggerEmitter.onAnyTrigger((triggerKey, payload) => {
|
|
2852
1961
|
void handleTrigger(triggerKey, payload, dataSource, entityMap, getCms).catch((err) => {
|
|
2853
1962
|
console.error("[order-notifications]", triggerKey, err);
|
|
2854
1963
|
});
|
|
@@ -2856,6 +1965,13 @@ function initWhatsappTriggerDispatcher(deps) {
|
|
|
2856
1965
|
console.log("[order-notifications] dispatcher initialized (email + WhatsApp, unified via order_notification_bindings)");
|
|
2857
1966
|
}
|
|
2858
1967
|
chunkUSNT2KNT_cjs.__name(initWhatsappTriggerDispatcher, "initWhatsappTriggerDispatcher");
|
|
1968
|
+
async function dispatchOrderNotificationDirectly(triggerKey, payload, dataSource, entityMap, getCms) {
|
|
1969
|
+
const cmsResolver = getCms ?? globalGetCms ?? (async () => ({
|
|
1970
|
+
getPlugin: /* @__PURE__ */ chunkUSNT2KNT_cjs.__name(() => null, "getPlugin")
|
|
1971
|
+
}));
|
|
1972
|
+
await handleTrigger(triggerKey, payload, dataSource, entityMap, cmsResolver);
|
|
1973
|
+
}
|
|
1974
|
+
chunkUSNT2KNT_cjs.__name(dispatchOrderNotificationDirectly, "dispatchOrderNotificationDirectly");
|
|
2859
1975
|
async function resendOrderNotification(triggerKey, orderId, deps) {
|
|
2860
1976
|
if (!deps.entityMap.orders) return {
|
|
2861
1977
|
ok: false,
|
|
@@ -2906,31 +2022,46 @@ async function resendOrderNotification(triggerKey, orderId, deps) {
|
|
|
2906
2022
|
};
|
|
2907
2023
|
}
|
|
2908
2024
|
chunkUSNT2KNT_cjs.__name(resendOrderNotification, "resendOrderNotification");
|
|
2025
|
+
async function emitOrderNotificationTrigger(dataSource, entityMap, triggerKey, payload) {
|
|
2026
|
+
try {
|
|
2027
|
+
const settings = await getSettingsGroup(dataSource, entityMap, "event_notifications");
|
|
2028
|
+
const enabledChannels = [];
|
|
2029
|
+
if (settings.email_enabled !== "false" && settings.email_enabled !== "0" && settings.email_customer_enabled !== "false") enabledChannels.push("email");
|
|
2030
|
+
if (settings.whatsapp_enabled !== "false" && settings.whatsapp_enabled !== "0") enabledChannels.push("whatsapp");
|
|
2031
|
+
if (settings.sms_enabled !== "false" && settings.sms_enabled !== "0") enabledChannels.push("sms");
|
|
2032
|
+
if (settings.push_enabled !== "false" && settings.push_enabled !== "0") enabledChannels.push("push");
|
|
2033
|
+
chunkUEE4PTTB_cjs.notificationTriggerEmitter.emitTrigger(triggerKey, payload);
|
|
2034
|
+
return {
|
|
2035
|
+
ok: true,
|
|
2036
|
+
channels: enabledChannels
|
|
2037
|
+
};
|
|
2038
|
+
} catch (err) {
|
|
2039
|
+
return {
|
|
2040
|
+
ok: false,
|
|
2041
|
+
channels: []
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
chunkUSNT2KNT_cjs.__name(emitOrderNotificationTrigger, "emitOrderNotificationTrigger");
|
|
2909
2046
|
|
|
2910
2047
|
exports.CHAT_EMAIL_LOG = CHAT_EMAIL_LOG;
|
|
2911
|
-
exports.EmailService = EmailService;
|
|
2912
2048
|
exports.WhatsAppService = WhatsAppService;
|
|
2913
2049
|
exports.buildChatbotSystemPromptWithEmailTool = buildChatbotSystemPromptWithEmailTool;
|
|
2914
2050
|
exports.buildEventOrderTemplateVariables = buildEventOrderTemplateVariables;
|
|
2915
2051
|
exports.buildTranscriptForLeadEmail = buildTranscriptForLeadEmail;
|
|
2916
2052
|
exports.detectChatLeadIntent = detectChatLeadIntent;
|
|
2053
|
+
exports.dispatchOrderNotificationDirectly = dispatchOrderNotificationDirectly;
|
|
2917
2054
|
exports.emailPlugin = emailPlugin;
|
|
2918
|
-
exports.
|
|
2055
|
+
exports.emitOrderNotificationTrigger = emitOrderNotificationTrigger;
|
|
2919
2056
|
exports.getSettingsGroup = getSettingsGroup;
|
|
2920
2057
|
exports.initWhatsappTriggerDispatcher = initWhatsappTriggerDispatcher;
|
|
2921
2058
|
exports.isWhatsAppPluginEnabled = isWhatsAppPluginEnabled;
|
|
2922
2059
|
exports.joinRecipientsForSend = joinRecipientsForSend;
|
|
2923
|
-
exports.mergeWhatsAppConfigLayers = mergeWhatsAppConfigLayers;
|
|
2924
2060
|
exports.parseEmailRecipientsFromConfig = parseEmailRecipientsFromConfig;
|
|
2925
2061
|
exports.parseIntentRecipientEmails = parseIntentRecipientEmails;
|
|
2926
|
-
exports.queueWhatsApp = queueWhatsApp;
|
|
2927
|
-
exports.registerWhatsAppQueueProcessor = registerWhatsAppQueueProcessor;
|
|
2928
|
-
exports.renderEmail = renderEmail;
|
|
2929
|
-
exports.renderLayout = renderLayout;
|
|
2930
2062
|
exports.resendOrderNotification = resendOrderNotification;
|
|
2931
2063
|
exports.resolveChatEmailToolSettings = resolveChatEmailToolSettings;
|
|
2932
2064
|
exports.sendChatLeadEmail = sendChatLeadEmail;
|
|
2933
2065
|
exports.sendFcmPushNotification = sendFcmPushNotification;
|
|
2934
2066
|
exports.serializeEmailRecipients = serializeEmailRecipients;
|
|
2935
2067
|
exports.validateFcmCredentials = validateFcmCredentials;
|
|
2936
|
-
exports.whatsAppConfigured = whatsAppConfigured;
|