payload-plugin-newsletter 0.15.1 → 0.16.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,47 @@
1
+ ## [0.16.0] - 2025-07-27
2
+
3
+ ### Changed
4
+ - **Send = Publish Workflow** - Simplified broadcast sending to use Payload's native draft/publish system
5
+ - Publishing a broadcast now automatically sends it via the configured email provider
6
+ - Removed custom Send/Schedule modal in favor of Payload's built-in UI
7
+ - Scheduled publishing supported via Payload's Jobs Queue system
8
+ - Breaking: Removed `SendBroadcastModal` and `ActionsCell` components
9
+ - **Streamlined UI** - Removed custom action buttons from broadcasts list view
10
+ - Users now use standard Payload publish/schedule functionality
11
+ - Cleaner interface that follows Payload's patterns
12
+ - Less code to maintain while providing better integration
13
+
14
+ ### Added
15
+ - **Automatic Send on Publish** - New `afterChange` hook that sends broadcasts when published
16
+ - Checks if broadcast is transitioning to published status
17
+ - Automatically calls provider's send method
18
+ - Updates broadcast status to "sending" after successful send
19
+ - Handles failures gracefully with status update to "failed"
20
+ - **Jobs Queue Documentation** - Added comprehensive setup instructions for scheduled publishing
21
+ - Vercel Cron configuration example
22
+ - Security setup with CRON_SECRET
23
+ - Step-by-step guide for enabling scheduled broadcasts
24
+
25
+ ### Removed
26
+ - **Custom UI Components** (Breaking Change)
27
+ - `SendBroadcastModal` - Custom send/schedule modal
28
+ - `ActionsCell` - Custom action buttons in list view
29
+ - `actions` field from Broadcasts collection
30
+ - These are replaced by Payload's native publish/schedule functionality
31
+
32
+ ### Technical
33
+ - Enabled `versions` configuration on Broadcasts collection with drafts and scheduled publishing
34
+ - Updated default columns to show both `_status` (Draft/Published) and `status` (send status)
35
+ - Improved TypeScript exports by removing deleted component references
36
+ - All tests passing with minor version upgrade
37
+
38
+ ### Migration Guide
39
+ If you were using the custom Send/Schedule modal:
40
+ 1. The functionality is now built into Payload's publish system
41
+ 2. To send immediately: Click "Publish"
42
+ 3. To schedule: Click "Schedule" (requires Jobs Queue setup)
43
+ 4. Remove any imports of `SendBroadcastModal` or `ActionsCell` from your code
44
+
1
45
  ## [0.15.1] - 2025-07-27
2
46
 
3
47
  ### Fixed
package/README.md CHANGED
@@ -241,6 +241,50 @@ This adds a `broadcasts` collection with:
241
241
  - Custom email blocks (buttons, dividers)
242
242
  - Inline email preview with React Email
243
243
  - Automatic sync with your email provider
244
+ - Draft/publish system with scheduled publishing support
245
+
246
+ ### Send = Publish Workflow
247
+
248
+ The plugin integrates seamlessly with Payload's draft/publish system:
249
+
250
+ - **Draft**: Create and edit broadcasts without sending
251
+ - **Publish**: Publishing a broadcast automatically sends it via your configured email provider
252
+ - **Schedule**: Use Payload's scheduled publishing to send broadcasts at a future time
253
+
254
+ **How it works:**
255
+ 1. Create a broadcast and save as draft
256
+ 2. When ready, click "Publish" to send immediately
257
+ 3. Or use "Schedule" to publish (and send) at a specific date/time
258
+
259
+ **Important**: Scheduled publishing requires configuring Payload's Jobs Queue. For Vercel deployments, add this to your `vercel.json`:
260
+
261
+ ```json
262
+ {
263
+ "crons": [
264
+ {
265
+ "path": "/api/payload-jobs/run",
266
+ "schedule": "*/5 * * * *"
267
+ }
268
+ ]
269
+ }
270
+ ```
271
+
272
+ And secure the endpoint in your `payload.config.ts`:
273
+
274
+ ```typescript
275
+ export default buildConfig({
276
+ // ... other config
277
+ jobs: {
278
+ access: {
279
+ run: ({ req }) => {
280
+ if (req.user) return true
281
+ const authHeader = req.headers.get('authorization')
282
+ return authHeader === `Bearer ${process.env.CRON_SECRET}`
283
+ },
284
+ },
285
+ },
286
+ })
287
+ ```
244
288
 
