@infuro/cms-core 1.0.63 → 1.0.64

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.
Files changed (39) hide show
  1. package/dist/admin.cjs +183 -65
  2. package/dist/admin.js +183 -65
  3. package/dist/api.cjs +38 -38
  4. package/dist/api.d.cts +2 -0
  5. package/dist/api.d.ts +2 -0
  6. package/dist/api.js +6 -6
  7. package/dist/{chunk-KAOHOHCA.js → chunk-6YOQ7G5O.js} +92 -51
  8. package/dist/{chunk-DTUL3Q5L.js → chunk-DXERXWBR.js} +1 -1
  9. package/dist/{chunk-CJEOH6QC.cjs → chunk-FF45VZMM.cjs} +99 -73
  10. package/dist/{chunk-7L556RGL.cjs → chunk-GLD263KW.cjs} +3 -3
  11. package/dist/{chunk-77YWLP4X.js → chunk-K6C3ZSBZ.js} +56 -30
  12. package/dist/{chunk-EWXG4E74.cjs → chunk-M25DGZ5J.cjs} +111 -51
  13. package/dist/{chunk-BWOITUNI.js → chunk-OJAYCNE5.js} +103 -43
  14. package/dist/{chunk-MWEK7N3F.js → chunk-OPJEV44S.js} +2 -2
  15. package/dist/{chunk-RHYVNRYG.cjs → chunk-PX3BSCIG.cjs} +1 -1
  16. package/dist/{chunk-RXTUSF3R.js → chunk-TU23GJPJ.js} +2 -2
  17. package/dist/{chunk-JLDHK2QE.cjs → chunk-TZEMLHSP.cjs} +20 -14
  18. package/dist/{chunk-YVP4NQII.cjs → chunk-VM6OVUUM.cjs} +93 -52
  19. package/dist/{chunk-MXABMVF7.cjs → chunk-XFDIIVZB.cjs} +4 -4
  20. package/dist/{chunk-3VZAAQUV.js → chunk-YRCKEGL2.js} +20 -14
  21. package/dist/{email-queue-VTYL2KFA.cjs → email-queue-4OY45JIU.cjs} +7 -7
  22. package/dist/{email-queue-YPRSVG56.js → email-queue-HGXB2FIS.js} +3 -3
  23. package/dist/{email-service-ATCPJFWG.cjs → email-service-5O24BSFR.cjs} +7 -7
  24. package/dist/{email-service-DPKVQOKP.js → email-service-ADDF52PJ.js} +2 -2
  25. package/dist/{emit-order-notification-trigger-XVG2B5ZF.js → emit-order-notification-trigger-MFCBAE7L.js} +1 -1
  26. package/dist/{emit-order-notification-trigger-WI4HGMKH.cjs → emit-order-notification-trigger-ZRRGCQGB.cjs} +3 -3
  27. package/dist/{erp-order-invoice-3UGWLICU.js → erp-order-invoice-H6HCJ7YS.js} +2 -2
  28. package/dist/{erp-order-invoice-KJYJNWMG.cjs → erp-order-invoice-YUSPZ7CE.cjs} +6 -6
  29. package/dist/index.cjs +272 -258
  30. package/dist/index.d.cts +38 -19
  31. package/dist/index.d.ts +38 -19
  32. package/dist/index.js +40 -26
  33. package/dist/migrations/1783000000000-AddOrderPendingProcessingCompletedTriggers.ts +15 -39
  34. package/dist/migrations/1783300000000-SeedOrderNotificationTriggersAndTemplates.ts +155 -0
  35. package/dist/{order-completion-otp-handlers-WGM7HWG4.js → order-completion-otp-handlers-3W5I3LON.js} +4 -4
  36. package/dist/{order-completion-otp-handlers-IMMQGHDR.cjs → order-completion-otp-handlers-RF3DLNV7.cjs} +6 -6
  37. package/dist/{order-notification-dispatcher-JKFQJYGG.js → order-notification-dispatcher-ID2PDEXD.js} +5 -5
  38. package/dist/{order-notification-dispatcher-5TUFCDU7.cjs → order-notification-dispatcher-RNEQB3V7.cjs} +10 -10
  39. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { queueWhatsApp } from './chunk-GWDI752Q.js';
2
2
  import { notificationTriggerEmitter } from './chunk-AYWDQFJZ.js';
3
- import { sesRegionSupportsSmtp, sesSmtpHost, EmailService, renderLayout } from './chunk-KAOHOHCA.js';
4
- import { queueEmail } from './chunk-RXTUSF3R.js';
5
- import { mergeEmailLayoutCompanyDetails } from './chunk-3VZAAQUV.js';
3
+ import { sesRegionSupportsSmtp, sesSmtpHost, EmailService, renderLayout } from './chunk-6YOQ7G5O.js';
4
+ import { queueEmail } from './chunk-TU23GJPJ.js';
5
+ import { mergeEmailLayoutCompanyDetails } from './chunk-YRCKEGL2.js';
6
6
  import { applyTemplateVars } from './chunk-CRFV5WJK.js';
7
7
  import { __name } from './chunk-SHUYVCID.js';
8
8
  import * as crypto from 'crypto';
@@ -201,7 +201,10 @@ async function loadOrderNotificationContexts(orderId, deps) {
201
201
  deleted: false
202
202
  },
203
203
  relations: [
204
- "contact"
204
+ "contact",
205
+ "assignedUser",
206
+ "shippingAddress",
207
+ "billingAddress"
205
208
  ]
206
209
  });
207
210
  if (!order) return null;
@@ -256,7 +259,7 @@ async function loadOrderNotificationContexts(orderId, deps) {
256
259
  }
257
260
  }
258
261
  const effectiveEvent = eventsEnabled ? event : void 0;
