@mobilizehub/payload-plugin 0.1.0 → 0.3.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/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/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/pages/generatePagesCollection.d.ts +3 -0
- package/dist/collections/pages/generatePagesCollection.js +77 -0
- package/dist/collections/pages/generatePagesCollection.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/fields/name.d.ts +5 -0
- package/dist/fields/name.js +12 -0
- package/dist/fields/name.js.map +1 -0
- package/dist/fields/publishedAt.d.ts +5 -0
- package/dist/fields/publishedAt.js +16 -0
- package/dist/fields/publishedAt.js.map +1 -0
- package/dist/fields/slug.d.ts +7 -0
- package/dist/fields/slug.js +47 -0
- package/dist/fields/slug.js.map +1 -0
- package/dist/fields/status.d.ts +5 -0
- package/dist/fields/status.js +25 -0
- package/dist/fields/status.js.map +1 -0
- package/dist/index.js +48 -3
- 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 +133 -1
- package/dist/types/index.js.map +1 -1
- 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 +20 -9
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { authenticated } from '../../access/authenticated.js';
|
|
2
|
+
import { syncStatusFromActivityBeforeChangeHook } from './hooks/sync-status-from-activity.js';
|
|
3
|
+
export const generateEmailsCollection = (emailsConfig)=>{
|
|
4
|
+
const defaultFields = [
|
|
5
|
+
{
|
|
6
|
+
name: 'providerId',
|
|
7
|
+
type: 'text',
|
|
8
|
+
admin: {
|
|
9
|
+
position: 'sidebar',
|
|
10
|
+
readOnly: true
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: 'status',
|
|
15
|
+
type: 'select',
|
|
16
|
+
admin: {
|
|
17
|
+
position: 'sidebar',
|
|
18
|
+
readOnly: true
|
|
19
|
+
},
|
|
20
|
+
defaultValue: 'draft',
|
|
21
|
+
options: [
|
|
22
|
+
{
|
|
23
|
+
label: 'Queued',
|
|
24
|
+
value: 'queued'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: 'Failed',
|
|
28
|
+
value: 'failed'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: 'Sent',
|
|
32
|
+
value: 'sent'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: 'Delivered',
|
|
36
|
+
value: 'delivered'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'Bounced',
|
|
40
|
+
value: 'bounced'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: 'Unsubscribed',
|
|
44
|
+
value: 'unsubscribed'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: 'Complained',
|
|
48
|
+
value: 'complained'
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
required: true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'broadcast',
|
|
55
|
+
type: 'relationship',
|
|
56
|
+
admin: {
|
|
57
|
+
position: 'sidebar',
|
|
58
|
+
readOnly: true
|
|
59
|
+
},
|
|
60
|
+
relationTo: emailsConfig.broadcastsOverrides?.slug || 'broadcasts'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'contact',
|
|
64
|
+
type: 'relationship',
|
|
65
|
+
admin: {
|
|
66
|
+
position: 'sidebar',
|
|
67
|
+
readOnly: true
|
|
68
|
+
},
|
|
69
|
+
relationTo: emailsConfig.contactsOverrides?.slug || 'contacts'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'activityField',
|
|
73
|
+
type: 'ui',
|
|
74
|
+
admin: {
|
|
75
|
+
components: {
|
|
76
|
+
Field: '@mobilizehub/payload-plugin/rsc#EmailActivityField'
|
|
77
|
+
},
|
|
78
|
+
position: 'sidebar'
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'from',
|
|
83
|
+
type: 'text',
|
|
84
|
+
admin: {
|
|
85
|
+
readOnly: true
|
|
86
|
+
},
|
|
87
|
+
required: true
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'activity',
|
|
91
|
+
type: 'array',
|
|
92
|
+
admin: {
|
|
93
|
+
hidden: true,
|
|
94
|
+
readOnly: true
|
|
95
|
+
},
|
|
96
|
+
fields: [
|
|
97
|
+
{
|
|
98
|
+
name: 'type',
|
|
99
|
+
type: 'select',
|
|
100
|
+
options: [
|
|
101
|
+
{
|
|
102
|
+
label: 'Sent',
|
|
103
|
+
value: 'sent'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: 'Delivered',
|
|
107
|
+
value: 'delivered'
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
label: 'Opened',
|
|
111
|
+
value: 'opened'
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
label: 'Clicked',
|
|
115
|
+
value: 'clicked'
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
label: 'Bounced',
|
|
119
|
+
value: 'bounced'
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
label: 'Unsubscribed',
|
|
123
|
+
value: 'unsubscribed'
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
label: 'Complained',
|
|
127
|
+
value: 'complained'
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
required: true
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'timestamp',
|
|
134
|
+
type: 'date',
|
|
135
|
+
required: true
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
label: ''
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'subject',
|
|
142
|
+
type: 'text',
|
|
143
|
+
admin: {
|
|
144
|
+
readOnly: true
|
|
145
|
+
},
|
|
146
|
+
required: true
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'to',
|
|
150
|
+
type: 'text',
|
|
151
|
+
admin: {
|
|
152
|
+
readOnly: true
|
|
153
|
+
},
|
|
154
|
+
required: true
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
name: 'html',
|
|
158
|
+
type: 'text',
|
|
159
|
+
admin: {
|
|
160
|
+
hidden: true,
|
|
161
|
+
readOnly: true
|
|
162
|
+
},
|
|
163
|
+
required: true
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'emailPreview',
|
|
167
|
+
type: 'ui',
|
|
168
|
+
admin: {
|
|
169
|
+
components: {
|
|
170
|
+
Field: '@mobilizehub/payload-plugin/client#EmailPreviewField'
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
];
|
|
175
|
+
const config = {
|
|
176
|
+
...emailsConfig.emailsOverrides || {},
|
|
177
|
+
slug: emailsConfig.emailsOverrides?.slug || 'emails',
|
|
178
|
+
access: {
|
|
179
|
+
create: ()=>false,
|
|
180
|
+
delete: ()=>false,
|
|
181
|
+
read: authenticated,
|
|
182
|
+
update: ()=>false,
|
|
183
|
+
...emailsConfig.emailsOverrides?.access || {}
|
|
184
|
+
},
|
|
185
|
+
admin: {
|
|
186
|
+
...emailsConfig.emailsOverrides?.admin || {},
|
|
187
|
+
hidden: emailsConfig.emailsOverrides?.admin?.hidden || false,
|
|
188
|
+
useAsTitle: emailsConfig.emailsOverrides?.admin?.useAsTitle || 'to'
|
|
189
|
+
},
|
|
190
|
+
fields: emailsConfig.emailsOverrides?.fields ? emailsConfig.emailsOverrides.fields({
|
|
191
|
+
defaultFields
|
|
192
|
+
}) : defaultFields,
|
|
193
|
+
hooks: {
|
|
194
|
+
beforeChange: [
|
|
195
|
+
syncStatusFromActivityBeforeChangeHook,
|
|
196
|
+
...emailsConfig.emailsOverrides?.hooks?.beforeChange || []
|
|
197
|
+
],
|
|
198
|
+
...emailsConfig.emailsOverrides?.hooks || {}
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
return config;
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
//# sourceMappingURL=generateEmailsCollection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/emails/generateEmailsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\nimport { syncStatusFromActivityBeforeChangeHook } from './hooks/sync-status-from-activity.js'\n\nexport const generateEmailsCollection = (emailsConfig: MobilizehubPluginConfig) => {\n const defaultFields: Field[] = [\n {\n name: 'providerId',\n type: 'text',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n },\n {\n name: 'status',\n type: 'select',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n defaultValue: 'draft',\n options: [\n {\n label: 'Queued',\n value: 'queued',\n },\n {\n label: 'Failed',\n value: 'failed',\n },\n {\n label: 'Sent',\n value: 'sent',\n },\n {\n label: 'Delivered',\n value: 'delivered',\n },\n {\n label: 'Bounced',\n value: 'bounced',\n },\n {\n label: 'Unsubscribed',\n value: 'unsubscribed',\n },\n {\n label: 'Complained',\n value: 'complained',\n },\n ],\n required: true,\n },\n {\n name: 'broadcast',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.broadcastsOverrides?.slug || 'broadcasts',\n },\n {\n name: 'contact',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: emailsConfig.contactsOverrides?.slug || 'contacts',\n },\n\n {\n name: 'activityField',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/rsc#EmailActivityField',\n },\n position: 'sidebar',\n },\n },\n {\n name: 'from',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'activity',\n type: 'array',\n admin: {\n hidden: true,\n readOnly: true,\n },\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n { label: 'Sent', value: 'sent' },\n { label: 'Delivered', value: 'delivered' },\n { label: 'Opened', value: 'opened' },\n { label: 'Clicked', value: 'clicked' },\n { label: 'Bounced', value: 'bounced' },\n { label: 'Unsubscribed', value: 'unsubscribed' },\n { label: 'Complained', value: 'complained' },\n ],\n required: true,\n },\n {\n name: 'timestamp',\n type: 'date',\n required: true,\n },\n ],\n label: '',\n },\n {\n name: 'subject',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'to',\n type: 'text',\n admin: {\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'html',\n type: 'text',\n admin: {\n hidden: true,\n readOnly: true,\n },\n required: true,\n },\n {\n name: 'emailPreview',\n type: 'ui',\n admin: {\n components: {\n Field: '@mobilizehub/payload-plugin/client#EmailPreviewField',\n },\n },\n },\n ]\n\n const config: CollectionConfig = {\n ...(emailsConfig.emailsOverrides || {}),\n slug: emailsConfig.emailsOverrides?.slug || 'emails',\n access: {\n create: () => false,\n delete: () => false,\n read: authenticated,\n update: () => false,\n ...(emailsConfig.emailsOverrides?.access || {}),\n },\n admin: {\n ...(emailsConfig.emailsOverrides?.admin || {}),\n hidden: emailsConfig.emailsOverrides?.admin?.hidden || false,\n useAsTitle: emailsConfig.emailsOverrides?.admin?.useAsTitle || 'to',\n },\n fields: emailsConfig.emailsOverrides?.fields\n ? emailsConfig.emailsOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n beforeChange: [\n syncStatusFromActivityBeforeChangeHook,\n ...(emailsConfig.emailsOverrides?.hooks?.beforeChange || []),\n ],\n ...(emailsConfig.emailsOverrides?.hooks || {}),\n },\n }\n\n return config\n}\n"],"names":["authenticated","syncStatusFromActivityBeforeChangeHook","generateEmailsCollection","emailsConfig","defaultFields","name","type","admin","position","readOnly","defaultValue","options","label","value","required","relationTo","broadcastsOverrides","slug","contactsOverrides","components","Field","hidden","fields","config","emailsOverrides","access","create","delete","read","update","useAsTitle","hooks","beforeChange"],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps activity types to their corresponding email status.
|
|
3
|
+
* Some activity types (like 'opened', 'clicked') don't change the status.
|
|
4
|
+
*/ const activityTypeToStatus = {
|
|
5
|
+
bounced: 'bounced',
|
|
6
|
+
complained: 'complained',
|
|
7
|
+
delivered: 'delivered',
|
|
8
|
+
sent: 'sent',
|
|
9
|
+
unsubscribed: 'unsubscribed'
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Defines the priority of statuses for determining the "most significant" status.
|
|
13
|
+
* Higher index = higher priority (terminal/negative states take precedence).
|
|
14
|
+
*/ const statusPriority = [
|
|
15
|
+
'queued',
|
|
16
|
+
'sent',
|
|
17
|
+
'delivered',
|
|
18
|
+
'bounced',
|
|
19
|
+
'complained',
|
|
20
|
+
'unsubscribed'
|
|
21
|
+
];
|
|
22
|
+
function getStatusPriority(status) {
|
|
23
|
+
const index = statusPriority.indexOf(status);
|
|
24
|
+
return index === -1 ? 0 : index;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Determines the email status based on activity history.
|
|
28
|
+
* Uses the activity with the highest priority status.
|
|
29
|
+
*/ function getStatusFromActivity(activity) {
|
|
30
|
+
if (!activity || activity.length === 0) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
let highestPriorityStatus = null;
|
|
34
|
+
let highestPriority = -1;
|
|
35
|
+
for (const item of activity){
|
|
36
|
+
const status = activityTypeToStatus[item.type];
|
|
37
|
+
if (status) {
|
|
38
|
+
const priority = getStatusPriority(status);
|
|
39
|
+
if (priority > highestPriority) {
|
|
40
|
+
highestPriority = priority;
|
|
41
|
+
highestPriorityStatus = status;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return highestPriorityStatus;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A hook to synchronize the email status field based on activity history before saving.
|
|
49
|
+
*/ export const syncStatusFromActivityBeforeChangeHook = ({ data })=>{
|
|
50
|
+
const activity = data.activity;
|
|
51
|
+
if (!activity || activity.length === 0) {
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
const derivedStatus = getStatusFromActivity(activity);
|
|
55
|
+
if (derivedStatus && derivedStatus !== data.status) {
|
|
56
|
+
return {
|
|
57
|
+
...data,
|
|
58
|
+
status: derivedStatus
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return data;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=sync-status-from-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/collections/emails/hooks/sync-status-from-activity.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport type { EmailActivityType, EmailStatus } from '../../../types/index.js'\n\ntype ActivityItem = {\n timestamp: string\n type: 'unsubscribed' | EmailActivityType\n}\n\n/**\n * Maps activity types to their corresponding email status.\n * Some activity types (like 'opened', 'clicked') don't change the status.\n */\nconst activityTypeToStatus: Partial<Record<'unsubscribed' | EmailActivityType, EmailStatus>> = {\n bounced: 'bounced',\n complained: 'complained',\n delivered: 'delivered',\n sent: 'sent',\n unsubscribed: 'unsubscribed',\n}\n\n/**\n * Defines the priority of statuses for determining the \"most significant\" status.\n * Higher index = higher priority (terminal/negative states take precedence).\n */\nconst statusPriority: EmailStatus[] = [\n 'queued',\n 'sent',\n 'delivered',\n 'bounced',\n 'complained',\n 'unsubscribed',\n]\n\nfunction getStatusPriority(status: EmailStatus): number {\n const index = statusPriority.indexOf(status)\n return index === -1 ? 0 : index\n}\n\n/**\n * Determines the email status based on activity history.\n * Uses the activity with the highest priority status.\n */\nfunction getStatusFromActivity(activity: ActivityItem[]): EmailStatus | null {\n if (!activity || activity.length === 0) {\n return null\n }\n\n let highestPriorityStatus: EmailStatus | null = null\n let highestPriority = -1\n\n for (const item of activity) {\n const status = activityTypeToStatus[item.type]\n if (status) {\n const priority = getStatusPriority(status)\n if (priority > highestPriority) {\n highestPriority = priority\n highestPriorityStatus = status\n }\n }\n }\n\n return highestPriorityStatus\n}\n\n/**\n * A hook to synchronize the email status field based on activity history before saving.\n */\nexport const syncStatusFromActivityBeforeChangeHook: CollectionBeforeChangeHook = ({ data }) => {\n const activity = data.activity as ActivityItem[] | undefined\n\n if (!activity || activity.length === 0) {\n return data\n }\n\n const derivedStatus = getStatusFromActivity(activity)\n\n if (derivedStatus && derivedStatus !== data.status) {\n return {\n ...data,\n status: derivedStatus,\n }\n }\n\n return data\n}\n"],"names":["activityTypeToStatus","bounced","complained","delivered","sent","unsubscribed","statusPriority","getStatusPriority","status","index","indexOf","getStatusFromActivity","activity","length","highestPriorityStatus","highestPriority","item","type","priority","syncStatusFromActivityBeforeChangeHook","data","derivedStatus"],"mappings":"AASA;;;CAGC,GACD,MAAMA,uBAAyF;IAC7FC,SAAS;IACTC,YAAY;IACZC,WAAW;IACXC,MAAM;IACNC,cAAc;AAChB;AAEA;;;CAGC,GACD,MAAMC,iBAAgC;IACpC;IACA;IACA;IACA;IACA;IACA;CACD;AAED,SAASC,kBAAkBC,MAAmB;IAC5C,MAAMC,QAAQH,eAAeI,OAAO,CAACF;IACrC,OAAOC,UAAU,CAAC,IAAI,IAAIA;AAC5B;AAEA;;;CAGC,GACD,SAASE,sBAAsBC,QAAwB;IACrD,IAAI,CAACA,YAAYA,SAASC,MAAM,KAAK,GAAG;QACtC,OAAO;IACT;IAEA,IAAIC,wBAA4C;IAChD,IAAIC,kBAAkB,CAAC;IAEvB,KAAK,MAAMC,QAAQJ,SAAU;QAC3B,MAAMJ,SAASR,oBAAoB,CAACgB,KAAKC,IAAI,CAAC;QAC9C,IAAIT,QAAQ;YACV,MAAMU,WAAWX,kBAAkBC;YACnC,IAAIU,WAAWH,iBAAiB;gBAC9BA,kBAAkBG;gBAClBJ,wBAAwBN;YAC1B;QACF;IACF;IAEA,OAAOM;AACT;AAEA;;CAEC,GACD,OAAO,MAAMK,yCAAqE,CAAC,EAAEC,IAAI,EAAE;IACzF,MAAMR,WAAWQ,KAAKR,QAAQ;IAE9B,IAAI,CAACA,YAAYA,SAASC,MAAM,KAAK,GAAG;QACtC,OAAOO;IACT;IAEA,MAAMC,gBAAgBV,sBAAsBC;IAE5C,IAAIS,iBAAiBA,kBAAkBD,KAAKZ,MAAM,EAAE;QAClD,OAAO;YACL,GAAGY,IAAI;YACPZ,QAAQa;QACV;IACF;IAEA,OAAOD;AACT,EAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { createNameField } from '../../fields/name.js';
|
|
2
|
+
import { createPublishedAtField } from '../../fields/publishedAt.js';
|
|
3
|
+
import { createSlugField } from '../../fields/slug.js';
|
|
4
|
+
import { createStatusField } from '../../fields/status.js';
|
|
5
|
+
export const generatePagesCollection = (pagesConfig)=>{
|
|
6
|
+
const defaultBlocks = [
|
|
7
|
+
{
|
|
8
|
+
slug: 'content',
|
|
9
|
+
fields: [
|
|
10
|
+
{
|
|
11
|
+
name: 'richText',
|
|
12
|
+
type: 'richText',
|
|
13
|
+
label: false
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
interfaceName: 'ContentBlock'
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
const blocks = pagesConfig.pagesOverrides?.blocks ? pagesConfig.pagesOverrides.blocks({
|
|
20
|
+
defaultBlocks
|
|
21
|
+
}) : defaultBlocks;
|
|
22
|
+
const defaultFields = [
|
|
23
|
+
createStatusField(),
|
|
24
|
+
{
|
|
25
|
+
type: 'tabs',
|
|
26
|
+
tabs: [
|
|
27
|
+
{
|
|
28
|
+
fields: [
|
|
29
|
+
createNameField(),
|
|
30
|
+
createSlugField(),
|
|
31
|
+
createPublishedAtField()
|
|
32
|
+
],
|
|
33
|
+
label: 'Settings'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
fields: [
|
|
37
|
+
{
|
|
38
|
+
name: 'blocks',
|
|
39
|
+
type: 'blocks',
|
|
40
|
+
blocks,
|
|
41
|
+
label: 'Blocks'
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
label: 'Content'
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
const config = {
|
|
50
|
+
slug: pagesConfig.pagesOverrides?.slug || 'pages',
|
|
51
|
+
access: {
|
|
52
|
+
read: ()=>true,
|
|
53
|
+
...pagesConfig.pagesOverrides?.access || {}
|
|
54
|
+
},
|
|
55
|
+
admin: {
|
|
56
|
+
...pagesConfig.pagesOverrides?.admin || {},
|
|
57
|
+
defaultColumns: pagesConfig.pagesOverrides?.admin?.defaultColumns || [
|
|
58
|
+
'id',
|
|
59
|
+
'name',
|
|
60
|
+
'slug',
|
|
61
|
+
'status'
|
|
62
|
+
],
|
|
63
|
+
hidden: pagesConfig.pagesOverrides?.admin?.hidden || false,
|
|
64
|
+
useAsTitle: pagesConfig.pagesOverrides?.admin?.useAsTitle || 'name'
|
|
65
|
+
},
|
|
66
|
+
fields: pagesConfig.pagesOverrides?.fields ? pagesConfig.pagesOverrides.fields({
|
|
67
|
+
defaultFields
|
|
68
|
+
}) : defaultFields,
|
|
69
|
+
hooks: {
|
|
70
|
+
...pagesConfig.pagesOverrides?.hooks || {}
|
|
71
|
+
},
|
|
72
|
+
...pagesConfig.pagesOverrides = {}
|
|
73
|
+
};
|
|
74
|
+
return config;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=generatePagesCollection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/pages/generatePagesCollection.ts"],"sourcesContent":["import type { Block, CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { createNameField } from '../../fields/name.js'\nimport { createPublishedAtField } from '../../fields/publishedAt.js'\nimport { createSlugField } from '../../fields/slug.js'\nimport { createStatusField } from '../../fields/status.js'\n\nexport const generatePagesCollection = (pagesConfig: MobilizehubPluginConfig) => {\n const defaultBlocks: Block[] = [\n {\n slug: 'content',\n fields: [\n {\n name: 'richText',\n type: 'richText',\n label: false,\n },\n ],\n interfaceName: 'ContentBlock',\n },\n ]\n\n const blocks = pagesConfig.pagesOverrides?.blocks\n ? pagesConfig.pagesOverrides.blocks({ defaultBlocks })\n : defaultBlocks\n\n const defaultFields: Field[] = [\n createStatusField(),\n {\n type: 'tabs',\n tabs: [\n {\n fields: [createNameField(), createSlugField(), createPublishedAtField()],\n label: 'Settings',\n },\n {\n fields: [\n {\n name: 'blocks',\n type: 'blocks',\n blocks,\n label: 'Blocks',\n },\n ],\n label: 'Content',\n },\n ],\n },\n ]\n\n const config: CollectionConfig = {\n slug: pagesConfig.pagesOverrides?.slug || 'pages',\n access: {\n read: () => true,\n ...(pagesConfig.pagesOverrides?.access || {}),\n },\n admin: {\n ...(pagesConfig.pagesOverrides?.admin || {}),\n defaultColumns: pagesConfig.pagesOverrides?.admin?.defaultColumns || [\n 'id',\n 'name',\n 'slug',\n 'status',\n ],\n hidden: pagesConfig.pagesOverrides?.admin?.hidden || false,\n useAsTitle: pagesConfig.pagesOverrides?.admin?.useAsTitle || 'name',\n },\n fields: pagesConfig.pagesOverrides?.fields\n ? pagesConfig.pagesOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n ...(pagesConfig.pagesOverrides?.hooks || {}),\n },\n ...(pagesConfig.pagesOverrides = {}),\n }\n\n return config\n}\n"],"names":["createNameField","createPublishedAtField","createSlugField","createStatusField","generatePagesCollection","pagesConfig","defaultBlocks","slug","fields","name","type","label","interfaceName","blocks","pagesOverrides","defaultFields","tabs","config","access","read","admin","defaultColumns","hidden","useAsTitle","hooks"],"mappings":"AAIA,SAASA,eAAe,QAAQ,uBAAsB;AACtD,SAASC,sBAAsB,QAAQ,8BAA6B;AACpE,SAASC,eAAe,QAAQ,uBAAsB;AACtD,SAASC,iBAAiB,QAAQ,yBAAwB;AAE1D,OAAO,MAAMC,0BAA0B,CAACC;IACtC,MAAMC,gBAAyB;QAC7B;YACEC,MAAM;YACNC,QAAQ;gBACN;oBACEC,MAAM;oBACNC,MAAM;oBACNC,OAAO;gBACT;aACD;YACDC,eAAe;QACjB;KACD;IAED,MAAMC,SAASR,YAAYS,cAAc,EAAED,SACvCR,YAAYS,cAAc,CAACD,MAAM,CAAC;QAAEP;IAAc,KAClDA;IAEJ,MAAMS,gBAAyB;QAC7BZ;QACA;YACEO,MAAM;YACNM,MAAM;gBACJ;oBACER,QAAQ;wBAACR;wBAAmBE;wBAAmBD;qBAAyB;oBACxEU,OAAO;gBACT;gBACA;oBACEH,QAAQ;wBACN;4BACEC,MAAM;4BACNC,MAAM;4BACNG;4BACAF,OAAO;wBACT;qBACD;oBACDA,OAAO;gBACT;aACD;QACH;KACD;IAED,MAAMM,SAA2B;QAC/BV,MAAMF,YAAYS,cAAc,EAAEP,QAAQ;QAC1CW,QAAQ;YACNC,MAAM,IAAM;YACZ,GAAId,YAAYS,cAAc,EAAEI,UAAU,CAAC,CAAC;QAC9C;QACAE,OAAO;YACL,GAAIf,YAAYS,cAAc,EAAEM,SAAS,CAAC,CAAC;YAC3CC,gBAAgBhB,YAAYS,cAAc,EAAEM,OAAOC,kBAAkB;gBACnE;gBACA;gBACA;gBACA;aACD;YACDC,QAAQjB,YAAYS,cAAc,EAAEM,OAAOE,UAAU;YACrDC,YAAYlB,YAAYS,cAAc,EAAEM,OAAOG,cAAc;QAC/D;QACAf,QAAQH,YAAYS,cAAc,EAAEN,SAChCH,YAAYS,cAAc,CAACN,MAAM,CAAC;YAAEO;QAAc,KAClDA;QACJS,OAAO;YACL,GAAInB,YAAYS,cAAc,EAAEU,SAAS,CAAC,CAAC;QAC7C;QACA,GAAInB,YAAYS,cAAc,GAAG,CAAC,CAAC;IACrC;IAEA,OAAOG;AACT,EAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const generateUnsubscribeTokensCollection = ()=>{
|
|
2
|
+
const defaultFields = [
|
|
3
|
+
{
|
|
4
|
+
name: 'id',
|
|
5
|
+
type: 'text',
|
|
6
|
+
required: true,
|
|
7
|
+
unique: true
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: 'emailId',
|
|
11
|
+
type: 'relationship',
|
|
12
|
+
relationTo: 'emails'
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: 'expiresAt',
|
|
16
|
+
type: 'date'
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
const config = {
|
|
20
|
+
slug: 'emailUnsubscribeTokens',
|
|
21
|
+
access: {
|
|
22
|
+
create: ()=>false,
|
|
23
|
+
delete: ()=>false,
|
|
24
|
+
read: ()=>false,
|
|
25
|
+
update: ()=>false
|
|
26
|
+
},
|
|
27
|
+
admin: {
|
|
28
|
+
hidden: true
|
|
29
|
+
},
|
|
30
|
+
fields: defaultFields,
|
|
31
|
+
hooks: {
|
|
32
|
+
beforeChange: [
|
|
33
|
+
({ data, operation })=>{
|
|
34
|
+
if (operation === 'create') {
|
|
35
|
+
if (!data.expiresAt) {
|
|
36
|
+
const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
|
|
37
|
+
data.expiresAt = expiresAt.toISOString();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return config;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=generateUnsubscribeTokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/unsubscribe-tokens/generateUnsubscribeTokens.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nexport const generateUnsubscribeTokensCollection = () => {\n const defaultFields: Field[] = [\n {\n name: 'id',\n type: 'text',\n required: true,\n unique: true,\n },\n {\n name: 'emailId',\n type: 'relationship',\n relationTo: 'emails',\n },\n {\n name: 'expiresAt',\n type: 'date',\n },\n ]\n\n const config: CollectionConfig = {\n slug: 'emailUnsubscribeTokens',\n access: {\n create: () => false,\n delete: () => false,\n read: () => false,\n update: () => false,\n },\n admin: {\n hidden: true,\n },\n fields: defaultFields,\n hooks: {\n beforeChange: [\n ({ data, operation }) => {\n if (operation === 'create') {\n if (!data.expiresAt) {\n const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)\n data.expiresAt = expiresAt.toISOString()\n }\n }\n return data\n },\n ],\n },\n }\n\n return config\n}\n"],"names":["generateUnsubscribeTokensCollection","defaultFields","name","type","required","unique","relationTo","config","slug","access","create","delete","read","update","admin","hidden","fields","hooks","beforeChange","data","operation","expiresAt","Date","now","toISOString"],"mappings":"AAEA,OAAO,MAAMA,sCAAsC;IACjD,MAAMC,gBAAyB;QAC7B;YACEC,MAAM;YACNC,MAAM;YACNC,UAAU;YACVC,QAAQ;QACV;QACA;YACEH,MAAM;YACNC,MAAM;YACNG,YAAY;QACd;QACA;YACEJ,MAAM;YACNC,MAAM;QACR;KACD;IAED,MAAMI,SAA2B;QAC/BC,MAAM;QACNC,QAAQ;YACNC,QAAQ,IAAM;YACdC,QAAQ,IAAM;YACdC,MAAM,IAAM;YACZC,QAAQ,IAAM;QAChB;QACAC,OAAO;YACLC,QAAQ;QACV;QACAC,QAAQf;QACRgB,OAAO;YACLC,cAAc;gBACZ,CAAC,EAAEC,IAAI,EAAEC,SAAS,EAAE;oBAClB,IAAIA,cAAc,UAAU;wBAC1B,IAAI,CAACD,KAAKE,SAAS,EAAE;4BACnB,MAAMA,YAAY,IAAIC,KAAKA,KAAKC,GAAG,KAAK,KAAK,KAAK,KAAK,KAAK;4BAC5DJ,KAAKE,SAAS,GAAGA,UAAUG,WAAW;wBACxC;oBACF;oBACA,OAAOL;gBACT;aACD;QACH;IACF;IAEA,OAAOZ;AACT,EAAC"}
|
|
@@ -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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"}
|