@mobilizehub/payload-plugin 0.0.1 → 0.2.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.
Files changed (91) hide show
  1. package/dist/access/authenticated.d.ts +4 -0
  2. package/dist/access/authenticated.js +5 -0
  3. package/dist/access/authenticated.js.map +1 -0
  4. package/dist/access/authenticated.spec.d.ts +1 -0
  5. package/dist/access/authenticated.spec.js +33 -0
  6. package/dist/access/authenticated.spec.js.map +1 -0
  7. package/dist/adapters/index.d.ts +1 -0
  8. package/dist/adapters/index.js +3 -0
  9. package/dist/adapters/index.js.map +1 -0
  10. package/dist/adapters/resend-adapter.d.ts +34 -0
  11. package/dist/adapters/resend-adapter.js +219 -0
  12. package/dist/adapters/resend-adapter.js.map +1 -0
  13. package/dist/collections/broadcasts/generateBroadcastsCollection.d.ts +3 -0
  14. package/dist/collections/broadcasts/generateBroadcastsCollection.js +241 -0
  15. package/dist/collections/broadcasts/generateBroadcastsCollection.js.map +1 -0
  16. package/dist/collections/contacts/generateContactsCollection.d.ts +22 -0
  17. package/dist/collections/contacts/generateContactsCollection.js +124 -0
  18. package/dist/collections/contacts/generateContactsCollection.js.map +1 -0
  19. package/dist/collections/emails/generateEmailsCollection.d.ts +3 -0
  20. package/dist/collections/emails/generateEmailsCollection.js +204 -0
  21. package/dist/collections/emails/generateEmailsCollection.js.map +1 -0
  22. package/dist/collections/emails/hooks/sync-status-from-activity.d.ts +5 -0
  23. package/dist/collections/emails/hooks/sync-status-from-activity.js +64 -0
  24. package/dist/collections/emails/hooks/sync-status-from-activity.js.map +1 -0
  25. package/dist/collections/tags/generateTagsCollection.d.ts +3 -0
  26. package/dist/collections/tags/generateTagsCollection.js +29 -0
  27. package/dist/collections/tags/generateTagsCollection.js.map +1 -0
  28. package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.d.ts +2 -0
  29. package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.js +48 -0
  30. package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.js.map +1 -0
  31. package/dist/components/broadcast-metrics-card.d.ts +7 -0
  32. package/dist/components/broadcast-metrics-card.js +159 -0
  33. package/dist/components/broadcast-metrics-card.js.map +1 -0
  34. package/dist/components/broadcast-send-modal.d.ts +9 -0
  35. package/dist/components/broadcast-send-modal.js +51 -0
  36. package/dist/components/broadcast-send-modal.js.map +1 -0
  37. package/dist/components/broadcast-send-test-drawer.d.ts +7 -0
  38. package/dist/components/broadcast-send-test-drawer.js +154 -0
  39. package/dist/components/broadcast-send-test-drawer.js.map +1 -0
  40. package/dist/components/email-activity.d.ts +4 -0
  41. package/dist/components/email-activity.js +359 -0
  42. package/dist/components/email-activity.js.map +1 -0
  43. package/dist/components/email-preview.d.ts +2 -0
  44. package/dist/components/email-preview.js +95 -0
  45. package/dist/components/email-preview.js.map +1 -0
  46. package/dist/endpoints/sendBroadcastHandler.d.ts +9 -0
  47. package/dist/endpoints/sendBroadcastHandler.js +107 -0
  48. package/dist/endpoints/sendBroadcastHandler.js.map +1 -0
  49. package/dist/endpoints/sendTestBroadcastHandler.d.ts +10 -0
  50. package/dist/endpoints/sendTestBroadcastHandler.js +143 -0
  51. package/dist/endpoints/sendTestBroadcastHandler.js.map +1 -0
  52. package/dist/endpoints/unsubscribeHandler.d.ts +9 -0
  53. package/dist/endpoints/unsubscribeHandler.js +153 -0
  54. package/dist/endpoints/unsubscribeHandler.js.map +1 -0
  55. package/dist/exports/client.d.ts +3 -1
  56. package/dist/exports/client.js +3 -0
  57. package/dist/exports/client.js.map +1 -1
  58. package/dist/exports/rsc.d.ts +2 -1
  59. package/dist/exports/rsc.js +2 -0
  60. package/dist/exports/rsc.js.map +1 -1
  61. package/dist/index.d.ts +2 -3
  62. package/dist/index.js +51 -2
  63. package/dist/index.js.map +1 -1
  64. package/dist/react/index.d.ts +1 -0
  65. package/dist/react/index.js +3 -0
  66. package/dist/react/index.js.map +1 -0
  67. package/dist/react/unsubscribe.d.ts +6 -0
  68. package/dist/react/unsubscribe.js +16 -0
  69. package/dist/react/unsubscribe.js.map +1 -0
  70. package/dist/tasks/sendBroadcastsTask.d.ts +11 -0
  71. package/dist/tasks/sendBroadcastsTask.js +196 -0
  72. package/dist/tasks/sendBroadcastsTask.js.map +1 -0
  73. package/dist/tasks/sendEmailTask.d.ts +9 -0
  74. package/dist/tasks/sendEmailTask.js +167 -0
  75. package/dist/tasks/sendEmailTask.js.map +1 -0
  76. package/dist/types/index.d.ts +135 -0
  77. package/dist/types/index.js +3 -0
  78. package/dist/types/index.js.map +1 -0
  79. package/dist/utils/api-response.d.ts +72 -0
  80. package/dist/utils/api-response.js +66 -0
  81. package/dist/utils/api-response.js.map +1 -0
  82. package/dist/utils/email.d.ts +36 -0
  83. package/dist/utils/email.js +40 -0
  84. package/dist/utils/email.js.map +1 -0
  85. package/dist/utils/lexical.d.ts +13 -0
  86. package/dist/utils/lexical.js +27 -0
  87. package/dist/utils/lexical.js.map +1 -0
  88. package/dist/utils/unsubscribe-token.d.ts +67 -0
  89. package/dist/utils/unsubscribe-token.js +103 -0
  90. package/dist/utils/unsubscribe-token.js.map +1 -0
  91. package/package.json +25 -9
