payload-plugin-newsletter 0.16.3 → 0.16.6

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.
@@ -269,9 +269,9 @@ var init_broadcast2 = __esm({
269
269
  async update(id, data) {
270
270
  try {
271
271
  const existing = await this.get(id);
272
- if (!this.canEditInStatus(existing.status)) {
272
+ if (!this.canEditInStatus(existing.sendStatus)) {
273
273
  throw new BroadcastProviderError(
274
- `Cannot update broadcast in status: ${existing.status}`,
274
+ `Cannot update broadcast in status: ${existing.sendStatus}`,
275
275
  "INVALID_STATUS" /* INVALID_STATUS */,
276
276
  this.name
277
277
  );
@@ -314,9 +314,9 @@ var init_broadcast2 = __esm({
314
314
  async delete(id) {
315
315
  try {
316
316
  const existing = await this.get(id);
317
- if (!this.canEditInStatus(existing.status)) {
317
+ if (!this.canEditInStatus(existing.sendStatus)) {
318
318
  throw new BroadcastProviderError(
319
- `Cannot delete broadcast in status: ${existing.status}`,
319
+ `Cannot delete broadcast in status: ${existing.sendStatus}`,
320
320
  "INVALID_STATUS" /* INVALID_STATUS */,
321
321
  this.name
322
322
  );
@@ -475,7 +475,7 @@ var init_broadcast2 = __esm({
475
475
  subject: broadcast.subject,
476
476
  preheader: broadcast.preheader,
477
477
  content: broadcast.body,
478
- status: this.mapBroadcastStatus(broadcast.status),
478
+ sendStatus: this.mapBroadcastStatus(broadcast.status),
479
479
  trackOpens: broadcast.track_opens,
480
480
  trackClicks: broadcast.track_clicks,
481
481
  replyTo: broadcast.reply_to,
@@ -1178,6 +1178,19 @@ var createBroadcastsCollection = (pluginConfig) => {
1178
1178
  const customizations = pluginConfig.customizations?.broadcasts;
1179
1179
  return {
1180
1180
  slug: "broadcasts",
1181
+ access: {
1182
+ read: () => true,
1183
+ // Public read access
1184
+ create: ({ req: { user } }) => {
1185
+ return Boolean(user);
1186
+ },
1187
+ update: ({ req: { user } }) => {
1188
+ return Boolean(user);
1189
+ },
1190
+ delete: ({ req: { user } }) => {
1191
+ return Boolean(user);
1192
+ }
1193
+ },
1181
1194
  versions: {
1182
1195
  drafts: {
1183
1196
  autosave: true,
@@ -1191,7 +1204,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1191
1204
  admin: {
1192
1205
  useAsTitle: "subject",
1193
1206
  description: "Individual email campaigns sent to subscribers",
1194
- defaultColumns: ["subject", "_status", "status", "sentAt", "recipientCount"]
1207
+ defaultColumns: ["subject", "_status", "sendStatus", "sentAt", "recipientCount"]
1195
1208
  },
1196
1209
  fields: [
1197
1210
  {
@@ -1251,8 +1264,9 @@ var createBroadcastsCollection = (pluginConfig) => {
1251
1264
  ]
1252
1265
  },
1253
1266
  {
1254
- name: "status",
1267
+ name: "sendStatus",
1255
1268
  type: "select",
1269
+ label: "Send Status",
1256
1270
  required: true,
1257
1271
  defaultValue: "draft" /* DRAFT */,
1258
1272
  options: [
@@ -1266,6 +1280,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1266
1280
  ],
1267
1281
  admin: {
1268
1282
  readOnly: true,
1283
+ description: "The status of the email send operation",
1269
1284
  components: {
1270
1285
  Cell: "payload-plugin-newsletter/components#StatusBadge"
1271
1286
  }
@@ -1322,7 +1337,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1322
1337
  type: "group",
1323
1338
  admin: {
1324
1339
  readOnly: true,
1325
- condition: (data) => data?.status === "sent" /* SENT */
1340
+ condition: (data) => data?.sendStatus === "sent" /* SENT */
1326
1341
  },
1327
1342
  fields: [
1328
1343
  {
@@ -1381,7 +1396,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1381
1396
  name: "scheduledAt",
1382
1397
  type: "date",
1383
1398
  admin: {
1384
- condition: (data) => data?.status === "scheduled" /* SCHEDULED */,
1399
+ condition: (data) => data?.sendStatus === "scheduled" /* SCHEDULED */,
1385
1400
  date: {
1386
1401
  displayFormat: "MMM d, yyyy h:mm a"
1387
1402
  }
@@ -1407,58 +1422,130 @@ var createBroadcastsCollection = (pluginConfig) => {
1407
1422
  }
1408
1423
  ],
1409
1424
  hooks: {
1410
- // Sync with provider on create
1425
+ // Sync with provider on create and update
1411
1426
  afterChange: [
1412
- async ({ doc, operation, req }) => {
1413
- if (!hasProviders || operation !== "create") return doc;
1414
- try {
1415
- const providerConfig = await getBroadcastConfig(req, pluginConfig);
1416
- if (!providerConfig || !providerConfig.token) {
1417
- req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
1418
- return doc;
1419
- }
1420
- const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1421
- const provider = new BroadcastApiProvider2(providerConfig);
1422
- const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
1423
- const providerBroadcast = await provider.create({
1424
- name: doc.subject,
1425
- // Use subject as name since we removed the name field
1426
- subject: doc.subject,
1427
- preheader: doc.contentSection?.preheader,
1428
- content: htmlContent,
1429
- trackOpens: doc.settings?.trackOpens,
1430
- trackClicks: doc.settings?.trackClicks,
1431
- replyTo: doc.settings?.replyTo || providerConfig.replyTo,
1432
- audienceIds: doc.audienceIds?.map((a) => a.audienceId)
1433
- });
1434
- await req.payload.update({
1435
- collection: "broadcasts",
1436
- id: doc.id,
1437
- data: {
1427
+ async ({ doc, operation, req, previousDoc }) => {
1428
+ if (!hasProviders) return doc;
1429
+ if (operation === "create") {
1430
+ try {
1431
+ const providerConfig = await getBroadcastConfig(req, pluginConfig);
1432
+ if (!providerConfig || !providerConfig.token) {
1433
+ req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
1434
+ return doc;
1435
+ }
1436
+ const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1437
+ const provider = new BroadcastApiProvider2(providerConfig);
1438
+ const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
1439
+ const providerBroadcast = await provider.create({
1440
+ name: doc.subject,
1441
+ // Use subject as name since we removed the name field
1442
+ subject: doc.subject,
1443
+ preheader: doc.contentSection?.preheader,
1444
+ content: htmlContent,
1445
+ trackOpens: doc.settings?.trackOpens,
1446
+ trackClicks: doc.settings?.trackClicks,
1447
+ replyTo: doc.settings?.replyTo || providerConfig.replyTo,
1448
+ audienceIds: doc.audienceIds?.map((a) => a.audienceId)
1449
+ });
1450
+ await req.payload.update({
1451
+ collection: "broadcasts",
1452
+ id: doc.id,
1453
+ data: {
1454
+ providerId: providerBroadcast.id,
1455
+ providerData: providerBroadcast.providerData
1456
+ },
1457
+ req
1458
+ });
1459
+ return {
1460
+ ...doc,
1438
1461
  providerId: providerBroadcast.id,
1439
1462
  providerData: providerBroadcast.providerData
1440
- },
1441
- req
1463
+ };
1464
+ } catch (error) {
1465
+ if (error instanceof Error) {
1466
+ req.payload.logger.error("Failed to create broadcast in provider:", {
1467
+ message: error.message,
1468
+ stack: error.stack,
1469
+ name: error.name,
1470
+ // If it's a BroadcastProviderError, it might have additional details
1471
+ ...error.details
1472
+ });
1473
+ } else {
1474
+ req.payload.logger.error("Failed to create broadcast in provider:", error);
1475
+ }
1476
+ return doc;
1477
+ }
1478
+ }
1479
+ if (operation === "update" && doc.providerId) {
1480
+ req.payload.logger.info("Broadcast afterChange update hook triggered", {
1481
+ operation,
1482
+ hasProviderId: !!doc.providerId,
1483
+ sendStatus: doc.sendStatus,
1484
+ publishStatus: doc._status
1442
1485
  });
1443
- return {
1444
- ...doc,
1445
- providerId: providerBroadcast.id,
1446
- providerData: providerBroadcast.providerData
1447
- };
1448
- } catch (error) {
1449
- if (error instanceof Error) {
1450
- req.payload.logger.error("Failed to create broadcast in provider:", {
1451
- message: error.message,
1452
- stack: error.stack,
1453
- name: error.name,
1454
- // If it's a BroadcastProviderError, it might have additional details
1455
- ...error.details
1456
- });
1457
- } else {
1458
- req.payload.logger.error("Failed to create broadcast in provider:", error);
1486
+ try {
1487
+ const providerConfig = await getBroadcastConfig(req, pluginConfig);
1488
+ if (!providerConfig || !providerConfig.token) {
1489
+ req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
1490
+ return doc;
1491
+ }
1492
+ const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1493
+ const provider = new BroadcastApiProvider2(providerConfig);
1494
+ const capabilities = provider.getCapabilities();
1495
+ const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
1496
+ if (!capabilities.editableStatuses.includes(sendStatus)) {
1497
+ req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
1498
+ return doc;
1499
+ }
1500
+ const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
1501
+ if (contentChanged) {
1502
+ const updates = {};
1503
+ if (doc.subject !== previousDoc?.subject) {
1504
+ updates.name = doc.subject;
1505
+ updates.subject = doc.subject;
1506
+ }
1507
+ if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
1508
+ updates.preheader = doc.contentSection?.preheader;
1509
+ }
1510
+ if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
1511
+ updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
1512
+ }
1513
+ if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
1514
+ updates.trackOpens = doc.settings.trackOpens;
1515
+ }
1516
+ if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
1517
+ updates.trackClicks = doc.settings.trackClicks;
1518
+ }
1519
+ if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
1520
+ updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
1521
+ }
1522
+ if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
1523
+ updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
1524
+ }
1525
+ req.payload.logger.info("Syncing broadcast updates to provider", {
1526
+ providerId: doc.providerId,
1527
+ updates
1528
+ });
1529
+ await provider.update(doc.providerId, updates);
1530
+ req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
1531
+ } else {
1532
+ req.payload.logger.info("No content changes to sync to provider");
1533
+ }
1534
+ } catch (error) {
1535
+ if (error instanceof Error) {
1536
+ req.payload.logger.error("Failed to sync broadcast update to provider:", {
1537
+ message: error.message,
1538
+ stack: error.stack,
1539
+ name: error.name,
1540
+ // If it's a BroadcastProviderError, it might have additional details
1541
+ ...error.details
1542
+ });
1543
+ } else {
1544
+ req.payload.logger.error("Failed to sync broadcast update to provider:", error);
1545
+ }
1459
1546
  }
1460
- return doc;
1461
1547
  }
1548
+ return doc;
1462
1549
  },
1463
1550
  // Hook to send when published
1464
1551
  async ({ doc, operation, previousDoc, req }) => {
@@ -1466,7 +1553,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1466
1553
  const wasUnpublished = !previousDoc?._status || previousDoc._status === "draft";
1467
1554
  const isNowPublished = doc._status === "published";
1468
1555
  if (wasUnpublished && isNowPublished && doc.providerId) {
1469
- if (doc.status === "sent" || doc.status === "sending") {
1556
+ if (doc.sendStatus === "sent" /* SENT */ || doc.sendStatus === "sending" /* SENDING */) {
1470
1557
  return doc;
1471
1558
  }
1472
1559
  try {
@@ -1485,7 +1572,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1485
1572
  collection: "broadcasts",
1486
1573
  id: doc.id,
1487
1574
  data: {
1488
- status: "sending" /* SENDING */,
1575
+ sendStatus: "sending" /* SENDING */,
1489
1576
  sentAt: (/* @__PURE__ */ new Date()).toISOString()
1490
1577
  },
1491
1578
  req
@@ -1507,7 +1594,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1507
1594
  collection: "broadcasts",
1508
1595
  id: doc.id,
1509
1596
  data: {
1510
- status: "failed" /* FAILED */
1597
+ sendStatus: "failed" /* FAILED */
1511
1598
  },
1512
1599
  req
1513
1600
  });
@@ -1516,62 +1603,8 @@ var createBroadcastsCollection = (pluginConfig) => {
1516
1603
  return doc;
1517
1604
  }
1518
1605
  ],
1519
- // Sync updates with provider
1520
- beforeChange: [
1521
- async ({ data, originalDoc, operation, req }) => {
1522
- if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
1523
- try {
1524
- const providerConfig = await getBroadcastConfig(req, pluginConfig);
1525
- if (!providerConfig || !providerConfig.token) {
1526
- req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
1527
- return data;
1528
- }
1529
- const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1530
- const provider = new BroadcastApiProvider2(providerConfig);
1531
- const capabilities = provider.getCapabilities();
1532
- if (!capabilities.editableStatuses.includes(originalDoc.status)) {
1533
- return data;
1534
- }
1535
- const updates = {};
1536
- if (data.subject !== originalDoc.subject) {
1537
- updates.name = data.subject;
1538
- updates.subject = data.subject;
1539
- }
1540
- if (data.contentSection?.preheader !== originalDoc.contentSection?.preheader) updates.preheader = data.contentSection?.preheader;
1541
- if (data.contentSection?.content !== originalDoc.contentSection?.content) {
1542
- updates.content = await convertToEmailSafeHtml(data.contentSection?.content);
1543
- }
1544
- if (data.settings?.trackOpens !== originalDoc.settings?.trackOpens) {
1545
- updates.trackOpens = data.settings.trackOpens;
1546
- }
1547
- if (data.settings?.trackClicks !== originalDoc.settings?.trackClicks) {
1548
- updates.trackClicks = data.settings.trackClicks;
1549
- }
1550
- if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
1551
- updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
1552
- }
1553
- if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
1554
- updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
1555
- }
1556
- if (Object.keys(updates).length > 0) {
1557
- await provider.update(originalDoc.providerId, updates);
1558
- }
1559
- } catch (error) {
1560
- if (error instanceof Error) {
1561
- req.payload.logger.error("Failed to update broadcast in provider:", {
1562
- message: error.message,
1563
- stack: error.stack,
1564
- name: error.name,
1565
- // If it's a BroadcastProviderError, it might have additional details
1566
- ...error.details
1567
- });
1568
- } else {
1569
- req.payload.logger.error("Failed to update broadcast in provider:", error);
1570
- }
1571
- }
1572
- return data;
1573
- }
1574
- ],
1606
+ // beforeChange hooks can be added here if needed
1607
+ beforeChange: [],
1575
1608
  // Handle deletion
1576
1609
  afterDelete: [
1577
1610
  async ({ doc, req }) => {
@@ -1585,7 +1618,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1585
1618
  const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1586
1619
  const provider = new BroadcastApiProvider2(providerConfig);
1587
1620
  const capabilities = provider.getCapabilities();
1588
- if (capabilities.editableStatuses.includes(doc.status)) {
1621
+ if (capabilities.editableStatuses.includes(doc.sendStatus)) {
1589
1622
  await provider.delete(doc.providerId);
1590
1623
  }
1591
1624
  } catch (error) {