@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.
- package/dist/access/authenticated.d.ts +4 -0
- package/dist/access/authenticated.js +5 -0
- package/dist/access/authenticated.js.map +1 -0
- package/dist/access/authenticated.spec.d.ts +1 -0
- package/dist/access/authenticated.spec.js +33 -0
- package/dist/access/authenticated.spec.js.map +1 -0
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/resend-adapter.d.ts +34 -0
- package/dist/adapters/resend-adapter.js +219 -0
- package/dist/adapters/resend-adapter.js.map +1 -0
- package/dist/collections/broadcasts/generateBroadcastsCollection.d.ts +3 -0
- package/dist/collections/broadcasts/generateBroadcastsCollection.js +241 -0
- package/dist/collections/broadcasts/generateBroadcastsCollection.js.map +1 -0
- package/dist/collections/contacts/generateContactsCollection.d.ts +22 -0
- package/dist/collections/contacts/generateContactsCollection.js +124 -0
- package/dist/collections/contacts/generateContactsCollection.js.map +1 -0
- package/dist/collections/emails/generateEmailsCollection.d.ts +3 -0
- package/dist/collections/emails/generateEmailsCollection.js +204 -0
- package/dist/collections/emails/generateEmailsCollection.js.map +1 -0
- package/dist/collections/emails/hooks/sync-status-from-activity.d.ts +5 -0
- package/dist/collections/emails/hooks/sync-status-from-activity.js +64 -0
- package/dist/collections/emails/hooks/sync-status-from-activity.js.map +1 -0
- package/dist/collections/tags/generateTagsCollection.d.ts +3 -0
- package/dist/collections/tags/generateTagsCollection.js +29 -0
- package/dist/collections/tags/generateTagsCollection.js.map +1 -0
- package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.d.ts +2 -0
- package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.js +48 -0
- package/dist/collections/unsubscribe-tokens/generateUnsubscribeTokens.js.map +1 -0
- package/dist/components/broadcast-metrics-card.d.ts +7 -0
- package/dist/components/broadcast-metrics-card.js +159 -0
- package/dist/components/broadcast-metrics-card.js.map +1 -0
- package/dist/components/broadcast-send-modal.d.ts +9 -0
- package/dist/components/broadcast-send-modal.js +51 -0
- package/dist/components/broadcast-send-modal.js.map +1 -0
- package/dist/components/broadcast-send-test-drawer.d.ts +7 -0
- package/dist/components/broadcast-send-test-drawer.js +154 -0
- package/dist/components/broadcast-send-test-drawer.js.map +1 -0
- package/dist/components/email-activity.d.ts +4 -0
- package/dist/components/email-activity.js +359 -0
- package/dist/components/email-activity.js.map +1 -0
- package/dist/components/email-preview.d.ts +2 -0
- package/dist/components/email-preview.js +95 -0
- package/dist/components/email-preview.js.map +1 -0
- package/dist/endpoints/sendBroadcastHandler.d.ts +9 -0
- package/dist/endpoints/sendBroadcastHandler.js +107 -0
- package/dist/endpoints/sendBroadcastHandler.js.map +1 -0
- package/dist/endpoints/sendTestBroadcastHandler.d.ts +10 -0
- package/dist/endpoints/sendTestBroadcastHandler.js +143 -0
- package/dist/endpoints/sendTestBroadcastHandler.js.map +1 -0
- package/dist/endpoints/unsubscribeHandler.d.ts +9 -0
- package/dist/endpoints/unsubscribeHandler.js +153 -0
- package/dist/endpoints/unsubscribeHandler.js.map +1 -0
- package/dist/exports/client.d.ts +3 -1
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/rsc.d.ts +2 -1
- package/dist/exports/rsc.js +2 -0
- package/dist/exports/rsc.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +51 -2
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/unsubscribe.d.ts +6 -0
- package/dist/react/unsubscribe.js +16 -0
- package/dist/react/unsubscribe.js.map +1 -0
- package/dist/tasks/sendBroadcastsTask.d.ts +11 -0
- package/dist/tasks/sendBroadcastsTask.js +196 -0
- package/dist/tasks/sendBroadcastsTask.js.map +1 -0
- package/dist/tasks/sendEmailTask.d.ts +9 -0
- package/dist/tasks/sendEmailTask.js +167 -0
- package/dist/tasks/sendEmailTask.js.map +1 -0
- package/dist/types/index.d.ts +135 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/api-response.d.ts +72 -0
- package/dist/utils/api-response.js +66 -0
- package/dist/utils/api-response.js.map +1 -0
- package/dist/utils/email.d.ts +36 -0
- package/dist/utils/email.js +40 -0
- package/dist/utils/email.js.map +1 -0
- package/dist/utils/lexical.d.ts +13 -0
- package/dist/utils/lexical.js +27 -0
- package/dist/utils/lexical.js.map +1 -0
- package/dist/utils/unsubscribe-token.d.ts +67 -0
- package/dist/utils/unsubscribe-token.js +103 -0
- package/dist/utils/unsubscribe-token.js.map +1 -0
- 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,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,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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"}
|