@@ -0,0 +1,159 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ function roundToTwo(num) {
3
+ return Math.round(num * 100) / 100;
4
+ }
5
+ export const MetricsCards = async ({ data, payload })=>{
6
+ const broadcastId = data.id;
7
+ if (!broadcastId) {
8
+ return null;
9
+ }
10
+ const broadcast = await payload.find({
11
+ collection: 'broadcasts',
12
+ limit: 1,
13
+ where: {
14
+ id: {
15
+ equals: broadcastId
16
+ }
17
+ }
18
+ });
19
+ if (!broadcast.totalDocs) {
20
+ return null;
21
+ }
22
+ const contacts = broadcast.docs[0].meta.contactsCount;
23
+ // Run all count queries in parallel for better performance
24
+ const [{ totalDocs: emails }, { totalDocs: delivered }, { totalDocs: bounced }, { totalDocs: unsubscribed }, { totalDocs: complained }] = await Promise.all([
25
+ payload.count({
26
+ collection: 'emails',
27
+ where: {
28
+ broadcast: {
29
+ equals: broadcastId
30
+ }
31
+ }
32
+ }),
33
+ payload.count({
34
+ collection: 'emails',
35
+ where: {
36
+ broadcast: {
37
+ equals: broadcastId
38
+ },
39
+ status: {
40
+ equals: 'delivered'
41
+ }
42
+ }
43
+ }),
44
+ payload.count({
45
+ collection: 'emails',
46
+ where: {
47
+ broadcast: {
48
+ equals: broadcastId
49
+ },
50
+ status: {
51
+ equals: 'bounced'
52
+ }
53
+ }
54
+ }),
55
+ payload.count({
56
+ collection: 'emails',
57
+ where: {
58
+ broadcast: {
59
+ equals: broadcastId
60
+ },
61
+ status: {
62
+ equals: 'unsubscribed'
63
+ }
64
+ }
65
+ }),
66
+ payload.count({
67
+ collection: 'emails',
68
+ where: {
69
+ broadcast: {
70
+ equals: broadcastId
71
+ },
72
+ status: {
73
+ equals: 'complained'
74
+ }
75
+ }
76
+ })
77
+ ]);
78
+ return /*#__PURE__*/ _jsxs("div", {
79
+ style: {
80
+ marginBottom: '2.5rem'
81
+ },
82
+ children: [
83
+ /*#__PURE__*/ _jsx("div", {
84
+ style: {
85
+ marginBottom: '1rem'
86
+ },
87
+ children: /*#__PURE__*/ _jsx("p", {
88
+ children: "Metrics"
89
+ })
90
+ }),
91
+ /*#__PURE__*/ _jsx("div", {
92
+ className: "metrics-grid",
93
+ style: {
94
+ display: 'grid',
95
+ gap: '1.5rem',
96
+ gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))'
97
+ },
98
+ children: [
99
+ {
100
+ name: 'Contacts',
101
+ value: contacts
102
+ },
103
+ {
104
+ name: 'Emails',
105
+ value: emails
106
+ },
107
+ {
108
+ name: 'Delivered',
109
+ value: delivered
110
+ },
111
+ {
112
+ name: 'Bounced',
113
+ value: bounced
114
+ },
115
+ {
116
+ name: 'Unsubscribed',
117
+ value: unsubscribed
118
+ },
119
+ {
120
+ name: 'Complained',
121
+ value: complained
122
+ }
123
+ ].map((stat)=>/*#__PURE__*/ _jsxs("div", {
124
+ className: "card",
125
+ children: [
126
+ /*#__PURE__*/ _jsxs("div", {
127
+ children: [
128
+ /*#__PURE__*/ _jsx("dt", {
129
+ className: "card__title",
130
+ children: stat.name
131
+ }),
132
+ /*#__PURE__*/ _jsx("dd", {
133
+ style: {
134
+ color: 'var(--theme-elevation-1000)',
135
+ fontSize: '1.5rem',
136
+ fontWeight: '600',
137
+ letterSpacing: '-0.025em',
138
+ lineHeight: '2.5rem',
139
+ margin: 0
140
+ },
141
+ children: stat.value.toLocaleString()
142
+ })
143
+ ]
144
+ }),
145
+ stat.name !== 'Emails' && stat.name !== 'Contacts' && emails > 0 && /*#__PURE__*/ _jsxs("dd", {
146
+ className: "card__action",
147
+ children: [
148
+ roundToTwo(stat.value / emails * 100),
149
+ "%"
150
+ ]
151
+ })
152
+ ]
153
+ }, stat.name))
154
+ })
155
+ ]
156
+ });
157
+ };
158
+
159
+ //# sourceMappingURL=broadcast-metrics-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/broadcast-metrics-card.tsx"],"sourcesContent":["import type { Payload } from 'payload'\n\nfunction roundToTwo(num: number): number {\n return Math.round(num * 100) / 100\n}\n\nexport const MetricsCards = async ({\n data,\n payload,\n}: {\n data: { id: number }\n payload: Payload\n}) => {\n const broadcastId = data.id\n\n if (!broadcastId) {\n return null\n }\n\n const broadcast = await payload.find({\n collection: 'broadcasts',\n limit: 1,\n where: {\n id: {\n equals: broadcastId,\n },\n },\n })\n\n if (!broadcast.totalDocs) {\n return null\n }\n\n const contacts = broadcast.docs[0].meta.contactsCount as number\n\n // Run all count queries in parallel for better performance\n const [\n { totalDocs: emails },\n { totalDocs: delivered },\n { totalDocs: bounced },\n { totalDocs: unsubscribed },\n { totalDocs: complained },\n ] = await Promise.all([\n payload.count({\n collection: 'emails',\n where: {\n broadcast: { equals: broadcastId },\n },\n }),\n payload.count({\n collection: 'emails',\n where: {\n broadcast: { equals: broadcastId },\n status: { equals: 'delivered' },\n },\n }),\n payload.count({\n collection: 'emails',\n where: {\n broadcast: { equals: broadcastId },\n status: { equals: 'bounced' },\n },\n }),\n payload.count({\n collection: 'emails',\n where: {\n broadcast: { equals: broadcastId },\n status: { equals: 'unsubscribed' },\n },\n }),\n payload.count({\n collection: 'emails',\n where: {\n broadcast: { equals: broadcastId },\n status: { equals: 'complained' },\n },\n }),\n ])\n\n return (\n <div style={{ marginBottom: '2.5rem' }}>\n <div style={{ marginBottom: '1rem' }}>\n <p>Metrics</p>\n </div>\n <div\n className=\"metrics-grid\"\n style={{\n display: 'grid',\n gap: '1.5rem',\n gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',\n }}\n >\n {[\n { name: 'Contacts', value: contacts },\n { name: 'Emails', value: emails },\n { name: 'Delivered', value: delivered },\n { name: 'Bounced', value: bounced },\n { name: 'Unsubscribed', value: unsubscribed },\n { name: 'Complained', value: complained },\n ].map((stat) => (\n <div className=\"card\" key={stat.name}>\n <div>\n <dt className=\"card__title\">{stat.name}</dt>\n <dd\n style={{\n color: 'var(--theme-elevation-1000)',\n fontSize: '1.5rem',\n fontWeight: '600',\n letterSpacing: '-0.025em',\n lineHeight: '2.5rem',\n margin: 0,\n }}\n >\n {stat.value.toLocaleString()}\n </dd>\n </div>\n {stat.name !== 'Emails' && stat.name !== 'Contacts' && emails > 0 && (\n <dd className=\"card__action\">{roundToTwo((stat.value / emails) * 100)}%</dd>\n )}\n </div>\n ))}\n </div>\n </div>\n )\n}\n"],"names":["roundToTwo","num","Math","round","MetricsCards","data","payload","broadcastId","id","broadcast","find","collection","limit","where","equals","totalDocs","contacts","docs","meta","contactsCount","emails","delivered","bounced","unsubscribed","complained","Promise","all","count","status","div","style","marginBottom","p","className","display","gap","gridTemplateColumns","name","value","map","stat","dt","dd","color","fontSize","fontWeight","letterSpacing","lineHeight","margin","toLocaleString"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ interface SendBroadcastModalProps {
3
+ buttonLabel?: string;
4
+ modalBody?: string;
5
+ modalSlug?: string;
6
+ modalTitle?: string;
7
+ }
8
+ export declare const SendBroadcastModal: React.FC<SendBroadcastModalProps>;
9
+ export {};
@@ -0,0 +1,51 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button, ConfirmationModal, toast, useDocumentInfo, useFormModified, useModal } from '@payloadcms/ui';
4
+ import React from 'react';
5
+ const apiEndpoint = '/api/send-broadcast';
6
+ export const SendBroadcastModal = ({ buttonLabel = 'Send broadcast', modalBody = 'Are you sure you want to send this broadcast?', modalSlug = 'send-broadcast-modal', modalTitle = 'Send Broadcast' })=>{
7
+ const { id, initialData } = useDocumentInfo();
8
+ const modified = useFormModified();
9
+ const isDraft = initialData?.status === 'draft';
10
+ const { toggleModal } = useModal();
11
+ if (!id) {
12
+ return null;
13
+ }
14
+ const handleOpenModal = ()=>{
15
+ toggleModal(modalSlug);
16
+ };
17
+ const handleConfirm = async ()=>{
18
+ await fetch(apiEndpoint, {
19
+ body: JSON.stringify({
20
+ broadcastId: id
21
+ }),
22
+ headers: {
23
+ 'Content-Type': 'application/json'
24
+ },
25
+ method: 'POST'
26
+ });
27
+ toast.success('Broadcast queued for sending');
28
+ window.location.reload();
29
+ toggleModal(modalSlug);
30
+ };
31
+ return /*#__PURE__*/ _jsxs(_Fragment, {
32
+ children: [
33
+ /*#__PURE__*/ _jsx(Button, {
34
+ disabled: modified || !isDraft,
35
+ onClick: handleOpenModal,
36
+ size: "medium",
37
+ children: buttonLabel
38
+ }),
39
+ /*#__PURE__*/ _jsx(ConfirmationModal, {
40
+ body: modalBody || /*#__PURE__*/ _jsx("p", {
41
+ children: "Are you sure you want to proceed with this action?"
42
+ }),
43
+ heading: modalTitle,
44
+ modalSlug: modalSlug,
45
+ onConfirm: handleConfirm
46
+ })
47
+ ]
48
+ });
49
+ };
50
+
51
+ //# sourceMappingURL=broadcast-send-modal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/broadcast-send-modal.tsx"],"sourcesContent":["'use client'\n\nimport {\n Button,\n ConfirmationModal,\n toast,\n useDocumentInfo,\n useFormModified,\n useModal,\n} from '@payloadcms/ui'\nimport React from 'react'\n\ninterface SendBroadcastModalProps {\n buttonLabel?: string\n modalBody?: string\n modalSlug?: string\n modalTitle?: string\n}\n\nconst apiEndpoint = '/api/send-broadcast'\n\nexport const SendBroadcastModal: React.FC<SendBroadcastModalProps> = ({\n buttonLabel = 'Send broadcast',\n modalBody = 'Are you sure you want to send this broadcast?',\n modalSlug = 'send-broadcast-modal',\n modalTitle = 'Send Broadcast',\n}) => {\n const { id, initialData } = useDocumentInfo()\n const modified = useFormModified()\n\n const isDraft = initialData?.status === 'draft'\n\n const { toggleModal } = useModal()\n if (!id) {\n return null\n }\n\n const handleOpenModal = () => {\n toggleModal(modalSlug)\n }\n\n const handleConfirm = async () => {\n await fetch(apiEndpoint, {\n body: JSON.stringify({ broadcastId: id }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n toast.success('Broadcast queued for sending')\n window.location.reload()\n toggleModal(modalSlug)\n }\n\n return (\n <>\n <Button disabled={modified || !isDraft} onClick={handleOpenModal} size=\"medium\">\n {buttonLabel}\n </Button>\n\n <ConfirmationModal\n body={modalBody || <p>Are you sure you want to proceed with this action?</p>}\n heading={modalTitle}\n modalSlug={modalSlug}\n onConfirm={handleConfirm}\n />\n </>\n )\n}\n"],"names":["Button","ConfirmationModal","toast","useDocumentInfo","useFormModified","useModal","React","apiEndpoint","SendBroadcastModal","buttonLabel","modalBody","modalSlug","modalTitle","id","initialData","modified","isDraft","status","toggleModal","handleOpenModal","handleConfirm","fetch","body","JSON","stringify","broadcastId","headers","method","success","window","location","reload","disabled","onClick","size","p","heading","onConfirm"],"mappings":"AAAA;;AAEA,SACEA,MAAM,EACNC,iBAAiB,EACjBC,KAAK,EACLC,eAAe,EACfC,eAAe,EACfC,QAAQ,QACH,iBAAgB;AACvB,OAAOC,WAAW,QAAO;AASzB,MAAMC,cAAc;AAEpB,OAAO,MAAMC,qBAAwD,CAAC,EACpEC,cAAc,gBAAgB,EAC9BC,YAAY,+CAA+C,EAC3DC,YAAY,sBAAsB,EAClCC,aAAa,gBAAgB,EAC9B;IACC,MAAM,EAAEC,EAAE,EAAEC,WAAW,EAAE,GAAGX;IAC5B,MAAMY,WAAWX;IAEjB,MAAMY,UAAUF,aAAaG,WAAW;IAExC,MAAM,EAAEC,WAAW,EAAE,GAAGb;IACxB,IAAI,CAACQ,IAAI;QACP,OAAO;IACT;IAEA,MAAMM,kBAAkB;QACtBD,YAAYP;IACd;IAEA,MAAMS,gBAAgB;QACpB,MAAMC,MAAMd,aAAa;YACvBe,MAAMC,KAAKC,SAAS,CAAC;gBAAEC,aAAaZ;YAAG;YACvCa,SAAS;gBACP,gBAAgB;YAClB;YACAC,QAAQ;QACV;QAEAzB,MAAM0B,OAAO,CAAC;QACdC,OAAOC,QAAQ,CAACC,MAAM;QACtBb,YAAYP;IACd;IAEA,qBACE;;0BACE,KAACX;gBAAOgC,UAAUjB,YAAY,CAACC;gBAASiB,SAASd;gBAAiBe,MAAK;0BACpEzB;;0BAGH,KAACR;gBACCqB,MAAMZ,2BAAa,KAACyB;8BAAE;;gBACtBC,SAASxB;gBACTD,WAAWA;gBACX0B,WAAWjB;;;;AAInB,EAAC"}
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ interface TestBroadcastDrawerProps {
3
+ broadcastId?: string;
4
+ buttonLabel?: string;
5
+ }
6
+ export declare const SendTestBroadcastDrawer: React.FC<TestBroadcastDrawerProps>;
7
+ export {};
@@ -0,0 +1,154 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Button, Drawer, toast, useDocumentInfo, useFormModified, useModal } from '@payloadcms/ui';
4
+ import React, { useCallback, useState } from 'react';
5
+ import { isValidEmail } from '../utils/email.js';
6
+ const apiEndpoint = '/api/send-test-email';
7
+ export const SendTestBroadcastDrawer = ({ buttonLabel = 'Test email' })=>{
8
+ const [email, setEmail] = useState('');
9
+ const [isLoading, setIsLoading] = useState(false);
10
+ const [error, setError] = useState(null);
11
+ const { id, initialData } = useDocumentInfo();
12
+ const { toggleModal } = useModal();
13
+ const modified = useFormModified();
14
+ const isDraft = initialData?.status === 'draft';
15
+ const drawerSlug = 'test-broadcast-drawer';
16
+ const handleSendTest = useCallback(async ()=>{
17
+ setError(null);
18
+ if (!email) {
19
+ setError('Please enter an email address');
20
+ return;
21
+ }
22
+ if (!isValidEmail(email)) {
23
+ setError('Please enter a valid email address');
24
+ return;
25
+ }
26
+ setIsLoading(true);
27
+ try {
28
+ const response = await fetch(apiEndpoint, {
29
+ body: JSON.stringify({
30
+ broadcastId: id,
31
+ testEmail: email
32
+ }),
33
+ headers: {
34
+ 'Content-Type': 'application/json'
35
+ },
36
+ method: 'POST'
37
+ });
38
+ const data = await response.json();
39
+ if (response.ok) {
40
+ setEmail(''); // Clear the input
41
+ toast.success('Test broadcast sent successfully');
42
+ toggleModal(drawerSlug);
43
+ } else {
44
+ toast.error('Failed to send test broadcast');
45
+ setError(data.message || 'Failed to send test broadcast');
46
+ }
47
+ } catch {
48
+ toast.error('Failed to send test broadcast');
49
+ setError('An error occurred while sending the test broadcast');
50
+ } finally{
51
+ setIsLoading(false);
52
+ }
53
+ }, [
54
+ email,
55
+ id,
56
+ toggleModal
57
+ ]);
58
+ if (!id) {
59
+ return null;
60
+ }
61
+ return /*#__PURE__*/ _jsxs(_Fragment, {
62
+ children: [
63
+ /*#__PURE__*/ _jsx(Button, {
64
+ disabled: !isDraft || modified,
65
+ onClick: ()=>toggleModal(drawerSlug),
66
+ size: "medium",
67
+ children: buttonLabel
68
+ }),
69
+ /*#__PURE__*/ _jsx(Drawer, {
70
+ slug: drawerSlug,
71
+ title: "Send Test",
72
+ children: /*#__PURE__*/ _jsxs("form", {
73
+ className: "form",
74
+ noValidate: true,
75
+ onSubmit: handleSendTest,
76
+ children: [
77
+ /*#__PURE__*/ _jsxs("div", {
78
+ className: "render-fields",
79
+ children: [
80
+ error && /*#__PURE__*/ _jsx("div", {
81
+ className: "field-type",
82
+ style: {
83
+ backgroundColor: 'var(--theme-error-50)',
84
+ border: '1px solid var(--theme-error-500)',
85
+ borderRadius: '4px',
86
+ color: 'var(--theme-error-900)',
87
+ marginBottom: 'var(--base-unit)',
88
+ padding: '0.75rem'
89
+ },
90
+ children: error
91
+ }),
92
+ /*#__PURE__*/ _jsxs("div", {
93
+ className: "field-type text",
94
+ children: [
95
+ /*#__PURE__*/ _jsxs("label", {
96
+ className: "field-label",
97
+ htmlFor: "field-test-email",
98
+ children: [
99
+ "Email Address",
100
+ /*#__PURE__*/ _jsx("span", {
101
+ className: "required",
102
+ children: "*"
103
+ })
104
+ ]
105
+ }),
106
+ /*#__PURE__*/ _jsx("div", {
107
+ className: "field-type__wrap",
108
+ children: /*#__PURE__*/ _jsx("input", {
109
+ "aria-label": "Email address",
110
+ "data-rtl": "false",
111
+ disabled: isLoading,
112
+ id: "field-test-email",
113
+ name: "testEmail",
114
+ onChange: (e)=>{
115
+ setEmail(e.target.value);
116
+ setError(null);
117
+ },
118
+ placeholder: "name@example.com",
119
+ type: "email",
120
+ value: email
121
+ })
122
+ }),
123
+ /*#__PURE__*/ _jsx("div", {
124
+ className: "field-description field-description-testEmail",
125
+ children: "A test version of this broadcast will be sent to the email address above. The test email will be clearly marked as a test."
126
+ })
127
+ ]
128
+ })
129
+ ]
130
+ }),
131
+ /*#__PURE__*/ _jsx("div", {
132
+ className: "form-submit",
133
+ children: /*#__PURE__*/ _jsx("button", {
134
+ "aria-label": "Send test broadcast",
135
+ className: "btn btn--icon-style-without-border btn--size-medium btn--style-primary",
136
+ disabled: isLoading || !email,
137
+ type: "submit",
138
+ children: /*#__PURE__*/ _jsx("span", {
139
+ className: "btn__content",
140
+ children: /*#__PURE__*/ _jsx("span", {
141
+ className: "btn__label",
142
+ children: isLoading ? 'Sending...' : 'Send Test'
143
+ })
144
+ })
145
+ })
146
+ })
147
+ ]
148
+ })
149
+ })
150
+ ]
151
+ });
152
+ };
153
+
154
+ //# sourceMappingURL=broadcast-send-test-drawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/broadcast-send-test-drawer.tsx"],"sourcesContent":["'use client'\n\nimport { Button, Drawer, toast, useDocumentInfo, useFormModified, useModal } from '@payloadcms/ui'\nimport React, { useCallback, useState } from 'react'\n\nimport { isValidEmail } from '../utils/email.js'\n\ninterface TestBroadcastDrawerProps {\n broadcastId?: string\n buttonLabel?: string\n}\n\nconst apiEndpoint = '/api/send-test-email'\n\nexport const SendTestBroadcastDrawer: React.FC<TestBroadcastDrawerProps> = ({\n buttonLabel = 'Test email',\n}) => {\n const [email, setEmail] = useState('')\n const [isLoading, setIsLoading] = useState(false)\n const [error, setError] = useState<null | string>(null)\n const { id, initialData } = useDocumentInfo()\n const { toggleModal } = useModal()\n const modified = useFormModified()\n\n const isDraft = initialData?.status === 'draft'\n\n const drawerSlug = 'test-broadcast-drawer'\n\n const handleSendTest = useCallback(async () => {\n setError(null)\n\n if (!email) {\n setError('Please enter an email address')\n return\n }\n\n if (!isValidEmail(email)) {\n setError('Please enter a valid email address')\n return\n }\n\n setIsLoading(true)\n\n try {\n const response = await fetch(apiEndpoint, {\n body: JSON.stringify({\n broadcastId: id,\n testEmail: email,\n }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n const data = await response.json()\n\n if (response.ok) {\n setEmail('') // Clear the input\n toast.success('Test broadcast sent successfully')\n toggleModal(drawerSlug)\n } else {\n toast.error('Failed to send test broadcast')\n setError(data.message || 'Failed to send test broadcast')\n }\n } catch {\n toast.error('Failed to send test broadcast')\n setError('An error occurred while sending the test broadcast')\n } finally {\n setIsLoading(false)\n }\n }, [email, id, toggleModal])\n\n if (!id) {\n return null\n }\n\n return (\n <>\n <Button disabled={!isDraft || modified} onClick={() => toggleModal(drawerSlug)} size=\"medium\">\n {buttonLabel}\n </Button>\n\n <Drawer slug={drawerSlug} title=\"Send Test\">\n <form className=\"form\" noValidate onSubmit={handleSendTest}>\n <div className=\"render-fields\">\n {error && (\n <div\n className=\"field-type\"\n style={{\n backgroundColor: 'var(--theme-error-50)',\n border: '1px solid var(--theme-error-500)',\n borderRadius: '4px',\n color: 'var(--theme-error-900)',\n marginBottom: 'var(--base-unit)',\n padding: '0.75rem',\n }}\n >\n {error}\n </div>\n )}\n\n <div className=\"field-type text\">\n <label className=\"field-label\" htmlFor=\"field-test-email\">\n Email Address\n <span className=\"required\">*</span>\n </label>\n <div className=\"field-type__wrap\">\n <input\n aria-label=\"Email address\"\n data-rtl=\"false\"\n disabled={isLoading}\n id=\"field-test-email\"\n name=\"testEmail\"\n onChange={(e) => {\n setEmail(e.target.value)\n setError(null)\n }}\n placeholder=\"name@example.com\"\n type=\"email\"\n value={email}\n />\n </div>\n <div className=\"field-description field-description-testEmail\">\n A test version of this broadcast will be sent to the email address above. The test\n email will be clearly marked as a test.\n </div>\n </div>\n </div>\n\n <div className=\"form-submit\">\n <button\n aria-label=\"Send test broadcast\"\n className=\"btn btn--icon-style-without-border btn--size-medium btn--style-primary\"\n disabled={isLoading || !email}\n type=\"submit\"\n >\n <span className=\"btn__content\">\n <span className=\"btn__label\">{isLoading ? 'Sending...' : 'Send Test'}</span>\n </span>\n </button>\n </div>\n </form>\n </Drawer>\n </>\n )\n}\n"],"names":["Button","Drawer","toast","useDocumentInfo","useFormModified","useModal","React","useCallback","useState","isValidEmail","apiEndpoint","SendTestBroadcastDrawer","buttonLabel","email","setEmail","isLoading","setIsLoading","error","setError","id","initialData","toggleModal","modified","isDraft","status","drawerSlug","handleSendTest","response","fetch","body","JSON","stringify","broadcastId","testEmail","headers","method","data","json","ok","success","message","disabled","onClick","size","slug","title","form","className","noValidate","onSubmit","div","style","backgroundColor","border","borderRadius","color","marginBottom","padding","label","htmlFor","span","input","aria-label","data-rtl","name","onChange","e","target","value","placeholder","type","button"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ export declare const EmailActivityField: ({ data }: {
3
+ data: Record<string, unknown>;
4
+ }) => React.JSX.Element;