245
289
  ### Custom Email Templates (v0.12.0+)
246
290
 
@@ -1168,6 +1168,12 @@ var createBroadcastsCollection = (pluginConfig) => {
1168
1168
  const customizations = pluginConfig.customizations?.broadcasts;
1169
1169
  return {
1170
1170
  slug: "broadcasts",
1171
+ versions: {
1172
+ drafts: {
1173
+ autosave: true,
1174
+ schedulePublish: true
1175
+ }
1176
+ },
1171
1177
  labels: {
1172
1178
  singular: "Broadcast",
1173
1179
  plural: "Broadcasts"
@@ -1175,7 +1181,7 @@ var createBroadcastsCollection = (pluginConfig) => {
1175
1181
  admin: {
1176
1182
  useAsTitle: "subject",
1177
1183
  description: "Individual email campaigns sent to subscribers",
1178
- defaultColumns: ["subject", "status", "sentAt", "recipientCount", "actions"]
1184
+ defaultColumns: ["subject", "_status", "status", "sentAt", "recipientCount"]
1179
1185
  },
1180
1186
  fields: [
1181
1187
  {
@@ -1388,18 +1394,6 @@ var createBroadcastsCollection = (pluginConfig) => {
1388
1394
  condition: () => false
1389
1395
  // Hidden by default
1390
1396
  }
1391
- },
1392
- // UI Field for custom actions in list view
1393
- {
1394
- name: "actions",
1395
- type: "ui",
1396
- admin: {
1397
- components: {
1398
- Cell: "payload-plugin-newsletter/components#ActionsCell",
1399
- Field: "payload-plugin-newsletter/components#EmptyField"
1400
- },
1401
- disableListColumn: false
1402
- }
1403
1397
  }
1404
1398
  ],
1405
1399
  hooks: {
@@ -1513,6 +1507,48 @@ var createBroadcastsCollection = (pluginConfig) => {
1513
1507
  req.payload.logger.error("Failed to delete broadcast from provider:", error);
1514
1508
  }
1515
1509
  return doc;
1510
+ },
1511
+ // Hook to send when published
1512
+ async ({ doc, req }) => {
1513
+ if (doc._status === "published" && doc.providerId) {
1514
+ if (doc.status === "sent" || doc.status === "sending") {
1515
+ return doc;
1516
+ }
1517
+ try {
1518
+ const broadcastConfig = pluginConfig.providers?.broadcast;
1519
+ const resendConfig = pluginConfig.providers?.resend;
1520
+ if (!broadcastConfig && !resendConfig) {
1521
+ req.payload.logger.error("No provider configured for sending");
1522
+ return doc;
1523
+ }
1524
+ if (broadcastConfig) {
1525
+ const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
1526
+ const provider = new BroadcastApiProvider2(broadcastConfig);
1527
+ await provider.send(doc.providerId);
1528
+ }
1529
+ await req.payload.update({
1530
+ collection: "broadcasts",
1531
+ id: doc.id,
1532
+ data: {
1533
+ status: "sending" /* SENDING */,
1534
+ sentAt: (/* @__PURE__ */ new Date()).toISOString()
1535
+ },
1536
+ req
1537
+ });
1538
+ req.payload.logger.info(`Broadcast ${doc.id} sent successfully`);
1539
+ } catch (error) {
1540
+ req.payload.logger.error(`Failed to send broadcast ${doc.id}:`, error);
1541
+ await req.payload.update({
1542
+ collection: "broadcasts",
1543
+ id: doc.id,
1544
+ data: {
1545
+ status: "failed" /* FAILED */
1546
+ },
1547
+ req
1548
+ });
1549
+ }
1550
+ }
1551
+ return doc;
1516
1552
  }
1517
1553
  ]
1518
1554
  }