payload-plugin-newsletter 0.12.1 → 0.12.3

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,20 @@
1
+ ## [0.12.3] - 2025-07-20
2
+
3
+ ### Fixed
4
+ - Fixed import map component path references to use package exports instead of file paths
5
+ - Resolved "PayloadComponent not found in importMap" errors in Payload v3 projects
6
+ - Added missing component exports (StatusBadge, ActionsCell, EmptyField)
7
+
8
+ ## [0.12.2] - 2025-07-20
9
+
10
+ ### Documentation
11
+ - Updated all documentation to reflect single-channel architecture
12
+ - Removed outdated references to 'name' field in broadcasts
13
+ - Added comprehensive broadcast sync documentation
14
+ - Updated email preview and React Email template documentation
15
+ - Revised multi-channel setup guide to indicate it's no longer supported
16
+ - Enhanced README with broadcast management and custom template examples
17
+
1
18
  ## [0.12.1] - 2025-07-20
2
19
 
3
20
  ### Fixed
package/README.md CHANGED
@@ -21,6 +21,8 @@ A complete newsletter management plugin for [Payload CMS](https://github.com/pay
21
21
  - 👁️ **Email Preview** - Real-time preview with desktop/mobile views (v0.9.0+)
22
22
  - ✅ **Email Validation** - Built-in validation for email client compatibility (v0.9.0+)
23
23
  - 📝 **Email-Safe Editor** - Rich text editor limited to email-compatible features (v0.9.0+)
24
+ - 📬 **Broadcast Management** - Create and send email campaigns with provider sync (v0.10.0+)
25
+ - 🎨 **React Email Templates** - Customizable email templates with React Email (v0.12.0+)
24
26
 
25
27
  ## Prerequisites
26
28
 
@@ -208,6 +210,69 @@ Built-in validation checks for:
208
210
  - External resources that won't load
209
211
  - JavaScript that will be stripped
210
212
 
213
+ ## Broadcast Management (v0.10.0+)
214
+
215
+ Create and send email campaigns directly from Payload:
216
+
217
+ ### Enable Broadcasts
218
+
219
+ ```typescript
220
+ newsletterPlugin({
221
+ features: {
222
+ newsletterManagement: {
223
+ enabled: true,
224
+ }
225
+ },
226
+ providers: {
227
+ default: 'broadcast',
228
+ broadcast: {
229
+ apiUrl: process.env.BROADCAST_API_URL,
230
+ token: process.env.BROADCAST_TOKEN,
231
+ fromAddress: 'newsletter@yoursite.com',
232
+ fromName: 'Your Newsletter',
233
+ }
234
+ }
235
+ })
236
+ ```
237
+
238
+ This adds a `broadcasts` collection with:
239
+ - Rich text editor with email-safe formatting
240
+ - Image uploads with Media collection integration
241
+ - Custom email blocks (buttons, dividers)
242
+ - Inline email preview with React Email
243
+ - Automatic sync with your email provider
244
+
245
+ ### Custom Email Templates (v0.12.0+)
246
+
247
+ Customize your email design with React Email templates:
248
+
249
+ ```typescript
250
+ // email-templates/broadcast-template.tsx
251
+ import { Html, Body, Container, Text, Link } from '@react-email/components'
252
+
253
+ export default function BroadcastTemplate({ subject, preheader, content }) {
254
+ return (
255
+ <Html>
256
+ <Body style={{ backgroundColor: '#ffffff', fontFamily: 'Arial, sans-serif' }}>
257
+ <Container style={{ maxWidth: '600px', margin: '0 auto' }}>
258
+ <Text style={{ fontSize: '16px', lineHeight: '1.6' }}>
259
+ <div dangerouslySetInnerHTML={{ __html: content }} />
260
+ </Text>
261
+ <hr style={{ margin: '40px 0', border: '1px solid #e5e7eb' }} />
262
+ <Text style={{ fontSize: '14px', color: '#6b7280', textAlign: 'center' }}>
263
+ <Link href="{{unsubscribe_url}}" style={{ color: '#6b7280' }}>
264
+ Unsubscribe
265
+ </Link>
266
+ </Text>
267
+ </Container>
268
+ </Body>
269
+ </Html>
270
+ )
271
+ }
272
+ ```
273
+
274
+ The plugin automatically detects templates at `email-templates/broadcast-template.tsx`.
275
+
211
276
  ### Utilities
212
277
 
213
278
  Convert Lexical content to email-safe HTML:
@@ -307,6 +372,11 @@ newsletterPlugin({
307
372
  articlesCollection: 'posts', // Your articles/posts collection
308
373
  },
309
374
 
375
+ // Broadcast management (v0.10.0+)
376
+ newsletterManagement: {
377
+ enabled: true, // Enables broadcasts collection
378
+ },
379
+
310
380
  // UTM tracking
311
381
  utmTracking: {
312
382
  enabled: true,
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/exports/components.ts
32
32
  var components_exports = {};
33
33
  __export(components_exports, {
34
+ ActionsCell: () => ActionsCell,
34
35
  BroadcastEditor: () => BroadcastEditor,
35
36
  BroadcastInlinePreview: () => BroadcastInlinePreview,
36
37
  BroadcastPreviewField: () => BroadcastPreviewField,
@@ -38,10 +39,12 @@ __export(components_exports, {
38
39
  EmailPreview: () => EmailPreview,
39
40
  EmailPreviewField: () => EmailPreviewField,
40
41
  EmailRenderer: () => EmailRenderer,
42
+ EmptyField: () => EmptyField,
41
43
  MagicLinkVerify: () => MagicLinkVerify,
42
44
  NewsletterForm: () => NewsletterForm,
43
45
  PreferencesForm: () => PreferencesForm,
44
46
  PreviewControls: () => PreviewControls,
47
+ StatusBadge: () => StatusBadge,
45
48
  createMagicLinkVerify: () => createMagicLinkVerify,
46
49
  createNewsletterForm: () => createNewsletterForm,
47
50
  createPreferencesForm: () => createPreferencesForm,
@@ -2163,8 +2166,517 @@ var BroadcastPreviewField = () => {
2163
2166
  color: "#6b7280"
2164
2167
  }, children: "Email preview is available inline below the content editor." });
2165
2168
  };
2169
+
2170
+ // src/components/Broadcasts/StatusBadge.tsx
2171
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2172
+ var statusConfig = {
2173
+ ["draft" /* DRAFT */]: {
2174
+ label: "Draft",
2175
+ color: "#6B7280",
2176
+ // gray
2177
+ backgroundColor: "#F3F4F6"
2178
+ },
2179
+ ["scheduled" /* SCHEDULED */]: {
2180
+ label: "Scheduled",
2181
+ color: "#2563EB",
2182
+ // blue
2183
+ backgroundColor: "#DBEAFE"
2184
+ },
2185
+ ["sending" /* SENDING */]: {
2186
+ label: "Sending",
2187
+ color: "#D97706",
2188
+ // yellow/orange
2189
+ backgroundColor: "#FEF3C7"
2190
+ },
2191
+ ["sent" /* SENT */]: {
2192
+ label: "Sent",
2193
+ color: "#059669",
2194
+ // green
2195
+ backgroundColor: "#D1FAE5"
2196
+ },
2197
+ ["failed" /* FAILED */]: {
2198
+ label: "Failed",
2199
+ color: "#DC2626",
2200
+ // red
2201
+ backgroundColor: "#FEE2E2"
2202
+ },
2203
+ ["paused" /* PAUSED */]: {
2204
+ label: "Paused",
2205
+ color: "#9333EA",
2206
+ // purple
2207
+ backgroundColor: "#EDE9FE"
2208
+ },
2209
+ ["canceled" /* CANCELED */]: {
2210
+ label: "Canceled",
2211
+ color: "#6B7280",
2212
+ // gray
2213
+ backgroundColor: "#F3F4F6"
2214
+ }
2215
+ };
2216
+ var StatusBadge = ({ cellData }) => {
2217
+ const status = cellData;
2218
+ const config = statusConfig[status] || statusConfig["draft" /* DRAFT */];
2219
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2220
+ "span",
2221
+ {
2222
+ style: {
2223
+ display: "inline-flex",
2224
+ alignItems: "center",
2225
+ padding: "2px 10px",
2226
+ borderRadius: "12px",
2227
+ fontSize: "12px",
2228
+ fontWeight: "500",
2229
+ color: config.color,
2230
+ backgroundColor: config.backgroundColor
2231
+ },
2232
+ children: config.label
2233
+ }
2234
+ );
2235
+ };
2236
+
2237
+ // src/components/Broadcasts/ActionsCell.tsx
2238
+ var import_react11 = require("react");
2239
+
2240
+ // src/components/Broadcasts/SendBroadcastModal.tsx
2241
+ var import_react10 = require("react");
2242
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2243
+ var SendBroadcastModal = ({
2244
+ broadcast,
2245
+ onClose,
2246
+ onSuccess
2247
+ }) => {
2248
+ const [loading, setLoading] = (0, import_react10.useState)(false);
2249
+ const [error, setError] = (0, import_react10.useState)(null);
2250
+ const [selectedAudiences, setSelectedAudiences] = (0, import_react10.useState)([]);
2251
+ const [audiences, setAudiences] = (0, import_react10.useState)([]);
2252
+ const [providerCapabilities, setProviderCapabilities] = (0, import_react10.useState)(null);
2253
+ const [sendMode, setSendMode] = (0, import_react10.useState)("now");
2254
+ const [scheduledDate, setScheduledDate] = (0, import_react10.useState)("");
2255
+ const [scheduledTime, setScheduledTime] = (0, import_react10.useState)("");
2256
+ (0, import_react10.useEffect)(() => {
2257
+ const fetchData = async () => {
2258
+ try {
2259
+ setAudiences([
2260
+ { id: "all", name: "All Subscribers", subscriberCount: 1e3 },
2261
+ { id: "active", name: "Active Subscribers", subscriberCount: 850 }
2262
+ ]);
2263
+ if (broadcast.channel?.providerType === "broadcast") {
2264
+ setProviderCapabilities({ supportsScheduling: true });
2265
+ } else {
2266
+ setProviderCapabilities({ supportsScheduling: false });
2267
+ }
2268
+ } catch (err) {
2269
+ console.error("Failed to fetch modal data:", err);
2270
+ }
2271
+ };
2272
+ fetchData();
2273
+ }, [broadcast.channel]);
2274
+ const handleSubmit = async (e) => {
2275
+ e.preventDefault();
2276
+ setError(null);
2277
+ setLoading(true);
2278
+ try {
2279
+ if (sendMode === "now") {
2280
+ const response = await fetch(`/api/broadcasts/${broadcast.id}/send`, {
2281
+ method: "POST",
2282
+ headers: {
2283
+ "Content-Type": "application/json"
2284
+ },
2285
+ body: JSON.stringify({
2286
+ audienceIds: selectedAudiences.length > 0 ? selectedAudiences : void 0
2287
+ })
2288
+ });
2289
+ if (!response.ok) {
2290
+ const data = await response.json();
2291
+ throw new Error(data.error || "Failed to send broadcast");
2292
+ }
2293
+ alert(`Broadcast "${broadcast.name}" has been sent successfully`);
2294
+ } else {
2295
+ if (!scheduledDate || !scheduledTime) {
2296
+ throw new Error("Please select both date and time for scheduling");
2297
+ }
2298
+ const scheduledAt = (/* @__PURE__ */ new Date(`${scheduledDate}T${scheduledTime}`)).toISOString();
2299
+ const response = await fetch(`/api/broadcasts/${broadcast.id}/schedule`, {
2300
+ method: "POST",
2301
+ headers: {
2302
+ "Content-Type": "application/json"
2303
+ },
2304
+ body: JSON.stringify({
2305
+ scheduledAt,
2306
+ audienceIds: selectedAudiences.length > 0 ? selectedAudiences : void 0
2307
+ })
2308
+ });
2309
+ if (!response.ok) {
2310
+ const data = await response.json();
2311
+ throw new Error(data.error || "Failed to schedule broadcast");
2312
+ }
2313
+ alert(`Broadcast "${broadcast.name}" has been scheduled successfully`);
2314
+ }
2315
+ onSuccess();
2316
+ } catch (err) {
2317
+ setError(err instanceof Error ? err.message : "An error occurred");
2318
+ } finally {
2319
+ setLoading(false);
2320
+ }
2321
+ };
2322
+ const handleAudienceToggle = (audienceId) => {
2323
+ setSelectedAudiences(
2324
+ (prev) => prev.includes(audienceId) ? prev.filter((id) => id !== audienceId) : [...prev, audienceId]
2325
+ );
2326
+ };
2327
+ const now = /* @__PURE__ */ new Date();
2328
+ const minDate = now.toISOString().split("T")[0];
2329
+ const minTime = now.toTimeString().slice(0, 5);
2330
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: {
2331
+ position: "fixed",
2332
+ top: 0,
2333
+ left: 0,
2334
+ right: 0,
2335
+ bottom: 0,
2336
+ backgroundColor: "rgba(0, 0, 0, 0.5)",
2337
+ display: "flex",
2338
+ alignItems: "center",
2339
+ justifyContent: "center",
2340
+ zIndex: 9999
2341
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: {
2342
+ backgroundColor: "white",
2343
+ borderRadius: "8px",
2344
+ padding: "32px",
2345
+ maxWidth: "500px",
2346
+ width: "90%",
2347
+ maxHeight: "90vh",
2348
+ overflowY: "auto"
2349
+ }, children: [
2350
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("h2", { style: { marginTop: 0, marginBottom: "24px" }, children: [
2351
+ "Send Broadcast: ",
2352
+ broadcast.name
2353
+ ] }),
2354
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("form", { onSubmit: handleSubmit, children: [
2355
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { marginBottom: "24px" }, children: [
2356
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { style: { fontWeight: "bold", display: "block", marginBottom: "8px" }, children: "When to send:" }),
2357
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", gap: "16px" }, children: [
2358
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { style: { display: "flex", alignItems: "center", cursor: "pointer" }, children: [
2359
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2360
+ "input",
2361
+ {
2362
+ type: "radio",
2363
+ value: "now",
2364
+ checked: sendMode === "now",
2365
+ onChange: (e) => setSendMode(e.target.value),
2366
+ style: { marginRight: "8px" }
2367
+ }
2368
+ ),
2369
+ "Send Now"
2370
+ ] }),
2371
+ providerCapabilities?.supportsScheduling && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("label", { style: { display: "flex", alignItems: "center", cursor: "pointer" }, children: [
2372
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2373
+ "input",
2374
+ {
2375
+ type: "radio",
2376
+ value: "schedule",
2377
+ checked: sendMode === "schedule",
2378
+ onChange: (e) => setSendMode(e.target.value),
2379
+ style: { marginRight: "8px" }
2380
+ }
2381
+ ),
2382
+ "Schedule for Later"
2383
+ ] })
2384
+ ] })
2385
+ ] }),
2386
+ sendMode === "schedule" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { marginBottom: "24px" }, children: [
2387
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { style: { fontWeight: "bold", display: "block", marginBottom: "8px" }, children: "Schedule Date & Time:" }),
2388
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
2389
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2390
+ "input",
2391
+ {
2392
+ type: "date",
2393
+ value: scheduledDate,
2394
+ onChange: (e) => setScheduledDate(e.target.value),
2395
+ min: minDate,
2396
+ required: sendMode === "schedule",
2397
+ style: {
2398
+ padding: "8px 12px",
2399
+ border: "1px solid #D1D5DB",
2400
+ borderRadius: "4px",
2401
+ flex: 1
2402
+ }
2403
+ }
2404
+ ),
2405
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2406
+ "input",
2407
+ {
2408
+ type: "time",
2409
+ value: scheduledTime,
2410
+ onChange: (e) => setScheduledTime(e.target.value),
2411
+ min: scheduledDate === minDate ? minTime : void 0,
2412
+ required: sendMode === "schedule",
2413
+ style: {
2414
+ padding: "8px 12px",
2415
+ border: "1px solid #D1D5DB",
2416
+ borderRadius: "4px",
2417
+ flex: 1
2418
+ }
2419
+ }
2420
+ )
2421
+ ] })
2422
+ ] }),
2423
+ audiences.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { marginBottom: "24px" }, children: [
2424
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("label", { style: { fontWeight: "bold", display: "block", marginBottom: "8px" }, children: "Target Audiences (optional):" }),
2425
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: {
2426
+ border: "1px solid #D1D5DB",
2427
+ borderRadius: "4px",
2428
+ maxHeight: "200px",
2429
+ overflowY: "auto"
2430
+ }, children: audiences.map((audience) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2431
+ "label",
2432
+ {
2433
+ style: {
2434
+ display: "flex",
2435
+ alignItems: "center",
2436
+ padding: "12px",
2437
+ borderBottom: "1px solid #E5E7EB",
2438
+ cursor: "pointer",
2439
+ backgroundColor: selectedAudiences.includes(audience.id) ? "#F3F4F6" : "transparent"
2440
+ },
2441
+ children: [
2442
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2443
+ "input",
2444
+ {
2445
+ type: "checkbox",
2446
+ checked: selectedAudiences.includes(audience.id),
2447
+ onChange: () => handleAudienceToggle(audience.id),
2448
+ style: { marginRight: "12px" }
2449
+ }
2450
+ ),
2451
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { flex: 1 }, children: [
2452
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontWeight: 500 }, children: audience.name }),
2453
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { fontSize: "14px", color: "#6B7280" }, children: [
2454
+ audience.subscriberCount.toLocaleString(),
2455
+ " subscribers"
2456
+ ] })
2457
+ ] })
2458
+ ]
2459
+ },
2460
+ audience.id
2461
+ )) }),
2462
+ selectedAudiences.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: { fontSize: "14px", color: "#6B7280", marginTop: "8px" }, children: "If no audiences are selected, the broadcast will be sent to all subscribers." })
2463
+ ] }),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: {
2465
+ backgroundColor: "#F3F4F6",
2466
+ padding: "16px",
2467
+ borderRadius: "4px",
2468
+ marginBottom: "24px"
2469
+ }, children: [
2470
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h4", { style: { marginTop: 0, marginBottom: "8px" }, children: "Summary" }),
2471
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { style: { margin: 0, fontSize: "14px" }, children: [
2472
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Subject:" }),
2473
+ " ",
2474
+ broadcast.subject,
2475
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("br", {}),
2476
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Channel:" }),
2477
+ " ",
2478
+ broadcast.channel?.name || "Unknown",
2479
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("br", {}),
2480
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Provider:" }),
2481
+ " ",
2482
+ broadcast.channel?.providerType || "Unknown",
2483
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("br", {}),
2484
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "When:" }),
2485
+ " ",
2486
+ sendMode === "now" ? "Immediately" : `${scheduledDate} at ${scheduledTime}`,
2487
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("br", {}),
2488
+ selectedAudiences.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2489
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("strong", { children: "Audiences:" }),
2490
+ " ",
2491
+ selectedAudiences.length,
2492
+ " selected"
2493
+ ] })
2494
+ ] })
2495
+ ] }),
2496
+ error && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: {
2497
+ backgroundColor: "#FEE2E2",
2498
+ border: "1px solid #FCA5A5",
2499
+ color: "#DC2626",
2500
+ padding: "12px",
2501
+ borderRadius: "4px",
2502
+ marginBottom: "24px"
2503
+ }, children: error }),
2504
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: "12px" }, children: [
2505
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2506
+ "button",
2507
+ {
2508
+ type: "button",
2509
+ onClick: onClose,
2510
+ disabled: loading,
2511
+ style: {
2512
+ padding: "8px 24px",
2513
+ backgroundColor: "#E5E7EB",
2514
+ color: "#1F2937",
2515
+ border: "none",
2516
+ borderRadius: "4px",
2517
+ fontSize: "16px",
2518
+ cursor: loading ? "not-allowed" : "pointer",
2519
+ opacity: loading ? 0.6 : 1
2520
+ },
2521
+ children: "Cancel"
2522
+ }
2523
+ ),
2524
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2525
+ "button",
2526
+ {
2527
+ type: "submit",
2528
+ disabled: loading,
2529
+ style: {
2530
+ padding: "8px 24px",
2531
+ backgroundColor: "#2563EB",
2532
+ color: "white",
2533
+ border: "none",
2534
+ borderRadius: "4px",
2535
+ fontSize: "16px",
2536
+ cursor: loading ? "not-allowed" : "pointer",
2537
+ opacity: loading ? 0.6 : 1
2538
+ },
2539
+ children: loading ? "Processing..." : sendMode === "now" ? "Send Now" : "Schedule"
2540
+ }
2541
+ )
2542
+ ] })
2543
+ ] })
2544
+ ] }) });
2545
+ };
2546
+
2547
+ // src/components/Broadcasts/ActionsCell.tsx
2548
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2549
+ var ActionsCell = ({ rowData }) => {
2550
+ const [loading, setLoading] = (0, import_react11.useState)(false);
2551
+ const [showSendModal, setShowSendModal] = (0, import_react11.useState)(false);
2552
+ const broadcast = rowData;
2553
+ const handleSend = () => {
2554
+ if (!broadcast.providerId) {
2555
+ alert("This broadcast has not been synced with the provider yet");
2556
+ return;
2557
+ }
2558
+ setShowSendModal(true);
2559
+ };
2560
+ const handleSchedule = () => {
2561
+ if (!broadcast.providerId) {
2562
+ alert("This broadcast has not been synced with the provider yet");
2563
+ return;
2564
+ }
2565
+ setShowSendModal(true);
2566
+ };
2567
+ const handleDuplicate = async () => {
2568
+ try {
2569
+ setLoading(true);
2570
+ const response = await fetch(`/api/broadcasts/${broadcast.id}`);
2571
+ const data = await response.json();
2572
+ delete data.id;
2573
+ delete data.createdAt;
2574
+ delete data.updatedAt;
2575
+ delete data.providerId;
2576
+ delete data.sentAt;
2577
+ delete data.scheduledAt;
2578
+ delete data.analytics;
2579
+ data.name = `${data.name} (Copy)`;
2580
+ data.status = "draft" /* DRAFT */;
2581
+ const createResponse = await fetch("/api/broadcasts", {
2582
+ method: "POST",
2583
+ headers: {
2584
+ "Content-Type": "application/json"
2585
+ },
2586
+ body: JSON.stringify(data)
2587
+ });
2588
+ if (!createResponse.ok) {
2589
+ throw new Error("Failed to duplicate broadcast");
2590
+ }
2591
+ alert("Broadcast duplicated successfully");
2592
+ window.location.reload();
2593
+ } catch (error) {
2594
+ alert(error instanceof Error ? error.message : "Failed to duplicate broadcast");
2595
+ } finally {
2596
+ setLoading(false);
2597
+ }
2598
+ };
2599
+ const canSend = broadcast.status === "draft" /* DRAFT */;
2600
+ const canSchedule = broadcast.status === "draft" /* DRAFT */;
2601
+ const canDuplicate = true;
2602
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
2603
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
2604
+ canSend && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2605
+ "button",
2606
+ {
2607
+ onClick: handleSend,
2608
+ disabled: loading,
2609
+ style: {
2610
+ padding: "4px 12px",
2611
+ backgroundColor: "#2563EB",
2612
+ color: "white",
2613
+ border: "none",
2614
+ borderRadius: "4px",
2615
+ fontSize: "12px",
2616
+ cursor: loading ? "not-allowed" : "pointer",
2617
+ opacity: loading ? 0.6 : 1
2618
+ },
2619
+ children: "Send"
2620
+ }
2621
+ ),
2622
+ canSchedule && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2623
+ "button",
2624
+ {
2625
+ onClick: handleSchedule,
2626
+ disabled: loading,
2627
+ style: {
2628
+ padding: "4px 12px",
2629
+ backgroundColor: "#6366F1",
2630
+ color: "white",
2631
+ border: "none",
2632
+ borderRadius: "4px",
2633
+ fontSize: "12px",
2634
+ cursor: loading ? "not-allowed" : "pointer",
2635
+ opacity: loading ? 0.6 : 1
2636
+ },
2637
+ children: "Schedule"
2638
+ }
2639
+ ),
2640
+ canDuplicate && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2641
+ "button",
2642
+ {
2643
+ onClick: handleDuplicate,
2644
+ disabled: loading,
2645
+ style: {
2646
+ padding: "4px 12px",
2647
+ backgroundColor: "#6B7280",
2648
+ color: "white",
2649
+ border: "none",
2650
+ borderRadius: "4px",
2651
+ fontSize: "12px",
2652
+ cursor: loading ? "not-allowed" : "pointer",
2653
+ opacity: loading ? 0.6 : 1
2654
+ },
2655
+ children: "Duplicate"
2656
+ }
2657
+ )
2658
+ ] }),
2659
+ showSendModal && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2660
+ SendBroadcastModal,
2661
+ {
2662
+ broadcast,
2663
+ onClose: () => setShowSendModal(false),
2664
+ onSuccess: () => {
2665
+ setShowSendModal(false);
2666
+ window.location.reload();
2667
+ }
2668
+ }
2669
+ )
2670
+ ] });
2671
+ };
2672
+
2673
+ // src/components/Broadcasts/EmptyField.tsx
2674
+ var EmptyField = () => {
2675
+ return null;
2676
+ };
2166
2677
  // Annotate the CommonJS export names for ESM import in node:
2167
2678
  0 && (module.exports = {
2679
+ ActionsCell,
2168
2680
  BroadcastEditor,
2169
2681
  BroadcastInlinePreview,
2170
2682
  BroadcastPreviewField,
@@ -2172,10 +2684,12 @@ var BroadcastPreviewField = () => {
2172
2684
  EmailPreview,
2173
2685
  EmailPreviewField,
2174
2686
  EmailRenderer,
2687
+ EmptyField,
2175
2688
  MagicLinkVerify,
2176
2689
  NewsletterForm,
2177
2690
  PreferencesForm,
2178
2691
  PreviewControls,
2692
+ StatusBadge,
2179
2693
  createMagicLinkVerify,
2180
2694
  createNewsletterForm,
2181
2695
  createPreferencesForm,