@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,124 @@
1
+ import iso from 'i18n-iso-countries';
2
+ import { authenticated } from '../../access/authenticated.js';
3
+ /**
4
+ * Generates country options for the country select field.
5
+ */ const countryOptions = Object.entries(iso.getNames('en', {
6
+ select: 'official'
7
+ })).map(([code, name])=>({
8
+ label: name,
9
+ value: code
10
+ }));
11
+ /**
12
+ * Generates the contacts collection configuration.
13
+ * @param contactsConfig
14
+ * @returns CollectionConfig
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { generateContactsCollection } from 'path/to/generateContactsCollection';
19
+ *
20
+ * const contactsCollection = generateContactsCollection({
21
+ * contactsOverrides: {
22
+ * slug: 'my-contacts',
23
+ * admin: {
24
+ * useAsTitle: 'firstName',
25
+ * },
26
+ * },
27
+ * });
28
+ * ```
29
+ */ export const generateContactsCollection = (contactsConfig)=>{
30
+ const defaultFields = [
31
+ {
32
+ name: 'tags',
33
+ type: 'relationship',
34
+ admin: {
35
+ position: 'sidebar'
36
+ },
37
+ hasMany: true,
38
+ relationTo: 'tags'
39
+ },
40
+ {
41
+ type: 'tabs',
42
+ tabs: [
43
+ {
44
+ fields: [
45
+ {
46
+ name: 'email',
47
+ type: 'email',
48
+ required: true
49
+ },
50
+ {
51
+ name: 'emailOptIn',
52
+ type: 'checkbox',
53
+ defaultValue: false,
54
+ label: 'Receive emails'
55
+ },
56
+ {
57
+ name: 'firstName',
58
+ type: 'text'
59
+ },
60
+ {
61
+ name: 'lastName',
62
+ type: 'text'
63
+ },
64
+ {
65
+ name: 'mobileNumber',
66
+ type: 'text'
67
+ },
68
+ {
69
+ name: 'mobileOptIn',
70
+ type: 'checkbox',
71
+ defaultValue: false,
72
+ label: 'Receive sms'
73
+ }
74
+ ],
75
+ label: 'Details'
76
+ },
77
+ {
78
+ fields: [
79
+ {
80
+ name: 'address',
81
+ type: 'text'
82
+ },
83
+ {
84
+ name: 'city',
85
+ type: 'text'
86
+ },
87
+ {
88
+ name: 'state',
89
+ type: 'text'
90
+ },
91
+ {
92
+ name: 'zip',
93
+ type: 'text'
94
+ },
95
+ {
96
+ name: 'country',
97
+ type: 'select',
98
+ options: countryOptions
99
+ }
100
+ ],
101
+ label: 'Location'
102
+ }
103
+ ]
104
+ }
105
+ ];
106
+ const config = {
107
+ ...contactsConfig?.contactsOverrides || {},
108
+ slug: contactsConfig.contactsOverrides?.slug || 'contacts',
109
+ access: {
110
+ read: authenticated,
111
+ ...contactsConfig?.contactsOverrides?.access || {}
112
+ },
113
+ admin: {
114
+ useAsTitle: contactsConfig?.contactsOverrides?.admin?.useAsTitle || 'email',
115
+ ...contactsConfig?.contactsOverrides?.admin || {}
116
+ },
117
+ fields: contactsConfig.contactsOverrides?.fields ? contactsConfig.contactsOverrides.fields({
118
+ defaultFields
119
+ }) : defaultFields
120
+ };
121
+ return config;
122
+ };
123
+
124
+ //# sourceMappingURL=generateContactsCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/collections/contacts/generateContactsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport iso from 'i18n-iso-countries'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\n\n/**\n * Generates country options for the country select field.\n */\nconst countryOptions = Object.entries(iso.getNames('en', { select: 'official' })).map(\n ([code, name]) => ({\n label: name,\n value: code,\n }),\n)\n\n/**\n * Generates the contacts collection configuration.\n * @param contactsConfig\n * @returns CollectionConfig\n *\n * @example\n * ```ts\n * import { generateContactsCollection } from 'path/to/generateContactsCollection';\n *\n * const contactsCollection = generateContactsCollection({\n * contactsOverrides: {\n * slug: 'my-contacts',\n * admin: {\n * useAsTitle: 'firstName',\n * },\n * },\n * });\n * ```\n */\nexport const generateContactsCollection = (\n contactsConfig: Pick<MobilizehubPluginConfig, 'contactsOverrides'>,\n): CollectionConfig => {\n const defaultFields: Field[] = [\n {\n name: 'tags',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n },\n hasMany: true,\n relationTo: 'tags',\n },\n {\n type: 'tabs',\n tabs: [\n {\n fields: [\n {\n name: 'email',\n type: 'email',\n required: true,\n },\n {\n name: 'emailOptIn',\n type: 'checkbox',\n defaultValue: false,\n label: 'Receive emails',\n },\n {\n name: 'firstName',\n type: 'text',\n },\n {\n name: 'lastName',\n type: 'text',\n },\n {\n name: 'mobileNumber',\n type: 'text',\n },\n {\n name: 'mobileOptIn',\n type: 'checkbox',\n defaultValue: false,\n label: 'Receive sms',\n },\n ],\n label: 'Details',\n },\n {\n fields: [\n {\n name: 'address',\n type: 'text',\n },\n {\n name: 'city',\n type: 'text',\n },\n {\n name: 'state',\n type: 'text',\n },\n {\n name: 'zip',\n type: 'text',\n },\n {\n name: 'country',\n type: 'select',\n options: countryOptions,\n },\n ],\n label: 'Location',\n },\n /**\n * @todo Enable form submissions relationship when form submissions collection is available.\n */\n // {\n // fields: [\n // {\n // name: 'formSubmissions',\n // type: 'join',\n // admin: {\n // description: 'Form submissions made by this contact',\n // },\n // collection: 'formSubmissions',\n // on: 'contact',\n // },\n // ],\n // label: 'Submissions',\n // },\n /**\n * @todo Enable transactions relationship when transactions collection is available.\n */\n // {\n // fields: [\n // {\n // name: 'transactions',\n // type: 'join',\n // admin: {\n // description: 'Transactions made by this contact',\n // },\n // collection: 'transactions',\n // on: 'contact',\n // },\n // ],\n // label: 'Transactions',\n // },\n ],\n },\n ]\n\n const config: CollectionConfig = {\n ...(contactsConfig?.contactsOverrides || {}),\n slug: contactsConfig.contactsOverrides?.slug || 'contacts',\n access: {\n read: authenticated,\n ...(contactsConfig?.contactsOverrides?.access || {}),\n },\n admin: {\n useAsTitle: contactsConfig?.contactsOverrides?.admin?.useAsTitle || 'email',\n ...(contactsConfig?.contactsOverrides?.admin || {}),\n },\n fields: contactsConfig.contactsOverrides?.fields\n ? contactsConfig.contactsOverrides.fields({ defaultFields })\n : defaultFields,\n }\n return config\n}\n"],"names":["iso","authenticated","countryOptions","Object","entries","getNames","select","map","code","name","label","value","generateContactsCollection","contactsConfig","defaultFields","type","admin","position","hasMany","relationTo","tabs","fields","required","defaultValue","options","config","contactsOverrides","slug","access","read","useAsTitle"],"mappings":"AAEA,OAAOA,SAAS,qBAAoB;AAIpC,SAASC,aAAa,QAAQ,gCAA+B;AAE7D;;CAEC,GACD,MAAMC,iBAAiBC,OAAOC,OAAO,CAACJ,IAAIK,QAAQ,CAAC,MAAM;IAAEC,QAAQ;AAAW,IAAIC,GAAG,CACnF,CAAC,CAACC,MAAMC,KAAK,GAAM,CAAA;QACjBC,OAAOD;QACPE,OAAOH;IACT,CAAA;AAGF;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,MAAMI,6BAA6B,CACxCC;IAEA,MAAMC,gBAAyB;QAC7B;YACEL,MAAM;YACNM,MAAM;YACNC,OAAO;gBACLC,UAAU;YACZ;YACAC,SAAS;YACTC,YAAY;QACd;QACA;YACEJ,MAAM;YACNK,MAAM;gBACJ;oBACEC,QAAQ;wBACN;4BACEZ,MAAM;4BACNM,MAAM;4BACNO,UAAU;wBACZ;wBACA;4BACEb,MAAM;4BACNM,MAAM;4BACNQ,cAAc;4BACdb,OAAO;wBACT;wBACA;4BACED,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;4BACNQ,cAAc;4BACdb,OAAO;wBACT;qBACD;oBACDA,OAAO;gBACT;gBACA;oBACEW,QAAQ;wBACN;4BACEZ,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;wBACR;wBACA;4BACEN,MAAM;4BACNM,MAAM;4BACNS,SAAStB;wBACX;qBACD;oBACDQ,OAAO;gBACT;aAmCD;QACH;KACD;IAED,MAAMe,SAA2B;QAC/B,GAAIZ,gBAAgBa,qBAAqB,CAAC,CAAC;QAC3CC,MAAMd,eAAea,iBAAiB,EAAEC,QAAQ;QAChDC,QAAQ;YACNC,MAAM5B;YACN,GAAIY,gBAAgBa,mBAAmBE,UAAU,CAAC,CAAC;QACrD;QACAZ,OAAO;YACLc,YAAYjB,gBAAgBa,mBAAmBV,OAAOc,cAAc;YACpE,GAAIjB,gBAAgBa,mBAAmBV,SAAS,CAAC,CAAC;QACpD;QACAK,QAAQR,eAAea,iBAAiB,EAAEL,SACtCR,eAAea,iBAAiB,CAACL,MAAM,CAAC;YAAEP;QAAc,KACxDA;IACN;IACA,OAAOW;AACT,EAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { MobilizehubPluginConfig } from '../../types/index.js';
3
+ export declare const generateEmailsCollection: (emailsConfig: MobilizehubPluginConfig) => CollectionConfig;
@@ -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,5 @@
1
+ import type { CollectionBeforeChangeHook } from 'payload';
2
+ /**
3
+ * A hook to synchronize the email status field based on activity history before saving.
4
+ */
5
+ export declare const syncStatusFromActivityBeforeChangeHook: CollectionBeforeChangeHook;
@@ -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,3 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ import type { MobilizehubPluginConfig } from '../../types/index.js';
3
+ export declare const generateTagsCollection: (tagsConfig: Pick<MobilizehubPluginConfig, "tagsOverrides">) => CollectionConfig;
@@ -0,0 +1,29 @@
1
+ import { authenticated } from '../../access/authenticated.js';
2
+ export const generateTagsCollection = (tagsConfig)=>{
3
+ const defaultFields = [
4
+ {
5
+ name: 'name',
6
+ type: 'text',
7
+ required: true
8
+ }
9
+ ];
10
+ const config = {
11
+ ...tagsConfig.tagsOverrides,
12
+ slug: tagsConfig.tagsOverrides?.slug || 'tags',
13
+ access: {
14
+ read: authenticated,
15
+ ...tagsConfig.tagsOverrides?.access || {}
16
+ },
17
+ admin: {
18
+ ...tagsConfig.tagsOverrides?.admin || {},
19
+ hidden: tagsConfig.tagsOverrides?.admin?.hidden || true,
20
+ useAsTitle: tagsConfig.tagsOverrides?.admin?.useAsTitle || 'name'
21
+ },
22
+ fields: tagsConfig.tagsOverrides?.fields ? tagsConfig.tagsOverrides.fields({
23
+ defaultFields
24
+ }) : defaultFields
25
+ };
26
+ return config;
27
+ };
28
+
29
+ //# sourceMappingURL=generateTagsCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/collections/tags/generateTagsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\n\nexport const generateTagsCollection = (\n tagsConfig: Pick<MobilizehubPluginConfig, 'tagsOverrides'>,\n): CollectionConfig => {\n const defaultFields: Field[] = [\n {\n name: 'name',\n type: 'text',\n required: true,\n },\n ]\n\n const config: CollectionConfig = {\n ...tagsConfig.tagsOverrides,\n slug: tagsConfig.tagsOverrides?.slug || 'tags',\n access: {\n read: authenticated,\n ...(tagsConfig.tagsOverrides?.access || {}),\n },\n admin: {\n ...(tagsConfig.tagsOverrides?.admin || {}),\n hidden: tagsConfig.tagsOverrides?.admin?.hidden || true,\n useAsTitle: tagsConfig.tagsOverrides?.admin?.useAsTitle || 'name',\n },\n fields: tagsConfig.tagsOverrides?.fields\n ? tagsConfig.tagsOverrides.fields({ defaultFields })\n : defaultFields,\n }\n\n return config\n}\n"],"names":["authenticated","generateTagsCollection","tagsConfig","defaultFields","name","type","required","config","tagsOverrides","slug","access","read","admin","hidden","useAsTitle","fields"],"mappings":"AAIA,SAASA,aAAa,QAAQ,gCAA+B;AAE7D,OAAO,MAAMC,yBAAyB,CACpCC;IAEA,MAAMC,gBAAyB;QAC7B;YACEC,MAAM;YACNC,MAAM;YACNC,UAAU;QACZ;KACD;IAED,MAAMC,SAA2B;QAC/B,GAAGL,WAAWM,aAAa;QAC3BC,MAAMP,WAAWM,aAAa,EAAEC,QAAQ;QACxCC,QAAQ;YACNC,MAAMX;YACN,GAAIE,WAAWM,aAAa,EAAEE,UAAU,CAAC,CAAC;QAC5C;QACAE,OAAO;YACL,GAAIV,WAAWM,aAAa,EAAEI,SAAS,CAAC,CAAC;YACzCC,QAAQX,WAAWM,aAAa,EAAEI,OAAOC,UAAU;YACnDC,YAAYZ,WAAWM,aAAa,EAAEI,OAAOE,cAAc;QAC7D;QACAC,QAAQb,WAAWM,aAAa,EAAEO,SAC9Bb,WAAWM,aAAa,CAACO,MAAM,CAAC;YAAEZ;QAAc,KAChDA;IACN;IAEA,OAAOI;AACT,EAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CollectionConfig } from 'payload';
2
+ export declare const generateUnsubscribeTokensCollection: () => CollectionConfig;
@@ -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,7 @@
1
+ import type { Payload } from 'payload';
2
+ export declare const MetricsCards: ({ data, payload, }: {
3
+ data: {
4
+ id: number;
5
+ };
6
+ payload: Payload;
7
+ }) => Promise<import("react").JSX.Element | null>;