259
- const variables = effectiveEvent ? buildEventOrderTemplateVariables({
262
+ const baseVariables = effectiveEvent ? buildEventOrderTemplateVariables({
260
263
  event: effectiveEvent,
261
264
  order: {
262
265
  orderNumber: o.orderNumber,
@@ -288,13 +291,42 @@ async function loadOrderNotificationContexts(orderId, deps) {
288
291
  orderLines,
289
292
  siteUrl
290
293
  });
294
+ const assignedUser = o.assignedUser ?? null;
295
+ const assigneeName = assignedUser?.name?.trim() || "";
296
+ const assigneePhone = assignedUser?.phone?.trim() || "";
297
+ const assigneeEmail = assignedUser?.email?.trim() || "";
298
+ const timeSlot = String(meta.timeSlot || meta.slot || meta.serviceSlot || "").trim();
299
+ const rawServiceDate = meta.bookingDate || meta.serviceDate || meta.orderDate || o.createdAt;
300
+ const serviceDate = rawServiceDate ? formatScheduleDate(rawServiceDate) : "";
301
+ const addr = o.shippingAddress || o.billingAddress;
302
+ const customerAddress = addr ? [
303
+ addr.line1,
304
+ addr.line2,
305
+ addr.city,
306
+ addr.state,
307
+ addr.postalCode,
308
+ addr.country
309
+ ].map((p) => String(p ?? "").trim()).filter(Boolean).join(", ") : "";
310
+ const taskDescription = productNames || (orderLines.length > 0 ? orderLines.map((l) => `${l.quantity}x ${l.name}`).join(", ") : "Order service");
311
+ const variables = {
312
+ ...baseVariables,
313
+ assigneeName,
314
+ assigneePhone,
315
+ assigneeEmail,
316
+ timeSlot,
317
+ serviceDate,
318
+ customerAddress,
319
+ taskDescription
320
+ };
291
321
  return {
292
322
  order: {
293
323
  id: o.id,
294
324
  orderNumber: String(o.orderNumber ?? ""),
295
325
  total: o.total,
296
326
  currency,
297
- qrToken: o.qrToken
327
+ qrToken: o.qrToken,
328
+ assignedUserId: o.assignedUserId ?? null,
329
+ assignedUser
298
330
  },
299
331
  contact,
300
332
  orderLines,
@@ -304,6 +336,16 @@ async function loadOrderNotificationContexts(orderId, deps) {
304
336
  };
305
337
  }
306
338
  __name(loadOrderNotificationContexts, "loadOrderNotificationContexts");
339
+ function formatScheduleDate(val) {
340
+ if (!val || typeof val !== "string" && !(val instanceof Date)) return "";
341
+ const str = String(val).trim();
342
+ if (!str) return "";
343
+ const d = new Date(str.includes("T") ? str : `${str}T00:00:00`);
344
+ return isNaN(d.getTime()) ? str : d.toLocaleDateString(void 0, {
345
+ dateStyle: "medium"
346
+ });
347
+ }
348
+ __name(formatScheduleDate, "formatScheduleDate");
307
349
 
308
350
  // src/plugins/whatsapp/whatsapp-service.ts
309
351
  function norm(v) {
@@ -1254,60 +1296,68 @@ __name(validateFcmCredentials, "validateFcmCredentials");
1254
1296
  var ORDER_NOTIFICATION_DEFAULTS = {
1255
1297
  email: {
1256
1298
  pending: {
1257
- subject: "Your Order #{{orderNumber}} is Pending",
1258
- body: "<p>Thank you for your order! It is currently pending.</p>"
1299
+ subject: "Your order {{orderNumber}} is Pending",
1300
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently pending.\n\nOrder Details:\n{{orderDetails}}\n\nWe will update you as soon as your order status changes.\n\nThank you!"
1259
1301
  },
1260
1302
  order_pending: {
1261
- subject: "Your Order #{{orderNumber}} is Pending",
1262
- body: "<p>Thank you for your order! It is currently pending.</p>"
1303
+ subject: "Your order {{orderNumber}} is Pending",
1304
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently pending.\n\nOrder Details:\n{{orderDetails}}\n\nWe will update you as soon as your order status changes.\n\nThank you!"
1263
1305
  },
1264
1306
  confirmed: {
1265
- subject: "Your Order #{{orderNumber}} is Confirmed",
1266
- body: "<p>Your order has been confirmed!</p>"
1307
+ subject: "Your order {{orderNumber}} is confirmed",
1308
+ body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
1267
1309
  },
1268
1310
  order_confirmed: {
1269
- subject: "Your Order #{{orderNumber}} is Confirmed",
1270
- body: "<p>Your order has been confirmed!</p>"
1311
+ subject: "Your order {{orderNumber}} is confirmed",
1312
+ body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
1271
1313
  },
1272
1314
  order_placed: {
1273
- subject: "Your Order #{{orderNumber}} is Placed",
1274
- body: "<p>Thank you for your order! Your order #{{orderNumber}} has been placed.</p>"
1315
+ subject: "Your order {{orderNumber}} is confirmed",
1316
+ body: "Hi {{customerName}},\n\nThank you for your order! Your order {{orderNumber}} for {{total}} {{currency}} has been confirmed.\n\nOrder Details:\n{{orderDetails}}\n\nYour Tax Invoice PDF is attached to this email for your records.\n\nThank you!"
1275
1317
  },
1276
1318
  processing: {
1277
- subject: "Your Order #{{orderNumber}} is Processing",
1278
- body: "<p>Your order is now being processed by our team.</p>"
1319
+ subject: "Your order {{orderNumber}} is now Processing",
1320
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently being processed.\n\nOrder Details:\n{{orderDetails}}\n\nWe will notify you once it is completed.\n\nThank you!"
1279
1321
  },
1280
1322
  order_processing: {
1281
- subject: "Your Order #{{orderNumber}} is Processing",
1282
- body: "<p>Your order is now being processed by our team.</p>"
1323
+ subject: "Your order {{orderNumber}} is now Processing",
1324
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} is currently being processed.\n\nOrder Details:\n{{orderDetails}}\n\nWe will notify you once it is completed.\n\nThank you!"
1283
1325
  },
1284
1326
  completed: {
1285
- subject: "Your Order #{{orderNumber}} is Completed",
1286
- body: "<p>Your order has been completed. Thank you!</p>"
1327
+ subject: "Your order {{orderNumber}} is Completed",
1328
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been completed.\n\nOrder Details:\n{{orderDetails}}\n\nThank you for choosing us!"
1287
1329
  },
1288
1330
  order_completed: {
1289
- subject: "Your Order #{{orderNumber}} is Completed",
1290
- body: "<p>Your order has been completed. Thank you!</p>"
1331
+ subject: "Your order {{orderNumber}} is Completed",
1332
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been completed.\n\nOrder Details:\n{{orderDetails}}\n\nThank you for choosing us!"
1291
1333
  },
1292
1334
  cancelled: {
1293
- subject: "Your Order #{{orderNumber}} is Cancelled",
1294
- body: "<p>Your order has been cancelled.</p>"
1335
+ subject: "Your order {{orderNumber}} has been cancelled",
1336
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been cancelled.\n\nIf a refund is due, you will receive {{refundAmount}} {{currency}} back to your original payment method.\n\nIf you have any questions, please contact us.\n\nThank you!"
1295
1337
  },
1296
1338
  order_cancelled: {
1297
- subject: "Your Order #{{orderNumber}} is Cancelled",
1298
- body: "<p>Your order has been cancelled.</p>"
1339
+ subject: "Your order {{orderNumber}} has been cancelled",
1340
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} for {{total}} {{currency}} has been cancelled.\n\nIf a refund is due, you will receive {{refundAmount}} {{currency}} back to your original payment method.\n\nIf you have any questions, please contact us.\n\nThank you!"
1299
1341
  },
1300
1342
  payment_success: {
1301
- subject: "Payment Received for Order #{{orderNumber}}",
1302
- body: "<p>Payment of {{currency}} {{total}} has been successfully received for order #{{orderNumber}}.</p>"
1343
+ subject: "Payment Successful for Order {{orderNumber}}",
1344
+ body: "Hi {{customerName}},\n\nWe have received your payment for order {{orderNumber}} of {{total}} {{currency}}.\n\nOrder Details:\n{{orderDetails}}\n\nThank you!"
1303
1345
  },
1304
1346
  payment_failed: {
1305
- subject: "Payment Failed for Order #{{orderNumber}}",
1306
- body: "<p>Payment for order #{{orderNumber}} could not be completed.</p>"
1347
+ subject: "Payment Failed for Order {{orderNumber}}",
1348
+ body: "Hi {{customerName}},\n\nYour payment for order {{orderNumber}} of {{total}} {{currency}} could not be completed or remains unpaid.\n\nIf you were charged, please contact us with your order number {{orderNumber}}.\n\nThank you!"
1307
1349
  },
1308
1350
  payment_refund_initiated: {
1309
- subject: "Refund Initiated for Order #{{orderNumber}}",
1310
- body: "<p>A refund of {{currency}} {{refundAmount}} has been initiated for order #{{orderNumber}}.</p>"
1351
+ subject: "Refund Initiated for Order {{orderNumber}}",
1352
+ body: "Hi {{customerName}},\n\nYour order {{orderNumber}} has been cancelled and a refund of {{refundAmount}} {{currency}} has been initiated.\n\nThe amount will be credited back to your original payment method.\n\nThank you!"
1353
+ },
1354
+ order_assigned: {
1355
+ subject: "Your service expert has been assigned for order {{orderNumber}}",
1356
+ body: "Hi {{customerName}},\n\nA service expert has been assigned to your order {{orderNumber}}.\n\nAssignee Details:\n\u2022 Name: {{assigneeName}}\n\u2022 Phone: {{assigneePhone}}\n\nYour expert will reach you by your scheduled time slot:\n\u2022 Date: {{serviceDate}}\n\u2022 Slot: {{timeSlot}}\n\nOrder Details:\n{{orderDetails}}\n\nThank you!"
1357
+ },
1358
+ order_assigned_assignee: {
1359
+ subject: "New task assigned: Order {{orderNumber}}",
1360
+ body: "Hi {{assigneeName}},\n\nA new task has been assigned to you for order {{orderNumber}}.\n\nTask Details:\n{{taskDescription}}\n\nCustomer Details:\n\u2022 Name: {{customerName}}\n\u2022 Phone: {{customerPhone}}\n\u2022 Address: {{customerAddress}}\n\nSchedule:\n\u2022 Date: {{serviceDate}}\n\u2022 Slot: {{timeSlot}}\n\nPlease arrive promptly within the scheduled time slot."
1311
1361
  }
1312
1362
  },
1313
1363
  sms: {
@@ -1316,6 +1366,8 @@ var ORDER_NOTIFICATION_DEFAULTS = {
1316
1366
  confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
1317
1367
  order_confirmed: "Your Order #{{orderNumber}} is confirmed. Total: {{total}}.",
1318
1368
  order_placed: "Your Order #{{orderNumber}} is placed. Total: {{total}}.",
1369
+ order_assigned: "Hi {{customerName}}, your service expert {{assigneeName}} ({{assigneePhone}}) has been assigned for order {{orderNumber}}. Arrival slot: {{timeSlot}}.",
1370
+ order_assigned_assignee: "Hi {{assigneeName}}, new task assigned: Order {{orderNumber}}. Customer: {{customerName}}, Phone: {{customerPhone}}, Address: {{customerAddress}}, Slot: {{timeSlot}}.",
1319
1371
  processing: "Your Order #{{orderNumber}} is now processing.",
1320
1372
  order_processing: "Your Order #{{orderNumber}} is now processing.",
1321
1373
  completed: "Your Order #{{orderNumber}} has been completed. Thank you!",
@@ -1332,6 +1384,8 @@ var ORDER_NOTIFICATION_DEFAULTS = {
1332
1384
  confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
1333
1385
  order_confirmed: "Hi {{customerName}}, your Order #{{orderNumber}} is confirmed.",
1334
1386
  order_placed: "Hi {{customerName}}, your Order #{{orderNumber}} is placed.",
1387
+ order_assigned: "Hi {{customerName}}, expert {{assigneeName}} ({{assigneePhone}}) has been assigned to your order #{{orderNumber}}. Scheduled slot: {{timeSlot}} on {{serviceDate}}.",
1388
+ order_assigned_assignee: "Hi {{assigneeName}}, you have been assigned to order #{{orderNumber}}. Customer: {{customerName}} ({{customerPhone}}), Address: {{customerAddress}}, Slot: {{timeSlot}} on {{serviceDate}}.",
1335
1389
  processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
1336
1390
  order_processing: "Hi {{customerName}}, your Order #{{orderNumber}} is now being processed.",
1337
1391
  completed: "Hi {{customerName}}, your Order #{{orderNumber}} is completed.",
@@ -1875,10 +1929,15 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
1875
1929
  isAudienceEmailEnabled(dataSource, entityMap, "vendor"),
1876
1930
  isAudienceEmailEnabled(dataSource, entityMap, "admin")
1877
1931
  ]);
1878
- if (customerEnabled && contactEmail) {
1932
+ if (triggerKey === "order_assigned_assignee") {
1933
+ const assigneeEmail = context?.order?.assignedUser?.email?.trim();
1934
+ if (assigneeEmail) {
1935
+ await sendEmailForTrigger(triggerKey, payload.orderId, "customers", variables, assigneeEmail, dataSource, entityMap, getCms);
1936
+ }
1937
+ } else if (customerEnabled && contactEmail) {
1879
1938
  await sendEmailForTrigger(triggerKey, payload.orderId, "customers", variables, contactEmail, dataSource, entityMap, getCms);
1880
1939
  }
1881
- if (vendorEnabled) {
1940
+ if (vendorEnabled && triggerKey !== "order_assigned_assignee") {
1882
1941
  const multiVendorSettings = await getSettingsGroup(dataSource, entityMap, "multi_vendor");
1883
1942
  if (multiVendorSettings.enabled !== "false" && multiVendorSettings.enabled !== "0") {
1884
1943
  const vendorEmails = await getOrderVendorEmails(payload.orderId, payload.vendorId, dataSource, entityMap);
@@ -1887,7 +1946,7 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
1887
1946
  }
1888
1947
  }
1889
1948
  }
1890
- if (adminEnabled) {
1949
+ if (adminEnabled && triggerKey !== "order_assigned_assignee") {
1891
1950
  const adminEmails = await getAdminRecipientEmails(dataSource, entityMap);
1892
1951
  for (const aEmail of adminEmails) {
1893
1952
  await sendEmailForTrigger(triggerKey, payload.orderId, "admin", variables, aEmail, dataSource, entityMap, getCms);
@@ -1902,22 +1961,23 @@ async function handleTrigger(triggerKey, payload, dataSource, entityMap, getCms)
1902
1961
  isAudiencePushEnabled(dataSource, entityMap, "vendor"),
1903
1962
  isAudiencePushEnabled(dataSource, entityMap, "admin")
1904
1963
  ]);
1905
- const targetCustomerId = context?.order?.customerContactId || context?.order?.contactId || context?.order?.userId || context?.order?.id;
1964
+ const targetCustomerId = triggerKey === "order_assigned_assignee" ? context?.order?.assignedUser?.id : context?.order?.customerContactId || context?.order?.contactId || context?.order?.userId || context?.order?.id;
1906
1965
  if (pushCustomerEnabled && targetCustomerId) {
1907
1966
  await sendPushNotificationForTrigger(triggerKey, payload.orderId, "customers", String(targetCustomerId), variables, dataSource, entityMap);
1908
1967
  }
1909
- if (pushVendorEnabled && payload.vendorId) {
1968
+ if (pushVendorEnabled && payload.vendorId && triggerKey !== "order_assigned_assignee") {
1910
1969
  await sendPushNotificationForTrigger(triggerKey, payload.orderId, "vendors", String(payload.vendorId), variables, dataSource, entityMap);
1911
1970
  }
1912
- if (pushAdminEnabled) {
1971
+ if (pushAdminEnabled && triggerKey !== "order_assigned_assignee") {
1913
1972
  await sendPushNotificationForTrigger(triggerKey, payload.orderId, "admin", "admin", variables, dataSource, entityMap);
1914
1973
  }
1915
1974
  }
1916
1975
  if (whatsappChannelEnabled) {
1917
- if (contactPhone) {
1918
- await sendWhatsAppForTrigger(triggerKey, payload.orderId, contactPhone, variables, dataSource, entityMap, getCms);
1976
+ const targetPhone = triggerKey === "order_assigned_assignee" ? context?.order?.assignedUser?.phone?.trim() : contactPhone;
1977
+ if (targetPhone) {
1978
+ await sendWhatsAppForTrigger(triggerKey, payload.orderId, targetPhone, variables, dataSource, entityMap, getCms);
1919
1979
  } else {
1920
- console.warn("[order-notifications] skip WhatsApp: no customer phone", {
1980
+ console.warn("[order-notifications] skip WhatsApp: no recipient phone", {
1921
1981
  triggerKey,
1922
1982
  orderId: payload.orderId
1923
1983
  });
@@ -1,5 +1,5 @@
1
1
  import { isErpIntegrationEnabled } from './chunk-JC6DLWTE.js';
2
- import { mergeEmailLayoutCompanyDetails, getCompanyDetailsFromSettings } from './chunk-3VZAAQUV.js';
2
+ import { mergeEmailLayoutCompanyDetails, getCompanyDetailsFromSettings } from './chunk-YRCKEGL2.js';
3
3
  import { __name } from './chunk-SHUYVCID.js';
4
4
  import { rgb, PDFDocument, StandardFonts } from 'pdf-lib';
5
5
 
@@ -664,7 +664,7 @@ async function buildLocalInvoicePdfForOrder(ds, entityMap, orderId) {
664
664
  loadSettingsGroup(ds, entityMap, "branding"),
665
665
  loadSettingsGroup(ds, entityMap, "email")
666
666
  ]);
667
- const companyDetails = mergeEmailLayoutCompanyDetails(branding, email);
667
+ const companyDetails = mergeEmailLayoutCompanyDetails(branding, email, store);
668
668
  const fromBranding = getCompanyDetailsFromSettings(branding);
669
669
  const rawLogo = store.invoiceLogoUrl || store.companyLogo || store.logo || companyDetails.logoUrl || fromBranding.logoUrl || null;
670
670
  const logoUrl = resolveInvoiceAssetUrl(rawLogo);
@@ -92,7 +92,7 @@ async function emitOrderNotificationTrigger(triggerKey, orderId, deps, extra) {
92
92
  };
93
93
  if (listenerCount === 0) {
94
94
  try {
95
- const { dispatchOrderNotificationDirectly } = await import('./order-notification-dispatcher-5TUFCDU7.cjs');
95
+ const { dispatchOrderNotificationDirectly } = await import('./order-notification-dispatcher-RNEQB3V7.cjs');
96
96
  await dispatchOrderNotificationDirectly(triggerKey, triggerPayload, deps.dataSource, deps.entityMap);
97
97
  } catch (err) {
98
98
  console.error("[notification-triggers] direct dispatch failed", {
@@ -1,4 +1,4 @@
1
- import { isOrderEligibleForInvoiceEmail, resolveOrderInvoicePdfBytes } from './chunk-MWEK7N3F.js';
1
+ import { isOrderEligibleForInvoiceEmail, resolveOrderInvoicePdfBytes } from './chunk-OPJEV44S.js';
2
2
  import { __name } from './chunk-SHUYVCID.js';
3
3
 
4
4
  // src/plugins/email/email-queue.ts
@@ -131,7 +131,7 @@ async function queueEmail(cms, payload) {
131
131
  let email = cms && typeof cms.getPlugin === "function" ? cms.getPlugin("email") : void 0;
132
132
  if (!email) {
133
133
  try {
134
- const { EmailService } = await import('./email-service-DPKVQOKP.js');
134
+ const { EmailService } = await import('./email-service-ADDF52PJ.js');
135
135
  const env = typeof process !== "undefined" && process.env ? process.env : {};
136
136
  const type = String(env.SMTP_TYPE || "SMTP").toUpperCase();
137
137
  const port = env.SMTP_PORT ? parseInt(env.SMTP_PORT, 10) : void 0;
@@ -49,20 +49,25 @@ function absolutizeEmailAssetUrl(url) {
49
49
  chunkUSNT2KNT_cjs.__name(absolutizeEmailAssetUrl, "absolutizeEmailAssetUrl");
50
50
  function mergeEmailLayoutCompanyDetails(branding, emailSettings, storeSettings) {
51
51
  const fromBranding = getCompanyDetailsFromSettings(branding);
52
- const pick = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((emailVal, fallback) => {
53
- const t = emailVal?.trim();
54
- return t || fallback?.trim() || void 0;
55
- }, "pick");
56
- const storeLogo = storeSettings?.invoiceLogoUrl || storeSettings?.companyLogo || storeSettings?.logo || "";
57
- const storeName = storeSettings?.companyName || storeSettings?.storeName || "";
58
- const storeEmail = storeSettings?.companyEmail || "";
59
- const storePhone = storeSettings?.companyPhone || "";
60
- const logoUrl = absolutizeEmailAssetUrl(pick(emailSettings.logoUrl ?? emailSettings.emailLogoUrl, fromBranding.logoUrl || storeLogo));
61
- const companyName = pick(emailSettings.companyName ?? emailSettings.emailCompanyName, fromBranding.companyName || storeName);
62
- const supportEmail = pick(emailSettings.supportEmail ?? emailSettings.emailSupportEmail, fromBranding.supportEmail || storeEmail);
63
- const supportPhone = pick(emailSettings.supportPhone, storePhone || void 0);
64
- const footerDisclaimer = pick(emailSettings.footerDisclaimer, void 0);
65
- const followUsTitle = pick(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
52
+ const first = /* @__PURE__ */ chunkUSNT2KNT_cjs.__name((...vals) => {
53
+ for (const v of vals) {
54
+ const t = v?.trim();
55
+ if (t) return t;
56
+ }
57
+ return void 0;
58
+ }, "first");
59
+ const storeLogo = first(storeSettings?.invoiceLogoUrl, storeSettings?.companyLogo, storeSettings?.logo);
60
+ const storeName = first(storeSettings?.companyName, storeSettings?.storeName);
61
+ const storeEmail = first(storeSettings?.companyEmail);
62
+ const storePhone = first(storeSettings?.companyPhone);
63
+ const storeAddress = first(storeSettings?.companyAddress);
64
+ const logoUrl = absolutizeEmailAssetUrl(first(storeLogo, emailSettings.logoUrl, emailSettings.emailLogoUrl, fromBranding.logoUrl));
65
+ const companyName = first(storeName, emailSettings.companyName, emailSettings.emailCompanyName, fromBranding.companyName);
66
+ const supportEmail = first(storeEmail, emailSettings.supportEmail, emailSettings.emailSupportEmail, fromBranding.supportEmail);
67
+ const supportPhone = first(storePhone, emailSettings.supportPhone);
68
+ const companyAddress = storeAddress;
69
+ const footerDisclaimer = first(emailSettings.footerDisclaimer);
70
+ const followUsTitle = first(emailSettings.followUsTitle, "Follow Us") || "Follow Us";
66
71
  const socialFromEmail = parseSocialLinksJson(emailSettings.socialLinks);
67
72
  const socialLinks = socialFromEmail?.length ? socialFromEmail : fromBranding.socialLinks;
68
73
  return {
@@ -70,6 +75,7 @@ function mergeEmailLayoutCompanyDetails(branding, emailSettings, storeSettings)
70
75
  companyName,
71
76
  supportEmail,
72
77
  supportPhone,
78
+ companyAddress,
73
79
  socialLinks,
74
80
  footerDisclaimer,
75
81
  followUsTitle
@@ -17,41 +17,65 @@ function escapeHtml(s) {
17
17
  chunkUSNT2KNT_cjs.__name(escapeHtml, "escapeHtml");
18
18
  function socialLinkInnerHtml(s) {
19
19
  if (s.iconUrl?.trim()) {
20
- return `<img src="${escapeHtml(s.iconUrl.trim())}" alt="" width="20" height="20" style="display:inline-block;vertical-align:middle;border:0;" />`;
20
+ return `<img src="${escapeHtml(s.iconUrl.trim())}" alt="" width="22" height="22" style="display:block;border:0;border-radius:4px;" />`;
21
21
  }
22
- const label = s.icon && s.icon.trim() || "\u{1F517}";
23
- return escapeHtml(label);
22
+ const label = s.icon && s.icon.trim() || "\u2022";
23
+ return `<span style="display:inline-block;width:28px;height:28px;line-height:28px;text-align:center;border-radius:999px;background:#ffffff;color:#1e293b;font-size:14px;">${escapeHtml(label)}</span>`;
24
24
  }
25
25
  chunkUSNT2KNT_cjs.__name(socialLinkInnerHtml, "socialLinkInnerHtml");
26
- function supportLinesHtml(supportEmail, supportPhone) {
27
- const parts = [];
28
- if (supportEmail) {
29
- parts.push(`<a href="mailto:${escapeHtml(supportEmail)}" style="color:#6b7280;text-decoration:none;font-size:13px;">${escapeHtml(supportEmail)}</a>`);
30
- }
31
- if (supportPhone) {
32
- parts.push(`<span style="color:#6b7280;font-size:13px;">${escapeHtml(supportPhone)}</span>`);
33
- }
34
- return parts.join('<span style="color:#d1d5db;padding:0 8px;">\xB7</span>');
35
- }
36
- chunkUSNT2KNT_cjs.__name(supportLinesHtml, "supportLinesHtml");
37
26
  function renderLayout(options) {
38
27
  const { bodyHtml, companyDetails } = options;
39
- const { logoUrl, companyName, supportEmail, supportPhone, socialLinks, footerDisclaimer, followUsTitle = "Follow Us" } = companyDetails;
28
+ const { logoUrl, companyName, supportEmail, supportPhone, companyAddress, socialLinks, footerDisclaimer, followUsTitle = "Follow Us" } = companyDetails;
40
29
  const brand = (companyName || "Store").trim();
30
+ const year = (/* @__PURE__ */ new Date()).getFullYear();
31
+ const slate = "#0f172a";
32
+ const slateSoft = "#1e293b";
41
33
  const accent = "#ea580c";
42
- const logoHtml = logoUrl ? `<img src="${escapeHtml(logoUrl)}" alt="${escapeHtml(brand)}" width="160" style="max-height:56px;max-width:180px;height:auto;width:auto;display:block;border:0;margin:0 auto;" />` : `<div style="font-size:22px;font-weight:700;letter-spacing:-0.02em;color:#111827;">${escapeHtml(brand)}</div>`;
34
+ const accentSoft = "#ffedd5";
35
+ const pageBg = "#eef2f7";
36
+ const muted = "#64748b";
37
+ const border = "#e2e8f0";
38
+ const logoBlock = logoUrl ? `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:0 auto;border-collapse:collapse;">
39
+ <tr>
40
+ <td align="center" style="background:#ffffff;border-radius:14px;padding:14px 22px;box-shadow:0 8px 24px rgba(15,23,42,0.18);">
41
+ <img src="${escapeHtml(logoUrl)}" alt="${escapeHtml(brand)}" width="148" style="max-height:52px;max-width:148px;height:auto;width:auto;display:block;border:0;margin:0 auto;" />
42
+ </td>
43
+ </tr>
44
+ </table>` : `<div style="font-family:Georgia,'Times New Roman',serif;font-size:26px;font-weight:700;letter-spacing:-0.02em;color:#ffffff;">${escapeHtml(brand)}</div>`;
43
45
  const hasSocial = Boolean(socialLinks?.length);
44
- const socialRowHtml = hasSocial && socialLinks ? `<div style="margin-top:12px;text-align:center;font-size:0;line-height:0;">${socialLinks.map((s) => `<a href="${escapeHtml(s.url)}" style="display:inline-block;margin:0 8px;text-decoration:none;color:#374151;font-size:18px;line-height:1;vertical-align:middle;">${socialLinkInnerHtml(s)}</a>`).join("")}</div>` : "";
45
- const followTitleHtml = hasSocial && followUsTitle ? `<div style="font-size:12px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:0.06em;margin:0 0 4px 0;">${escapeHtml(followUsTitle)}</div>` : "";
46
- const supportHtml = supportLinesHtml(supportEmail, supportPhone);
47
- const disclaimerBlock = footerDisclaimer ? `<div style="margin-top:16px;font-size:11px;line-height:1.5;color:#9ca3af;">${escapeHtml(footerDisclaimer).replace(/\n/g, "<br/>")}</div>` : "";
46
+ const socialRowHtml = hasSocial && socialLinks ? `<table role="presentation" cellpadding="0" cellspacing="0" style="margin:14px auto 0 auto;border-collapse:collapse;">
47
+ <tr>
48
+ ${socialLinks.map((s) => `<td style="padding:0 6px;">
49
+ <a href="${escapeHtml(s.url)}" style="display:inline-block;text-decoration:none;border:1px solid ${border};border-radius:999px;padding:6px;background:#ffffff;">
50
+ ${socialLinkInnerHtml(s)}
51
+ </a>
52
+ </td>`).join("")}
53
+ </tr>
54
+ </table>` : "";
55
+ const followTitleHtml = hasSocial && followUsTitle ? `<div style="font-size:11px;font-weight:700;color:${muted};text-transform:uppercase;letter-spacing:0.08em;margin:0;">${escapeHtml(followUsTitle)}</div>` : "";
56
+ const contactRows = [];
57
+ if (supportEmail) {
58
+ contactRows.push(`<a href="mailto:${escapeHtml(supportEmail)}" style="color:${slateSoft};text-decoration:none;font-size:13px;font-weight:500;">${escapeHtml(supportEmail)}</a>`);
59
+ }
60
+ if (supportPhone) {
61
+ contactRows.push(`<span style="color:${muted};font-size:13px;">${escapeHtml(supportPhone)}</span>`);
62
+ }
63
+ if (companyAddress?.trim()) {
64
+ contactRows.push(`<span style="color:${muted};font-size:12px;line-height:1.5;display:inline-block;max-width:360px;">${escapeHtml(companyAddress.trim()).replace(/\n/g, "<br/>")}</span>`);
65
+ }
66
+ const contactHtml = contactRows.length ? `<div style="margin-top:12px;line-height:1.7;">${contactRows.join("<br/>")}</div>` : "";
67
+ const disclaimerBlock = footerDisclaimer ? `<div style="margin-top:14px;font-size:11px;line-height:1.55;color:#94a3b8;max-width:420px;margin-left:auto;margin-right:auto;">${escapeHtml(footerDisclaimer).replace(/\n/g, "<br/>")}</div>` : "";
48
68
  const responsiveCss = `
49
69
  @media only screen and (max-width: 620px) {
50
- .email-shell { padding: 16px 10px !important; }
51
- .email-card { border-radius: 12px !important; }
52
- .email-body { padding: 24px 18px !important; }
53
- .email-header { padding: 20px 18px !important; }
54
- }
70
+ .email-shell { padding: 12px 8px !important; }
71
+ .email-header { padding: 28px 18px 24px 18px !important; }
72
+ .email-body { padding: 26px 18px 8px 18px !important; }
73
+ .email-footer { padding: 22px 18px 28px 18px !important; }
74
+ .email-accent-label { display: none !important; }
75
+ }
76
+ .email-body p { margin: 0 0 14px 0 !important; }
77
+ .email-body h1, .email-body h2, .email-body h3 { margin: 0 0 12px 0 !important; color: ${slate} !important; }
78
+ .email-body a { color: ${accent} !important; }
55
79
  `;
56
80
  return `<!DOCTYPE html>
57
81
  <html lang="en">
@@ -60,39 +84,56 @@ function renderLayout(options) {
60
84
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
61
85
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
62
86
  <title>${escapeHtml(brand)}</title>
87
+ <!--[if mso]>
88
+ <style type="text/css">
89
+ body, table, td { font-family: Arial, Helvetica, sans-serif !important; }
90
+ </style>
91
+ <![endif]-->
63
92
  <style type="text/css">${responsiveCss}</style>
64
93
  </head>
65
- <body style="margin:0;padding:0;background:#f3f4f6;-webkit-font-smoothing:antialiased;">
66
- <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f3f4f6;border-collapse:collapse;">
94
+ <body style="margin:0;padding:0;background:${pageBg};-webkit-font-smoothing:antialiased;">
95
+ <div style="display:none;max-height:0;overflow:hidden;opacity:0;color:transparent;mso-hide:all;">
96
+ Update from ${escapeHtml(brand)}
97
+ </div>
98
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:${pageBg};border-collapse:collapse;">
67
99
  <tr>
68
- <td class="email-shell" align="center" style="padding:32px 16px;">
69
- <table role="presentation" class="email-card" width="100%" cellpadding="0" cellspacing="0" style="max-width:600px;background:#ffffff;border-radius:16px;overflow:hidden;border:1px solid #e5e7eb;border-collapse:collapse;">
70
- <tr>
71
- <td class="email-header" align="center" style="padding:28px 32px 20px 32px;background:#ffffff;">
72
- ${logoHtml}
73
- ${logoUrl && companyName ? `<div style="margin-top:10px;font-size:13px;color:#6b7280;">${escapeHtml(companyName)}</div>` : ""}
74
- </td>
75
- </tr>
76
- <tr>
77
- <td style="height:4px;line-height:4px;font-size:0;background:${accent};">&nbsp;</td>
78
- </tr>
79
- <tr>
80
- <td class="email-body" style="padding:28px 32px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:15px;line-height:1.65;color:#1f2937;">
81
- ${bodyHtml}
82
- </td>
83
- </tr>
100
+ <td class="email-shell" align="center" style="padding:40px 16px;">
101
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="max-width:600px;border-collapse:collapse;">
84
102
  <tr>
85
- <td style="padding:0 32px;">
86
- <div style="border-top:1px solid #e5e7eb;line-height:0;font-size:0;">&nbsp;</div>
103
+ <td style="background:#ffffff;border-radius:18px;overflow:hidden;border:1px solid ${border};box-shadow:0 12px 40px rgba(15,23,42,0.08);">
104
+ <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse;">
105
+ <tr>
106
+ <td class="email-header" align="center" style="padding:36px 32px 30px 32px;background:linear-gradient(165deg, ${slate} 0%, ${slateSoft} 100%);background-color:${slate};">
107
+ <div class="email-accent-label" style="display:inline-block;margin:0 0 18px 0;padding:5px 12px;border-radius:999px;background:rgba(234,88,12,0.18);border:1px solid rgba(251,146,60,0.35);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:11px;font-weight:600;letter-spacing:0.06em;text-transform:uppercase;color:${accentSoft};">
108
+ ${escapeHtml(brand)}
109
+ </div>
110
+ ${logoBlock}
111
+ ${logoUrl ? `<div style="margin-top:16px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:13px;color:#94a3b8;">${escapeHtml(brand)}</div>` : ""}
112
+ </td>
113
+ </tr>
114
+ <tr>
115
+ <td style="height:5px;line-height:5px;font-size:0;background:linear-gradient(90deg, ${accent} 0%, #f59e0b 55%, #fb923c 100%);background-color:${accent};">&nbsp;</td>
116
+ </tr>
117
+ <tr>
118
+ <td class="email-body" style="padding:32px 36px 12px 36px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:15px;line-height:1.7;color:#334155;background:#ffffff;">
119
+ ${bodyHtml}
120
+ </td>
121
+ </tr>
122
+ <tr>
123
+ <td class="email-footer" align="center" style="padding:24px 32px 32px 32px;background:#f8fafc;border-top:1px solid ${border};font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
124
+ ${followTitleHtml}
125
+ ${socialRowHtml}
126
+ ${contactHtml}
127
+ <div style="margin-top:18px;font-size:12px;color:#94a3b8;">\xA9 ${year} ${escapeHtml(brand)}. All rights reserved.</div>
128
+ ${disclaimerBlock}
129
+ </td>
130
+ </tr>
131
+ </table>
87
132
  </td>
88
133
  </tr>
89
134
  <tr>
90
- <td align="center" style="padding:20px 32px 28px 32px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
91
- ${followTitleHtml}
92
- ${socialRowHtml}
93
- ${supportHtml ? `<div style="margin-top:14px;">${supportHtml}</div>` : ""}
94
- <div style="margin-top:12px;font-size:12px;color:#9ca3af;">\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${escapeHtml(brand)}</div>
95
- ${disclaimerBlock}
135
+ <td align="center" style="padding:18px 8px 0 8px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:11px;line-height:1.5;color:#94a3b8;">
136
+ You\u2019re receiving this because of activity on your ${escapeHtml(brand)} account.
96
137
  </td>
97
138
  </tr>
98
139
  </table>
@@ -700,7 +741,7 @@ function renderEmail(templateName, ctx, options) {
700
741
  chunkUSNT2KNT_cjs.__name(renderEmail, "renderEmail");
701
742
 
702
743
  // src/plugins/email/email-service.ts
703
- var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-YVP4NQII.cjs', document.baseURI).href)));
744
+ var require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-VM6OVUUM.cjs', document.baseURI).href)));
704
745
  var MailComposer = require2("nodemailer/lib/mail-composer");
705
746
  var SES_REGIONS_WITHOUT_SMTP = /* @__PURE__ */ new Set([
706
747
  "af-south-1",
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk7L556RGL_cjs = require('./chunk-7L556RGL.cjs');
3
+ var chunkGLD263KW_cjs = require('./chunk-GLD263KW.cjs');
4
4
  var chunkUSNT2KNT_cjs = require('./chunk-USNT2KNT.cjs');
5
5
 
6
6
  // src/plugins/email/email-queue.ts
@@ -50,13 +50,13 @@ async function resolveInvoiceAttachmentForJob(cms, payload, options) {
50
50
  invoiceAttached: false
51
51
  };
52
52
  const o = order;
53
- if (!chunk7L556RGL_cjs.isOrderEligibleForInvoiceEmail(o.status, o.orderKind)) {
53
+ if (!chunkGLD263KW_cjs.isOrderEligibleForInvoiceEmail(o.status, o.orderKind)) {
54
54
  return {
55
55
  attachments: payload.attachments,
56
56
  invoiceAttached: false
57
57
  };
58
58
  }
59
- const resolved = await chunk7L556RGL_cjs.resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, payload.orderId);
59
+ const resolved = await chunkGLD263KW_cjs.resolveOrderInvoicePdfBytes(cms, dataSource, entityMap, payload.orderId);
60
60
  if ("error" in resolved) {
61
61
  console.warn("[email] order invoice attach failed", {
62
62
  orderId: payload.orderId,
@@ -133,7 +133,7 @@ async function queueEmail(cms, payload) {
133
133
  let email = cms && typeof cms.getPlugin === "function" ? cms.getPlugin("email") : void 0;
134
134
  if (!email) {
135
135
  try {
136
- const { EmailService } = await import('./email-service-ATCPJFWG.cjs');
136
+ const { EmailService } = await import('./email-service-5O24BSFR.cjs');
137
137
  const env = typeof process !== "undefined" && process.env ? process.env : {};
138
138
  const type = String(env.SMTP_TYPE || "SMTP").toUpperCase();
139
139
  const port = env.SMTP_PORT ? parseInt(env.SMTP_PORT, 10) : void 0;