@mobilizehub/payload-plugin 0.6.2 → 0.8.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/collections/petition-signatures/generatePetitionSignaturesCollection.d.ts +3 -0
- package/dist/collections/petition-signatures/generatePetitionSignaturesCollection.js +79 -0
- package/dist/collections/petition-signatures/generatePetitionSignaturesCollection.js.map +1 -0
- package/dist/collections/petition-signatures/hooks/processPetitionSignature.d.ts +11 -0
- package/dist/collections/petition-signatures/hooks/processPetitionSignature.js +153 -0
- package/dist/collections/petition-signatures/hooks/processPetitionSignature.js.map +1 -0
- package/dist/collections/petition-signatures/hooks/sendAutoresponse.d.ts +16 -0
- package/dist/collections/petition-signatures/hooks/sendAutoresponse.js +109 -0
- package/dist/collections/petition-signatures/hooks/sendAutoresponse.js.map +1 -0
- package/dist/collections/petitions/generatePetitionsCollection.d.ts +3 -0
- package/dist/collections/petitions/generatePetitionsCollection.js +252 -0
- package/dist/collections/petitions/generatePetitionsCollection.js.map +1 -0
- package/dist/endpoints/petitionSignatureHandler.d.ts +14 -0
- package/dist/endpoints/petitionSignatureHandler.js +146 -0
- package/dist/endpoints/petitionSignatureHandler.js.map +1 -0
- package/dist/helpers/countries.d.ts +22 -0
- package/dist/helpers/countries.js +25 -0
- package/dist/helpers/countries.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/sign-petition.d.ts +57 -0
- package/dist/react/sign-petition.js +54 -0
- package/dist/react/sign-petition.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { authenticated } from '../../access/authenticated.js';
|
|
2
|
+
import { createProcessPetitionSignatureHook } from './hooks/processPetitionSignature.js';
|
|
3
|
+
import { createSendPetitionAutoresponseHook } from './hooks/sendAutoresponse.js';
|
|
4
|
+
export const generatePetitionSignaturesCollection = (petitionSignaturesConfig)=>{
|
|
5
|
+
const defaultFields = [
|
|
6
|
+
{
|
|
7
|
+
name: 'petition',
|
|
8
|
+
type: 'relationship',
|
|
9
|
+
admin: {
|
|
10
|
+
position: 'sidebar',
|
|
11
|
+
readOnly: true
|
|
12
|
+
},
|
|
13
|
+
relationTo: petitionSignaturesConfig.petitionsOverrides?.slug || 'petitions',
|
|
14
|
+
required: true
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'contact',
|
|
18
|
+
type: 'relationship',
|
|
19
|
+
admin: {
|
|
20
|
+
position: 'sidebar',
|
|
21
|
+
readOnly: true
|
|
22
|
+
},
|
|
23
|
+
relationTo: petitionSignaturesConfig.contactsOverrides?.slug || 'contacts'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'createdAt',
|
|
27
|
+
type: 'date',
|
|
28
|
+
admin: {
|
|
29
|
+
position: 'sidebar',
|
|
30
|
+
readOnly: true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'data',
|
|
35
|
+
type: 'json',
|
|
36
|
+
admin: {
|
|
37
|
+
description: 'The raw data submitted with the petition signature.'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
];
|
|
41
|
+
const config = {
|
|
42
|
+
...petitionSignaturesConfig.petitionSignaturesOverrides || {},
|
|
43
|
+
slug: petitionSignaturesConfig.petitionSignaturesOverrides?.slug || 'petitionSignatures',
|
|
44
|
+
access: {
|
|
45
|
+
// Don't allow creation via admin or API - only via endpoint
|
|
46
|
+
create: ()=>false,
|
|
47
|
+
// Only authenticated users can read
|
|
48
|
+
read: authenticated,
|
|
49
|
+
// Prevent updates
|
|
50
|
+
update: ()=>false,
|
|
51
|
+
...petitionSignaturesConfig.petitionSignaturesOverrides?.access || {}
|
|
52
|
+
},
|
|
53
|
+
admin: {
|
|
54
|
+
...petitionSignaturesConfig.petitionSignaturesOverrides?.admin || {},
|
|
55
|
+
defaultColumns: petitionSignaturesConfig.petitionSignaturesOverrides?.admin?.defaultColumns || [
|
|
56
|
+
'id',
|
|
57
|
+
'petition',
|
|
58
|
+
'contact',
|
|
59
|
+
'createdAt'
|
|
60
|
+
],
|
|
61
|
+
hidden: petitionSignaturesConfig.petitionSignaturesOverrides?.admin?.hidden || true
|
|
62
|
+
},
|
|
63
|
+
fields: petitionSignaturesConfig.petitionSignaturesOverrides?.fields ? petitionSignaturesConfig.petitionSignaturesOverrides.fields({
|
|
64
|
+
defaultFields
|
|
65
|
+
}) : defaultFields,
|
|
66
|
+
hooks: {
|
|
67
|
+
afterChange: [
|
|
68
|
+
createSendPetitionAutoresponseHook(petitionSignaturesConfig)
|
|
69
|
+
],
|
|
70
|
+
beforeChange: [
|
|
71
|
+
createProcessPetitionSignatureHook(petitionSignaturesConfig)
|
|
72
|
+
],
|
|
73
|
+
...petitionSignaturesConfig.petitionSignaturesOverrides?.hooks || {}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
return config;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=generatePetitionSignaturesCollection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/petition-signatures/generatePetitionSignaturesCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { authenticated } from '../../access/authenticated.js'\nimport { createProcessPetitionSignatureHook } from './hooks/processPetitionSignature.js'\nimport { createSendPetitionAutoresponseHook } from './hooks/sendAutoresponse.js'\n\nexport const generatePetitionSignaturesCollection = (\n petitionSignaturesConfig: MobilizehubPluginConfig,\n) => {\n const defaultFields: Field[] = [\n {\n name: 'petition',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: petitionSignaturesConfig.petitionsOverrides?.slug || 'petitions',\n required: true,\n },\n {\n name: 'contact',\n type: 'relationship',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n relationTo: petitionSignaturesConfig.contactsOverrides?.slug || 'contacts',\n },\n {\n name: 'createdAt',\n type: 'date',\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n },\n {\n name: 'data',\n type: 'json',\n admin: {\n description: 'The raw data submitted with the petition signature.',\n },\n },\n ]\n\n const config: CollectionConfig = {\n ...(petitionSignaturesConfig.petitionSignaturesOverrides || {}),\n slug: petitionSignaturesConfig.petitionSignaturesOverrides?.slug || 'petitionSignatures',\n access: {\n // Don't allow creation via admin or API - only via endpoint\n create: () => false,\n // Only authenticated users can read\n read: authenticated,\n // Prevent updates\n update: () => false,\n ...(petitionSignaturesConfig.petitionSignaturesOverrides?.access || {}),\n },\n admin: {\n ...(petitionSignaturesConfig.petitionSignaturesOverrides?.admin || {}),\n defaultColumns: petitionSignaturesConfig.petitionSignaturesOverrides?.admin\n ?.defaultColumns || ['id', 'petition', 'contact', 'createdAt'],\n hidden: petitionSignaturesConfig.petitionSignaturesOverrides?.admin?.hidden || true,\n },\n fields: petitionSignaturesConfig.petitionSignaturesOverrides?.fields\n ? petitionSignaturesConfig.petitionSignaturesOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n afterChange: [createSendPetitionAutoresponseHook(petitionSignaturesConfig)],\n beforeChange: [createProcessPetitionSignatureHook(petitionSignaturesConfig)],\n ...(petitionSignaturesConfig.petitionSignaturesOverrides?.hooks || {}),\n },\n }\n\n return config\n}\n"],"names":["authenticated","createProcessPetitionSignatureHook","createSendPetitionAutoresponseHook","generatePetitionSignaturesCollection","petitionSignaturesConfig","defaultFields","name","type","admin","position","readOnly","relationTo","petitionsOverrides","slug","required","contactsOverrides","description","config","petitionSignaturesOverrides","access","create","read","update","defaultColumns","hidden","fields","hooks","afterChange","beforeChange"],"mappings":"AAIA,SAASA,aAAa,QAAQ,gCAA+B;AAC7D,SAASC,kCAAkC,QAAQ,sCAAqC;AACxF,SAASC,kCAAkC,QAAQ,8BAA6B;AAEhF,OAAO,MAAMC,uCAAuC,CAClDC;IAEA,MAAMC,gBAAyB;QAC7B;YACEC,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;YACAC,YAAYP,yBAAyBQ,kBAAkB,EAAEC,QAAQ;YACjEC,UAAU;QACZ;QACA;YACER,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;YACAC,YAAYP,yBAAyBW,iBAAiB,EAAEF,QAAQ;QAClE;QACA;YACEP,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLC,UAAU;gBACVC,UAAU;YACZ;QACF;QACA;YACEJ,MAAM;YACNC,MAAM;YACNC,OAAO;gBACLQ,aAAa;YACf;QACF;KACD;IAED,MAAMC,SAA2B;QAC/B,GAAIb,yBAAyBc,2BAA2B,IAAI,CAAC,CAAC;QAC9DL,MAAMT,yBAAyBc,2BAA2B,EAAEL,QAAQ;QACpEM,QAAQ;YACN,4DAA4D;YAC5DC,QAAQ,IAAM;YACd,oCAAoC;YACpCC,MAAMrB;YACN,kBAAkB;YAClBsB,QAAQ,IAAM;YACd,GAAIlB,yBAAyBc,2BAA2B,EAAEC,UAAU,CAAC,CAAC;QACxE;QACAX,OAAO;YACL,GAAIJ,yBAAyBc,2BAA2B,EAAEV,SAAS,CAAC,CAAC;YACrEe,gBAAgBnB,yBAAyBc,2BAA2B,EAAEV,OAClEe,kBAAkB;gBAAC;gBAAM;gBAAY;gBAAW;aAAY;YAChEC,QAAQpB,yBAAyBc,2BAA2B,EAAEV,OAAOgB,UAAU;QACjF;QACAC,QAAQrB,yBAAyBc,2BAA2B,EAAEO,SAC1DrB,yBAAyBc,2BAA2B,CAACO,MAAM,CAAC;YAAEpB;QAAc,KAC5EA;QACJqB,OAAO;YACLC,aAAa;gBAACzB,mCAAmCE;aAA0B;YAC3EwB,cAAc;gBAAC3B,mCAAmCG;aAA0B;YAC5E,GAAIA,yBAAyBc,2BAA2B,EAAEQ,SAAS,CAAC,CAAC;QACvE;IACF;IAEA,OAAOT;AACT,EAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CollectionBeforeChangeHook } from 'payload';
|
|
2
|
+
import type { MobilizehubPluginConfig } from '../../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the petition signature processing hook.
|
|
5
|
+
*
|
|
6
|
+
* This hook:
|
|
7
|
+
* 1. Creates or updates a contact based on signature data
|
|
8
|
+
* 2. Applies petition tags to the contact
|
|
9
|
+
* 3. Links the signature to the contact by returning modified data
|
|
10
|
+
*/
|
|
11
|
+
export declare const createProcessPetitionSignatureHook: (pluginConfig: MobilizehubPluginConfig) => CollectionBeforeChangeHook;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contact field mapping from petition signature data to contact fields.
|
|
3
|
+
*/ const CONTACT_FIELD_MAP = [
|
|
4
|
+
'email',
|
|
5
|
+
'emailOptIn',
|
|
6
|
+
'firstName',
|
|
7
|
+
'lastName',
|
|
8
|
+
'mobileNumber',
|
|
9
|
+
'mobileOptIn',
|
|
10
|
+
'address',
|
|
11
|
+
'city',
|
|
12
|
+
'state',
|
|
13
|
+
'zip',
|
|
14
|
+
'country'
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* Extracts contact-related fields from petition signature data.
|
|
18
|
+
*/ function extractContactData(signatureData) {
|
|
19
|
+
const contactData = {};
|
|
20
|
+
for (const field of CONTACT_FIELD_MAP){
|
|
21
|
+
if (signatureData[field] !== undefined) {
|
|
22
|
+
contactData[field] = signatureData[field];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return contactData;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates the petition signature processing hook.
|
|
29
|
+
*
|
|
30
|
+
* This hook:
|
|
31
|
+
* 1. Creates or updates a contact based on signature data
|
|
32
|
+
* 2. Applies petition tags to the contact
|
|
33
|
+
* 3. Links the signature to the contact by returning modified data
|
|
34
|
+
*/ export const createProcessPetitionSignatureHook = (pluginConfig)=>{
|
|
35
|
+
const contactsSlug = pluginConfig.contactsOverrides?.slug || 'contacts';
|
|
36
|
+
const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions';
|
|
37
|
+
return async ({ data, operation, req })=>{
|
|
38
|
+
// Only process on creation
|
|
39
|
+
if (operation !== 'create') {
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
const { payload } = req;
|
|
43
|
+
const logger = payload.logger;
|
|
44
|
+
// Parse signature data
|
|
45
|
+
const signatureData = data.data;
|
|
46
|
+
if (!signatureData) {
|
|
47
|
+
logger.warn('Petition signature has no data');
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
// Email is required for contact creation
|
|
51
|
+
const email = signatureData.email;
|
|
52
|
+
if (!email || typeof email !== 'string') {
|
|
53
|
+
logger.info('Petition signature has no email, skipping contact creation');
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
// Extract contact fields from signature
|
|
58
|
+
const contactData = extractContactData(signatureData);
|
|
59
|
+
// Find or create contact
|
|
60
|
+
const existingContactResult = await payload.find({
|
|
61
|
+
collection: contactsSlug,
|
|
62
|
+
limit: 1,
|
|
63
|
+
where: {
|
|
64
|
+
email: {
|
|
65
|
+
equals: email
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const existingContact = existingContactResult.docs[0];
|
|
70
|
+
let contactId;
|
|
71
|
+
if (existingContact) {
|
|
72
|
+
// Update existing contact (merge data, don't overwrite with empty values)
|
|
73
|
+
const updateData = {};
|
|
74
|
+
for (const [key, value] of Object.entries(contactData)){
|
|
75
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
76
|
+
updateData[key] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
await payload.update({
|
|
80
|
+
id: existingContact.id,
|
|
81
|
+
collection: contactsSlug,
|
|
82
|
+
data: updateData
|
|
83
|
+
});
|
|
84
|
+
contactId = existingContact.id;
|
|
85
|
+
logger.info(`Updated contact ${contactId} from petition signature`);
|
|
86
|
+
} else {
|
|
87
|
+
// Create new contact
|
|
88
|
+
const newContact = await payload.create({
|
|
89
|
+
collection: contactsSlug,
|
|
90
|
+
data: contactData
|
|
91
|
+
});
|
|
92
|
+
contactId = newContact.id;
|
|
93
|
+
logger.info(`Created contact ${contactId} from petition signature`);
|
|
94
|
+
}
|
|
95
|
+
const petitionId = data.petition;
|
|
96
|
+
const petitionIdValue = typeof petitionId === 'object' ? petitionId.id : petitionId;
|
|
97
|
+
if (!petitionIdValue) {
|
|
98
|
+
logger.error('Petition ID not found for petition signature');
|
|
99
|
+
throw new Error('Petition ID not found for petition signature');
|
|
100
|
+
}
|
|
101
|
+
// Get petition to check for tags
|
|
102
|
+
const petition = await payload.findByID({
|
|
103
|
+
id: petitionIdValue,
|
|
104
|
+
collection: petitionsSlug,
|
|
105
|
+
depth: 0
|
|
106
|
+
});
|
|
107
|
+
// Apply petition tags to contact
|
|
108
|
+
if (petition && Array.isArray(petition.tags) && petition.tags.length > 0) {
|
|
109
|
+
// Get petition tag IDs
|
|
110
|
+
const petitionTagIds = petition.tags.map((t)=>typeof t === 'object' ? t.id : t);
|
|
111
|
+
// Fetch current contact to get up-to-date tags
|
|
112
|
+
const currentContact = await payload.findByID({
|
|
113
|
+
id: contactId,
|
|
114
|
+
collection: contactsSlug,
|
|
115
|
+
depth: 0
|
|
116
|
+
});
|
|
117
|
+
// Get existing tag IDs (normalize to IDs)
|
|
118
|
+
const existingTagIds = (currentContact.tags || []).map((t)=>typeof t === 'object' ? t.id : t);
|
|
119
|
+
// Find only new tags that don't already exist
|
|
120
|
+
const newTagIds = petitionTagIds.filter((tagId)=>!existingTagIds.includes(tagId));
|
|
121
|
+
if (newTagIds.length > 0) {
|
|
122
|
+
// Merge tags
|
|
123
|
+
const mergedTags = [
|
|
124
|
+
...existingTagIds,
|
|
125
|
+
...newTagIds
|
|
126
|
+
];
|
|
127
|
+
// Update contact with merged tags
|
|
128
|
+
await payload.update({
|
|
129
|
+
id: contactId,
|
|
130
|
+
collection: contactsSlug,
|
|
131
|
+
data: {
|
|
132
|
+
tags: mergedTags
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
logger.info(`Applied ${newTagIds.length} new tags to contact ${contactId}`);
|
|
136
|
+
} else {
|
|
137
|
+
logger.info(`Contact ${contactId} already has all petition tags`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Return data with contact linked - no separate update needed
|
|
141
|
+
return {
|
|
142
|
+
...data,
|
|
143
|
+
contact: contactId
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
logger.error(error, 'Error processing petition signature');
|
|
147
|
+
// Don't throw - we don't want to fail the signature
|
|
148
|
+
return data;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=processPetitionSignature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/collections/petition-signatures/hooks/processPetitionSignature.ts"],"sourcesContent":["import type { CollectionBeforeChangeHook } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../../types/index.js'\n\n/**\n * Contact field mapping from petition signature data to contact fields.\n */\nconst CONTACT_FIELD_MAP = [\n 'email',\n 'emailOptIn',\n 'firstName',\n 'lastName',\n 'mobileNumber',\n 'mobileOptIn',\n 'address',\n 'city',\n 'state',\n 'zip',\n 'country',\n] as const\n\ntype ContactFieldName = (typeof CONTACT_FIELD_MAP)[number]\n\n/**\n * Extracts contact-related fields from petition signature data.\n */\nfunction extractContactData(\n signatureData: Record<string, unknown>,\n): Partial<Record<ContactFieldName, unknown>> {\n const contactData: Partial<Record<ContactFieldName, unknown>> = {}\n\n for (const field of CONTACT_FIELD_MAP) {\n if (signatureData[field] !== undefined) {\n contactData[field] = signatureData[field]\n }\n }\n\n return contactData\n}\n\n/**\n * Creates the petition signature processing hook.\n *\n * This hook:\n * 1. Creates or updates a contact based on signature data\n * 2. Applies petition tags to the contact\n * 3. Links the signature to the contact by returning modified data\n */\nexport const createProcessPetitionSignatureHook = (\n pluginConfig: MobilizehubPluginConfig,\n): CollectionBeforeChangeHook => {\n const contactsSlug = pluginConfig.contactsOverrides?.slug || 'contacts'\n const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions'\n\n return async ({ data, operation, req }) => {\n // Only process on creation\n if (operation !== 'create') {\n return data\n }\n\n const { payload } = req\n const logger = payload.logger\n\n // Parse signature data\n const signatureData = data.data as Record<string, unknown> | undefined\n\n if (!signatureData) {\n logger.warn('Petition signature has no data')\n return data\n }\n\n // Email is required for contact creation\n const email = signatureData.email as string | undefined\n\n if (!email || typeof email !== 'string') {\n logger.info('Petition signature has no email, skipping contact creation')\n return data\n }\n\n try {\n // Extract contact fields from signature\n const contactData = extractContactData(signatureData)\n\n // Find or create contact\n const existingContactResult = await payload.find({\n collection: contactsSlug,\n limit: 1,\n where: { email: { equals: email } },\n })\n\n const existingContact = existingContactResult.docs[0] as\n | { id: number | string; tags?: (number | string)[] }\n | undefined\n\n let contactId: number | string\n\n if (existingContact) {\n // Update existing contact (merge data, don't overwrite with empty values)\n const updateData: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(contactData)) {\n if (value !== undefined && value !== null && value !== '') {\n updateData[key] = value\n }\n }\n\n await payload.update({\n id: existingContact.id,\n collection: contactsSlug,\n data: updateData,\n })\n\n contactId = existingContact.id\n logger.info(`Updated contact ${contactId} from petition signature`)\n } else {\n // Create new contact\n const newContact = await payload.create({\n collection: contactsSlug,\n data: contactData,\n })\n\n contactId = newContact.id\n logger.info(`Created contact ${contactId} from petition signature`)\n }\n\n const petitionId = data.petition as { id: number | string } | number | string\n const petitionIdValue = typeof petitionId === 'object' ? petitionId.id : petitionId\n\n if (!petitionIdValue) {\n logger.error('Petition ID not found for petition signature')\n throw new Error('Petition ID not found for petition signature')\n }\n\n // Get petition to check for tags\n const petition = await payload.findByID({\n id: petitionIdValue,\n collection: petitionsSlug,\n depth: 0,\n })\n\n // Apply petition tags to contact\n if (petition && Array.isArray(petition.tags) && petition.tags.length > 0) {\n // Get petition tag IDs\n const petitionTagIds = petition.tags.map((t: { id: number | string } | number | string) =>\n typeof t === 'object' ? t.id : t,\n )\n\n // Fetch current contact to get up-to-date tags\n const currentContact = await payload.findByID({\n id: contactId,\n collection: contactsSlug,\n depth: 0,\n })\n\n // Get existing tag IDs (normalize to IDs)\n const existingTagIds = (\n (currentContact.tags as ({ id: number | string } | number | string)[]) || []\n ).map((t) => (typeof t === 'object' ? t.id : t))\n\n // Find only new tags that don't already exist\n const newTagIds = petitionTagIds.filter(\n (tagId: number | string) => !existingTagIds.includes(tagId),\n )\n\n if (newTagIds.length > 0) {\n // Merge tags\n const mergedTags = [...existingTagIds, ...newTagIds]\n\n // Update contact with merged tags\n await payload.update({\n id: contactId,\n collection: contactsSlug,\n data: { tags: mergedTags },\n })\n\n logger.info(`Applied ${newTagIds.length} new tags to contact ${contactId}`)\n } else {\n logger.info(`Contact ${contactId} already has all petition tags`)\n }\n }\n\n // Return data with contact linked - no separate update needed\n return {\n ...data,\n contact: contactId,\n }\n } catch (error) {\n logger.error(error as Error, 'Error processing petition signature')\n // Don't throw - we don't want to fail the signature\n return data\n }\n }\n}\n"],"names":["CONTACT_FIELD_MAP","extractContactData","signatureData","contactData","field","undefined","createProcessPetitionSignatureHook","pluginConfig","contactsSlug","contactsOverrides","slug","petitionsSlug","petitionsOverrides","data","operation","req","payload","logger","warn","email","info","existingContactResult","find","collection","limit","where","equals","existingContact","docs","contactId","updateData","key","value","Object","entries","update","id","newContact","create","petitionId","petition","petitionIdValue","error","Error","findByID","depth","Array","isArray","tags","length","petitionTagIds","map","t","currentContact","existingTagIds","newTagIds","filter","tagId","includes","mergedTags","contact"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { CollectionAfterChangeHook } from 'payload';
|
|
2
|
+
import type { MobilizehubPluginConfig } from '../../../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the autoresponse email hook for petition signatures.
|
|
5
|
+
*
|
|
6
|
+
* This hook sends an automatic confirmation email to the petition signer
|
|
7
|
+
* when autoresponse is enabled on the petition. It:
|
|
8
|
+
* 1. Checks if autoresponse is enabled on the petition
|
|
9
|
+
* 2. Validates required autoresponse fields
|
|
10
|
+
* 3. Parses Lexical content to HTML
|
|
11
|
+
* 4. Renders through email template
|
|
12
|
+
* 5. Sends the email
|
|
13
|
+
*
|
|
14
|
+
* Errors are logged but never thrown to avoid failing the signature.
|
|
15
|
+
*/
|
|
16
|
+
export declare const createSendPetitionAutoresponseHook: (pluginConfig: MobilizehubPluginConfig) => CollectionAfterChangeHook;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { formatFromAddress } from '../../../utils/email.js';
|
|
2
|
+
import { parseLexicalContent } from '../../../utils/lexical.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the autoresponse email hook for petition signatures.
|
|
5
|
+
*
|
|
6
|
+
* This hook sends an automatic confirmation email to the petition signer
|
|
7
|
+
* when autoresponse is enabled on the petition. It:
|
|
8
|
+
* 1. Checks if autoresponse is enabled on the petition
|
|
9
|
+
* 2. Validates required autoresponse fields
|
|
10
|
+
* 3. Parses Lexical content to HTML
|
|
11
|
+
* 4. Renders through email template
|
|
12
|
+
* 5. Sends the email
|
|
13
|
+
*
|
|
14
|
+
* Errors are logged but never thrown to avoid failing the signature.
|
|
15
|
+
*/ export const createSendPetitionAutoresponseHook = (pluginConfig)=>{
|
|
16
|
+
const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions';
|
|
17
|
+
const contactsSlug = pluginConfig.contactsOverrides?.slug || 'contacts';
|
|
18
|
+
return async ({ doc, operation, req })=>{
|
|
19
|
+
// Only process on create
|
|
20
|
+
if (operation !== 'create') {
|
|
21
|
+
return doc;
|
|
22
|
+
}
|
|
23
|
+
const { payload } = req;
|
|
24
|
+
const logger = payload.logger;
|
|
25
|
+
try {
|
|
26
|
+
// Get petition ID from signature
|
|
27
|
+
const petitionId = doc.petition;
|
|
28
|
+
const petitionIdValue = typeof petitionId === 'object' ? petitionId.id : petitionId;
|
|
29
|
+
if (!petitionIdValue) {
|
|
30
|
+
return doc;
|
|
31
|
+
}
|
|
32
|
+
// Fetch petition to get autoresponse configuration
|
|
33
|
+
const petition = await payload.findByID({
|
|
34
|
+
id: petitionIdValue,
|
|
35
|
+
collection: petitionsSlug
|
|
36
|
+
});
|
|
37
|
+
if (!petition) {
|
|
38
|
+
return doc;
|
|
39
|
+
}
|
|
40
|
+
// Check if autoresponse is enabled
|
|
41
|
+
const autoresponse = petition.autoresponse;
|
|
42
|
+
if (!autoresponse?.enabled) {
|
|
43
|
+
return doc;
|
|
44
|
+
}
|
|
45
|
+
// Get contact email
|
|
46
|
+
const contactId = doc.contact;
|
|
47
|
+
if (!contactId) {
|
|
48
|
+
logger.warn('Petition signature has no linked contact, skipping autoresponse');
|
|
49
|
+
return doc;
|
|
50
|
+
}
|
|
51
|
+
const contactIdValue = typeof contactId === 'object' ? contactId.id : contactId;
|
|
52
|
+
const contact = await payload.findByID({
|
|
53
|
+
id: contactIdValue,
|
|
54
|
+
collection: contactsSlug
|
|
55
|
+
});
|
|
56
|
+
const contactEmail = contact?.email;
|
|
57
|
+
if (!contactEmail) {
|
|
58
|
+
logger.warn(`Contact ${contactIdValue} has no email, skipping autoresponse`);
|
|
59
|
+
return doc;
|
|
60
|
+
}
|
|
61
|
+
// Validate required autoresponse fields
|
|
62
|
+
const { content, fromAddress, fromName, previewText, replyTo, subject } = autoresponse;
|
|
63
|
+
if (!subject) {
|
|
64
|
+
logger.warn(`Petition ${petitionIdValue} autoresponse has no subject, skipping`);
|
|
65
|
+
return doc;
|
|
66
|
+
}
|
|
67
|
+
if (!content) {
|
|
68
|
+
logger.warn(`Petition ${petitionIdValue} autoresponse has no content, skipping`);
|
|
69
|
+
return doc;
|
|
70
|
+
}
|
|
71
|
+
if (!fromName || !fromAddress) {
|
|
72
|
+
logger.warn(`Petition ${petitionIdValue} autoresponse has no from address, skipping`);
|
|
73
|
+
return doc;
|
|
74
|
+
}
|
|
75
|
+
// Get email adapter
|
|
76
|
+
const { render, sendEmail } = pluginConfig.email(req);
|
|
77
|
+
// Parse Lexical content
|
|
78
|
+
const parsedContent = await parseLexicalContent(content, payload.config);
|
|
79
|
+
// Format from address
|
|
80
|
+
const formattedFromAddress = formatFromAddress(fromName, fromAddress);
|
|
81
|
+
// Render through email template
|
|
82
|
+
const html = render({
|
|
83
|
+
from: formattedFromAddress,
|
|
84
|
+
html: parsedContent.html,
|
|
85
|
+
markdown: parsedContent.markdown,
|
|
86
|
+
plainText: parsedContent.plainText,
|
|
87
|
+
previewText,
|
|
88
|
+
replyTo,
|
|
89
|
+
subject,
|
|
90
|
+
to: contactEmail,
|
|
91
|
+
token: ''
|
|
92
|
+
});
|
|
93
|
+
// Send the email
|
|
94
|
+
await sendEmail({
|
|
95
|
+
from: formattedFromAddress,
|
|
96
|
+
html,
|
|
97
|
+
subject,
|
|
98
|
+
to: contactEmail
|
|
99
|
+
});
|
|
100
|
+
logger.info(`Sent autoresponse to ${contactEmail} for petition ${petitionIdValue}`);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logger.error(error, 'Error sending autoresponse email');
|
|
103
|
+
// Don't throw - signature should still succeed
|
|
104
|
+
}
|
|
105
|
+
return doc;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=sendAutoresponse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/collections/petition-signatures/hooks/sendAutoresponse.ts"],"sourcesContent":["import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'\nimport type { CollectionAfterChangeHook } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../../types/index.js'\n\nimport { formatFromAddress } from '../../../utils/email.js'\nimport { parseLexicalContent } from '../../../utils/lexical.js'\n\n/**\n * Autoresponse configuration from petition.\n */\ntype AutoresponseConfig = {\n content?: SerializedEditorState\n enabled?: boolean\n fromAddress?: string\n fromName?: string\n previewText?: string\n replyTo?: string\n subject?: string\n}\n\n/**\n * Creates the autoresponse email hook for petition signatures.\n *\n * This hook sends an automatic confirmation email to the petition signer\n * when autoresponse is enabled on the petition. It:\n * 1. Checks if autoresponse is enabled on the petition\n * 2. Validates required autoresponse fields\n * 3. Parses Lexical content to HTML\n * 4. Renders through email template\n * 5. Sends the email\n *\n * Errors are logged but never thrown to avoid failing the signature.\n */\nexport const createSendPetitionAutoresponseHook = (\n pluginConfig: MobilizehubPluginConfig,\n): CollectionAfterChangeHook => {\n const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions'\n const contactsSlug = pluginConfig.contactsOverrides?.slug || 'contacts'\n\n return async ({ doc, operation, req }) => {\n // Only process on create\n if (operation !== 'create') {\n return doc\n }\n\n const { payload } = req\n const logger = payload.logger\n\n try {\n // Get petition ID from signature\n const petitionId = doc.petition as { id: number | string } | number | string\n const petitionIdValue = typeof petitionId === 'object' ? petitionId.id : petitionId\n\n if (!petitionIdValue) {\n return doc\n }\n\n // Fetch petition to get autoresponse configuration\n const petition = await payload.findByID({\n id: petitionIdValue,\n collection: petitionsSlug,\n })\n\n if (!petition) {\n return doc\n }\n\n // Check if autoresponse is enabled\n const autoresponse = petition.autoresponse as AutoresponseConfig | undefined\n\n if (!autoresponse?.enabled) {\n return doc\n }\n\n // Get contact email\n const contactId = doc.contact as { id: number | string } | number | string | undefined\n\n if (!contactId) {\n logger.warn('Petition signature has no linked contact, skipping autoresponse')\n return doc\n }\n\n const contactIdValue = typeof contactId === 'object' ? contactId.id : contactId\n\n const contact = await payload.findByID({\n id: contactIdValue,\n collection: contactsSlug,\n })\n\n const contactEmail = contact?.email as string | undefined\n\n if (!contactEmail) {\n logger.warn(`Contact ${contactIdValue} has no email, skipping autoresponse`)\n return doc\n }\n\n // Validate required autoresponse fields\n const { content, fromAddress, fromName, previewText, replyTo, subject } = autoresponse\n\n if (!subject) {\n logger.warn(`Petition ${petitionIdValue} autoresponse has no subject, skipping`)\n return doc\n }\n\n if (!content) {\n logger.warn(`Petition ${petitionIdValue} autoresponse has no content, skipping`)\n return doc\n }\n\n if (!fromName || !fromAddress) {\n logger.warn(`Petition ${petitionIdValue} autoresponse has no from address, skipping`)\n return doc\n }\n\n // Get email adapter\n const { render, sendEmail } = pluginConfig.email(req)\n\n // Parse Lexical content\n const parsedContent = await parseLexicalContent(content, payload.config)\n\n // Format from address\n const formattedFromAddress = formatFromAddress(fromName, fromAddress)\n\n // Render through email template\n const html = render({\n from: formattedFromAddress,\n html: parsedContent.html,\n markdown: parsedContent.markdown,\n plainText: parsedContent.plainText,\n previewText,\n replyTo,\n subject,\n to: contactEmail,\n token: '', // No unsubscribe token for autoresponse\n })\n\n // Send the email\n await sendEmail({\n from: formattedFromAddress,\n html,\n subject,\n to: contactEmail,\n })\n\n logger.info(`Sent autoresponse to ${contactEmail} for petition ${petitionIdValue}`)\n } catch (error) {\n logger.error(error as Error, 'Error sending autoresponse email')\n // Don't throw - signature should still succeed\n }\n\n return doc\n }\n}\n"],"names":["formatFromAddress","parseLexicalContent","createSendPetitionAutoresponseHook","pluginConfig","petitionsSlug","petitionsOverrides","slug","contactsSlug","contactsOverrides","doc","operation","req","payload","logger","petitionId","petition","petitionIdValue","id","findByID","collection","autoresponse","enabled","contactId","contact","warn","contactIdValue","contactEmail","email","content","fromAddress","fromName","previewText","replyTo","subject","render","sendEmail","parsedContent","config","formattedFromAddress","html","from","markdown","plainText","to","token","info","error"],"mappings":"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"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { createContactFieldsConfig } from '../../fields/contact-fields.js';
|
|
2
|
+
import { createNameField } from '../../fields/name.js';
|
|
3
|
+
import { createPublishedAtField } from '../../fields/publishedAt.js';
|
|
4
|
+
import { createSlugField } from '../../fields/slug.js';
|
|
5
|
+
import { createStatusField } from '../../fields/status.js';
|
|
6
|
+
export const generatePetitionsCollection = (petitionsConfig)=>{
|
|
7
|
+
const defaultFields = [
|
|
8
|
+
createStatusField(),
|
|
9
|
+
{
|
|
10
|
+
type: 'tabs',
|
|
11
|
+
tabs: [
|
|
12
|
+
{
|
|
13
|
+
fields: [
|
|
14
|
+
createNameField(),
|
|
15
|
+
createSlugField(),
|
|
16
|
+
createPublishedAtField()
|
|
17
|
+
],
|
|
18
|
+
label: 'Settings'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
fields: [
|
|
22
|
+
{
|
|
23
|
+
name: 'headline',
|
|
24
|
+
type: 'text',
|
|
25
|
+
label: 'Headline'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'content',
|
|
29
|
+
type: 'richText',
|
|
30
|
+
label: 'Content'
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'target',
|
|
34
|
+
type: 'text',
|
|
35
|
+
admin: {
|
|
36
|
+
description: 'Who or what is this petition addressed to?'
|
|
37
|
+
},
|
|
38
|
+
label: 'Petition Target'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'ask',
|
|
42
|
+
type: 'textarea',
|
|
43
|
+
admin: {
|
|
44
|
+
description: 'What is this petition asking for or demanding?'
|
|
45
|
+
},
|
|
46
|
+
label: 'Petition Ask'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'goal',
|
|
50
|
+
type: 'number',
|
|
51
|
+
admin: {
|
|
52
|
+
description: 'The target number of signatures for this petition.'
|
|
53
|
+
},
|
|
54
|
+
label: 'Signature Goal',
|
|
55
|
+
min: 1
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
label: 'Content'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
fields: [
|
|
62
|
+
{
|
|
63
|
+
name: 'legend',
|
|
64
|
+
type: 'text',
|
|
65
|
+
localized: true
|
|
66
|
+
},
|
|
67
|
+
createContactFieldsConfig(),
|
|
68
|
+
{
|
|
69
|
+
name: 'submitButtonLabel',
|
|
70
|
+
type: 'text',
|
|
71
|
+
defaultValue: 'Sign Petition',
|
|
72
|
+
localized: true
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'confirmationType',
|
|
76
|
+
type: 'radio',
|
|
77
|
+
admin: {
|
|
78
|
+
description: 'Choose whether to display an on-page message or redirect to a different page after they sign the petition.',
|
|
79
|
+
layout: 'horizontal'
|
|
80
|
+
},
|
|
81
|
+
defaultValue: 'message',
|
|
82
|
+
options: [
|
|
83
|
+
{
|
|
84
|
+
label: 'Message',
|
|
85
|
+
value: 'message'
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
label: 'Redirect',
|
|
89
|
+
value: 'redirect'
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'confirmationMessage',
|
|
95
|
+
type: 'richText',
|
|
96
|
+
admin: {
|
|
97
|
+
condition: (_, siblingData)=>siblingData?.confirmationType === 'message'
|
|
98
|
+
},
|
|
99
|
+
localized: true
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'type',
|
|
103
|
+
type: 'radio',
|
|
104
|
+
admin: {
|
|
105
|
+
condition: (_, siblingData)=>siblingData?.confirmationType === 'redirect',
|
|
106
|
+
layout: 'horizontal'
|
|
107
|
+
},
|
|
108
|
+
defaultValue: 'reference',
|
|
109
|
+
options: [
|
|
110
|
+
{
|
|
111
|
+
label: 'Internal link',
|
|
112
|
+
value: 'reference'
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
label: 'Custom URL',
|
|
116
|
+
value: 'custom'
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'reference',
|
|
122
|
+
type: 'relationship',
|
|
123
|
+
admin: {
|
|
124
|
+
condition: (_, siblingData)=>siblingData?.confirmationType === 'redirect' && siblingData?.type === 'reference'
|
|
125
|
+
},
|
|
126
|
+
label: 'Document to link to',
|
|
127
|
+
maxDepth: 2,
|
|
128
|
+
relationTo: [
|
|
129
|
+
'pages',
|
|
130
|
+
'petitions'
|
|
131
|
+
],
|
|
132
|
+
required: true
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'url',
|
|
136
|
+
type: 'text',
|
|
137
|
+
admin: {
|
|
138
|
+
condition: (_, siblingData)=>siblingData?.confirmationType === 'redirect' && siblingData?.type === 'custom'
|
|
139
|
+
},
|
|
140
|
+
label: 'URL to redirect to',
|
|
141
|
+
required: true
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'tags',
|
|
145
|
+
type: 'relationship',
|
|
146
|
+
admin: {
|
|
147
|
+
description: 'Tag all contacts who sign this petition with these tags.'
|
|
148
|
+
},
|
|
149
|
+
hasMany: true,
|
|
150
|
+
relationTo: 'tags'
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'autoresponse',
|
|
154
|
+
type: 'group',
|
|
155
|
+
fields: [
|
|
156
|
+
{
|
|
157
|
+
name: 'enabled',
|
|
158
|
+
type: 'checkbox',
|
|
159
|
+
admin: {
|
|
160
|
+
style: {
|
|
161
|
+
marginTop: '1.5rem'
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
defaultValue: false,
|
|
165
|
+
label: 'Automatically send an autoresponse email to the signer'
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: 'fromName',
|
|
169
|
+
type: 'text',
|
|
170
|
+
defaultValue: ({ req })=>petitionsConfig.email(req).defaultFromName || '',
|
|
171
|
+
label: 'Name',
|
|
172
|
+
required: true
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'fromAddress',
|
|
176
|
+
type: 'text',
|
|
177
|
+
admin: {
|
|
178
|
+
description: 'The from address is set in the email configuration.',
|
|
179
|
+
readOnly: true
|
|
180
|
+
},
|
|
181
|
+
defaultValue: ({ req })=>petitionsConfig.email(req).defaultFromAddress || '',
|
|
182
|
+
label: 'Address',
|
|
183
|
+
required: true
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'replyTo',
|
|
187
|
+
type: 'text',
|
|
188
|
+
localized: true
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'subject',
|
|
192
|
+
type: 'text',
|
|
193
|
+
localized: true
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: 'previewText',
|
|
197
|
+
type: 'text',
|
|
198
|
+
localized: true
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'content',
|
|
202
|
+
type: 'richText',
|
|
203
|
+
localized: true
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
label: 'Form'
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
fields: [
|
|
212
|
+
{
|
|
213
|
+
name: 'petitionSignatures',
|
|
214
|
+
type: 'join',
|
|
215
|
+
collection: 'petitionSignatures',
|
|
216
|
+
on: 'petition'
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
label: 'Signatures'
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
];
|
|
224
|
+
const config = {
|
|
225
|
+
...petitionsConfig.petitionsOverrides || {},
|
|
226
|
+
slug: petitionsConfig.petitionsOverrides?.slug || 'petitions',
|
|
227
|
+
access: {
|
|
228
|
+
read: ()=>true,
|
|
229
|
+
...petitionsConfig.petitionsOverrides?.access || {}
|
|
230
|
+
},
|
|
231
|
+
admin: {
|
|
232
|
+
...petitionsConfig.petitionsOverrides?.admin || {},
|
|
233
|
+
defaultColumns: petitionsConfig.petitionsOverrides?.admin?.defaultColumns || [
|
|
234
|
+
'id',
|
|
235
|
+
'name',
|
|
236
|
+
'slug',
|
|
237
|
+
'status'
|
|
238
|
+
],
|
|
239
|
+
hidden: petitionsConfig.petitionsOverrides?.admin?.hidden || false,
|
|
240
|
+
useAsTitle: petitionsConfig.petitionsOverrides?.admin?.useAsTitle || 'name'
|
|
241
|
+
},
|
|
242
|
+
fields: petitionsConfig.petitionsOverrides?.fields ? petitionsConfig.petitionsOverrides.fields({
|
|
243
|
+
defaultFields
|
|
244
|
+
}) : defaultFields,
|
|
245
|
+
hooks: {
|
|
246
|
+
...petitionsConfig.petitionsOverrides?.hooks || {}
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
return config;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
//# sourceMappingURL=generatePetitionsCollection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/collections/petitions/generatePetitionsCollection.ts"],"sourcesContent":["import type { CollectionConfig, Field } from 'payload'\n\nimport type { MobilizehubPluginConfig } from '../../types/index.js'\n\nimport { createContactFieldsConfig } from '../../fields/contact-fields.js'\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 generatePetitionsCollection = (petitionsConfig: MobilizehubPluginConfig) => {\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: 'headline',\n type: 'text',\n label: 'Headline',\n },\n {\n name: 'content',\n type: 'richText',\n label: 'Content',\n },\n {\n name: 'target',\n type: 'text',\n admin: {\n description: 'Who or what is this petition addressed to?',\n },\n label: 'Petition Target',\n },\n {\n name: 'ask',\n type: 'textarea',\n admin: {\n description: 'What is this petition asking for or demanding?',\n },\n label: 'Petition Ask',\n },\n {\n name: 'goal',\n type: 'number',\n admin: {\n description: 'The target number of signatures for this petition.',\n },\n label: 'Signature Goal',\n min: 1,\n },\n ],\n label: 'Content',\n },\n {\n fields: [\n {\n name: 'legend',\n type: 'text',\n localized: true,\n },\n createContactFieldsConfig(),\n {\n name: 'submitButtonLabel',\n type: 'text',\n defaultValue: 'Sign Petition',\n localized: true,\n },\n {\n name: 'confirmationType',\n type: 'radio',\n admin: {\n description:\n 'Choose whether to display an on-page message or redirect to a different page after they sign the petition.',\n layout: 'horizontal',\n },\n defaultValue: 'message',\n options: [\n {\n label: 'Message',\n value: 'message',\n },\n {\n label: 'Redirect',\n value: 'redirect',\n },\n ],\n },\n {\n name: 'confirmationMessage',\n type: 'richText',\n admin: {\n condition: (_, siblingData) => siblingData?.confirmationType === 'message',\n },\n localized: true,\n },\n {\n name: 'type',\n type: 'radio',\n admin: {\n condition: (_, siblingData) => siblingData?.confirmationType === 'redirect',\n layout: 'horizontal',\n },\n defaultValue: 'reference',\n options: [\n {\n label: 'Internal link',\n value: 'reference',\n },\n {\n label: 'Custom URL',\n value: 'custom',\n },\n ],\n },\n {\n name: 'reference',\n type: 'relationship',\n admin: {\n condition: (_, siblingData) =>\n siblingData?.confirmationType === 'redirect' && siblingData?.type === 'reference',\n },\n label: 'Document to link to',\n maxDepth: 2,\n relationTo: ['pages', 'petitions'],\n required: true,\n },\n {\n name: 'url',\n type: 'text',\n admin: {\n condition: (_, siblingData) =>\n siblingData?.confirmationType === 'redirect' && siblingData?.type === 'custom',\n },\n label: 'URL to redirect to',\n required: true,\n },\n {\n name: 'tags',\n type: 'relationship',\n admin: {\n description: 'Tag all contacts who sign this petition with these tags.',\n },\n hasMany: true,\n relationTo: 'tags',\n },\n {\n name: 'autoresponse',\n type: 'group',\n fields: [\n {\n name: 'enabled',\n type: 'checkbox',\n admin: {\n style: {\n marginTop: '1.5rem',\n },\n },\n defaultValue: false,\n label: 'Automatically send an autoresponse email to the signer',\n },\n {\n name: 'fromName',\n type: 'text',\n defaultValue: ({ req }) => petitionsConfig.email(req).defaultFromName || '',\n label: 'Name',\n required: true,\n },\n {\n name: 'fromAddress',\n type: 'text',\n admin: {\n description: 'The from address is set in the email configuration.',\n readOnly: true,\n },\n defaultValue: ({ req }) => petitionsConfig.email(req).defaultFromAddress || '',\n label: 'Address',\n required: true,\n },\n {\n name: 'replyTo',\n type: 'text',\n localized: true,\n },\n {\n name: 'subject',\n type: 'text',\n localized: true,\n },\n {\n name: 'previewText',\n type: 'text',\n localized: true,\n },\n {\n name: 'content',\n type: 'richText',\n localized: true,\n },\n ],\n },\n ],\n label: 'Form',\n },\n {\n fields: [\n {\n name: 'petitionSignatures',\n type: 'join',\n collection: 'petitionSignatures',\n on: 'petition',\n },\n ],\n label: 'Signatures',\n },\n ],\n },\n ]\n\n const config: CollectionConfig = {\n ...(petitionsConfig.petitionsOverrides || {}),\n slug: petitionsConfig.petitionsOverrides?.slug || 'petitions',\n access: {\n read: () => true,\n ...(petitionsConfig.petitionsOverrides?.access || {}),\n },\n admin: {\n ...(petitionsConfig.petitionsOverrides?.admin || {}),\n defaultColumns: petitionsConfig.petitionsOverrides?.admin?.defaultColumns || [\n 'id',\n 'name',\n 'slug',\n 'status',\n ],\n hidden: petitionsConfig.petitionsOverrides?.admin?.hidden || false,\n useAsTitle: petitionsConfig.petitionsOverrides?.admin?.useAsTitle || 'name',\n },\n fields: petitionsConfig.petitionsOverrides?.fields\n ? petitionsConfig.petitionsOverrides.fields({ defaultFields })\n : defaultFields,\n hooks: {\n ...(petitionsConfig.petitionsOverrides?.hooks || {}),\n },\n }\n\n return config\n}\n"],"names":["createContactFieldsConfig","createNameField","createPublishedAtField","createSlugField","createStatusField","generatePetitionsCollection","petitionsConfig","defaultFields","type","tabs","fields","label","name","admin","description","min","localized","defaultValue","layout","options","value","condition","_","siblingData","confirmationType","maxDepth","relationTo","required","hasMany","style","marginTop","req","email","defaultFromName","readOnly","defaultFromAddress","collection","on","config","petitionsOverrides","slug","access","read","defaultColumns","hidden","useAsTitle","hooks"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PayloadHandler } from 'payload';
|
|
2
|
+
import type { MobilizehubPluginConfig } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the public petition signature endpoint handler.
|
|
5
|
+
*
|
|
6
|
+
* Accepts petition signatures from frontend applications, validates the data,
|
|
7
|
+
* creates a petition signature record, and returns the appropriate confirmation.
|
|
8
|
+
*
|
|
9
|
+
* This endpoint is public (no authentication required) but validates:
|
|
10
|
+
* - Petition exists and is published
|
|
11
|
+
* - Required fields are present
|
|
12
|
+
* - Email format is valid (if provided)
|
|
13
|
+
*/
|
|
14
|
+
export declare const petitionSignatureHandler: (pluginConfig: MobilizehubPluginConfig) => PayloadHandler;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { ErrorCodes, errorResponse, successResponse } from '../utils/api-response.js';
|
|
3
|
+
/**
|
|
4
|
+
* Schema for petition signature request body.
|
|
5
|
+
*/ const PetitionSignatureBodySchema = z.object({
|
|
6
|
+
data: z.record(z.string(), z.unknown()),
|
|
7
|
+
petitionId: z.union([
|
|
8
|
+
z.string(),
|
|
9
|
+
z.number()
|
|
10
|
+
])
|
|
11
|
+
});
|
|
12
|
+
/**
|
|
13
|
+
* Validates signature data against petition field configuration.
|
|
14
|
+
*/ function validateSignatureData(data, contactFields) {
|
|
15
|
+
const errors = [];
|
|
16
|
+
if (!contactFields || contactFields.length === 0) {
|
|
17
|
+
return {
|
|
18
|
+
errors: [],
|
|
19
|
+
valid: true
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
for (const field of contactFields){
|
|
23
|
+
if (field.required) {
|
|
24
|
+
const value = data[field.blockType];
|
|
25
|
+
if (value === undefined || value === null || value === '') {
|
|
26
|
+
errors.push(`${field.blockType} is required`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Validate email format if present
|
|
31
|
+
if (data.email && typeof data.email === 'string') {
|
|
32
|
+
const emailRegex = /^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/;
|
|
33
|
+
if (!emailRegex.test(data.email)) {
|
|
34
|
+
errors.push('Invalid email format');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
errors,
|
|
39
|
+
valid: errors.length === 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Fetches and validates a petition exists and is published.
|
|
44
|
+
*/ async function getPublishedPetition(payload, petitionId, collectionSlug) {
|
|
45
|
+
try {
|
|
46
|
+
const petition = await payload.findByID({
|
|
47
|
+
id: petitionId,
|
|
48
|
+
collection: collectionSlug
|
|
49
|
+
});
|
|
50
|
+
if (!petition) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
// Only allow signatures to published petitions
|
|
54
|
+
if (petition.status !== 'published') {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return petition;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Builds the confirmation response based on petition settings.
|
|
64
|
+
*/ function buildConfirmationResponse(petition) {
|
|
65
|
+
if (petition.confirmationType === 'redirect' && petition.reference) {
|
|
66
|
+
let redirectUrl;
|
|
67
|
+
if (petition.url) {
|
|
68
|
+
redirectUrl = petition.url;
|
|
69
|
+
} else if (typeof petition.reference.value === 'object' && petition.reference.value.slug) {
|
|
70
|
+
redirectUrl = `/${petition.reference.value.slug}`;
|
|
71
|
+
}
|
|
72
|
+
if (redirectUrl) {
|
|
73
|
+
return {
|
|
74
|
+
type: 'redirect',
|
|
75
|
+
redirect: redirectUrl
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
type: 'message',
|
|
81
|
+
message: petition.confirmationMessage || 'Thank you for signing this petition.'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates the public petition signature endpoint handler.
|
|
86
|
+
*
|
|
87
|
+
* Accepts petition signatures from frontend applications, validates the data,
|
|
88
|
+
* creates a petition signature record, and returns the appropriate confirmation.
|
|
89
|
+
*
|
|
90
|
+
* This endpoint is public (no authentication required) but validates:
|
|
91
|
+
* - Petition exists and is published
|
|
92
|
+
* - Required fields are present
|
|
93
|
+
* - Email format is valid (if provided)
|
|
94
|
+
*/ export const petitionSignatureHandler = (pluginConfig)=>{
|
|
95
|
+
const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions';
|
|
96
|
+
const petitionSignaturesSlug = pluginConfig.petitionSignaturesOverrides?.slug || 'petitionSignatures';
|
|
97
|
+
return async (req)=>{
|
|
98
|
+
const { payload } = req;
|
|
99
|
+
const logger = payload.logger;
|
|
100
|
+
if (!req.json) {
|
|
101
|
+
return errorResponse(ErrorCodes.BAD_REQUEST, 'No JSON body provided', 400);
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const body = await req.json();
|
|
105
|
+
// Validate request body structure
|
|
106
|
+
const parseResult = PetitionSignatureBodySchema.safeParse(body);
|
|
107
|
+
if (!parseResult.success) {
|
|
108
|
+
const firstError = parseResult.error.issues[0]?.message || 'Invalid request body';
|
|
109
|
+
return errorResponse(ErrorCodes.VALIDATION_ERROR, firstError, 400);
|
|
110
|
+
}
|
|
111
|
+
const { data, petitionId } = parseResult.data;
|
|
112
|
+
// Fetch and validate petition
|
|
113
|
+
const petition = await getPublishedPetition(payload, petitionId, petitionsSlug);
|
|
114
|
+
if (!petition) {
|
|
115
|
+
return errorResponse(ErrorCodes.NOT_FOUND, 'Petition not found or not published', 404);
|
|
116
|
+
}
|
|
117
|
+
// Validate signature data against petition fields
|
|
118
|
+
const validation = validateSignatureData(data, petition.contactFields);
|
|
119
|
+
if (!validation.valid) {
|
|
120
|
+
return errorResponse(ErrorCodes.VALIDATION_ERROR, validation.errors.join(', '), 400);
|
|
121
|
+
}
|
|
122
|
+
// Create the petition signature
|
|
123
|
+
// Note: The beforeChange hook will handle contact creation/update
|
|
124
|
+
const signature = await payload.create({
|
|
125
|
+
collection: petitionSignaturesSlug,
|
|
126
|
+
data: {
|
|
127
|
+
data,
|
|
128
|
+
petition: petition.id
|
|
129
|
+
},
|
|
130
|
+
// Use internal context to bypass access control
|
|
131
|
+
overrideAccess: true
|
|
132
|
+
});
|
|
133
|
+
// Build confirmation response
|
|
134
|
+
const confirmation = buildConfirmationResponse(petition);
|
|
135
|
+
return successResponse({
|
|
136
|
+
confirmation,
|
|
137
|
+
signatureId: signature.id
|
|
138
|
+
}, 201);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
logger.error(error, 'Error processing petition signature');
|
|
141
|
+
return errorResponse(ErrorCodes.INTERNAL_ERROR, error instanceof Error ? error.message : 'Failed to process signature', 500);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
//# sourceMappingURL=petitionSignatureHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoints/petitionSignatureHandler.ts"],"sourcesContent":["import type { CollectionSlug, Payload, PayloadHandler } from 'payload'\n\nimport z from 'zod'\n\nimport type { MobilizehubPluginConfig } from '../types/index.js'\n\nimport { ErrorCodes, errorResponse, successResponse } from '../utils/api-response.js'\n\n/**\n * Schema for petition signature request body.\n */\nconst PetitionSignatureBodySchema = z.object({\n data: z.record(z.string(), z.unknown()),\n petitionId: z.union([z.string(), z.number()]),\n})\n\n/**\n * Petition type for validation.\n */\ntype PetitionDocument = {\n confirmationMessage?: unknown\n confirmationType?: 'message' | 'redirect'\n contactFields?: Array<{\n blockType: string\n required?: boolean\n }>\n id: number | string\n reference?: {\n relationTo: 'pages' | 'petitions'\n value: { slug?: string } | number | string\n }\n status?: 'draft' | 'published'\n url?: string\n}\n\n/**\n * Validates signature data against petition field configuration.\n */\nfunction validateSignatureData(\n data: Record<string, unknown>,\n contactFields: PetitionDocument['contactFields'],\n): { errors: string[]; valid: boolean } {\n const errors: string[] = []\n\n if (!contactFields || contactFields.length === 0) {\n return { errors: [], valid: true }\n }\n\n for (const field of contactFields) {\n if (field.required) {\n const value = data[field.blockType]\n if (value === undefined || value === null || value === '') {\n errors.push(`${field.blockType} is required`)\n }\n }\n }\n\n // Validate email format if present\n if (data.email && typeof data.email === 'string') {\n const emailRegex = /^[^\\s@]+@[^\\s@][^\\s.@]*\\.[^\\s@]+$/\n if (!emailRegex.test(data.email)) {\n errors.push('Invalid email format')\n }\n }\n\n return {\n errors,\n valid: errors.length === 0,\n }\n}\n\n/**\n * Fetches and validates a petition exists and is published.\n */\nasync function getPublishedPetition(\n payload: Payload,\n petitionId: number | string,\n collectionSlug: CollectionSlug,\n): Promise<null | PetitionDocument> {\n try {\n const petition = await payload.findByID({\n id: petitionId,\n collection: collectionSlug,\n })\n\n if (!petition) {\n return null\n }\n\n // Only allow signatures to published petitions\n if ((petition as PetitionDocument).status !== 'published') {\n return null\n }\n\n return petition as PetitionDocument\n } catch {\n return null\n }\n}\n\n/**\n * Builds the confirmation response based on petition settings.\n */\nfunction buildConfirmationResponse(petition: PetitionDocument): {\n message?: unknown\n redirect?: string\n type: 'message' | 'redirect'\n} {\n if (petition.confirmationType === 'redirect' && petition.reference) {\n let redirectUrl: string | undefined\n\n if (petition.url) {\n redirectUrl = petition.url\n } else if (typeof petition.reference.value === 'object' && petition.reference.value.slug) {\n redirectUrl = `/${petition.reference.value.slug}`\n }\n\n if (redirectUrl) {\n return { type: 'redirect', redirect: redirectUrl }\n }\n }\n\n return {\n type: 'message',\n message: petition.confirmationMessage || 'Thank you for signing this petition.',\n }\n}\n\n/**\n * Creates the public petition signature endpoint handler.\n *\n * Accepts petition signatures from frontend applications, validates the data,\n * creates a petition signature record, and returns the appropriate confirmation.\n *\n * This endpoint is public (no authentication required) but validates:\n * - Petition exists and is published\n * - Required fields are present\n * - Email format is valid (if provided)\n */\nexport const petitionSignatureHandler = (\n pluginConfig: MobilizehubPluginConfig,\n): PayloadHandler => {\n const petitionsSlug = pluginConfig.petitionsOverrides?.slug || 'petitions'\n const petitionSignaturesSlug =\n pluginConfig.petitionSignaturesOverrides?.slug || 'petitionSignatures'\n\n return async (req) => {\n const { payload } = req\n const logger = payload.logger\n\n if (!req.json) {\n return errorResponse(ErrorCodes.BAD_REQUEST, 'No JSON body provided', 400)\n }\n\n try {\n const body = await req.json()\n\n // Validate request body structure\n const parseResult = PetitionSignatureBodySchema.safeParse(body)\n\n if (!parseResult.success) {\n const firstError = parseResult.error.issues[0]?.message || 'Invalid request body'\n return errorResponse(ErrorCodes.VALIDATION_ERROR, firstError, 400)\n }\n\n const { data, petitionId } = parseResult.data\n\n // Fetch and validate petition\n const petition = await getPublishedPetition(payload, petitionId, petitionsSlug)\n\n if (!petition) {\n return errorResponse(ErrorCodes.NOT_FOUND, 'Petition not found or not published', 404)\n }\n\n // Validate signature data against petition fields\n const validation = validateSignatureData(data, petition.contactFields)\n\n if (!validation.valid) {\n return errorResponse(ErrorCodes.VALIDATION_ERROR, validation.errors.join(', '), 400)\n }\n\n // Create the petition signature\n // Note: The beforeChange hook will handle contact creation/update\n const signature = await payload.create({\n collection: petitionSignaturesSlug,\n data: {\n data,\n petition: petition.id as number,\n },\n // Use internal context to bypass access control\n overrideAccess: true,\n })\n\n // Build confirmation response\n const confirmation = buildConfirmationResponse(petition)\n\n return successResponse(\n {\n confirmation,\n signatureId: signature.id,\n },\n 201,\n )\n } catch (error) {\n logger.error(error as Error, 'Error processing petition signature')\n return errorResponse(\n ErrorCodes.INTERNAL_ERROR,\n error instanceof Error ? error.message : 'Failed to process signature',\n 500,\n )\n }\n }\n}\n"],"names":["z","ErrorCodes","errorResponse","successResponse","PetitionSignatureBodySchema","object","data","record","string","unknown","petitionId","union","number","validateSignatureData","contactFields","errors","length","valid","field","required","value","blockType","undefined","push","email","emailRegex","test","getPublishedPetition","payload","collectionSlug","petition","findByID","id","collection","status","buildConfirmationResponse","confirmationType","reference","redirectUrl","url","slug","type","redirect","message","confirmationMessage","petitionSignatureHandler","pluginConfig","petitionsSlug","petitionsOverrides","petitionSignaturesSlug","petitionSignaturesOverrides","req","logger","json","BAD_REQUEST","body","parseResult","safeParse","success","firstError","error","issues","VALIDATION_ERROR","NOT_FOUND","validation","join","signature","create","overrideAccess","confirmation","signatureId","INTERNAL_ERROR","Error"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array of country options with ISO country codes and official names.
|
|
3
|
+
* Can be used to populate country select inputs on the frontend.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { countries } from '@mobilizehub/payload-plugin/helpers'
|
|
8
|
+
*
|
|
9
|
+
* <select>
|
|
10
|
+
* {countries.map((country) => (
|
|
11
|
+
* <option key={country.value} value={country.value}>
|
|
12
|
+
* {country.label}
|
|
13
|
+
* </option>
|
|
14
|
+
* ))}
|
|
15
|
+
* </select>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const countries: {
|
|
19
|
+
label: string;
|
|
20
|
+
value: string;
|
|
21
|
+
}[];
|
|
22
|
+
export type Country = (typeof countries)[number];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import iso from 'i18n-iso-countries';
|
|
2
|
+
/**
|
|
3
|
+
* Array of country options with ISO country codes and official names.
|
|
4
|
+
* Can be used to populate country select inputs on the frontend.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { countries } from '@mobilizehub/payload-plugin/helpers'
|
|
9
|
+
*
|
|
10
|
+
* <select>
|
|
11
|
+
* {countries.map((country) => (
|
|
12
|
+
* <option key={country.value} value={country.value}>
|
|
13
|
+
* {country.label}
|
|
14
|
+
* </option>
|
|
15
|
+
* ))}
|
|
16
|
+
* </select>
|
|
17
|
+
* ```
|
|
18
|
+
*/ export const countries = Object.entries(iso.getNames('en', {
|
|
19
|
+
select: 'official'
|
|
20
|
+
})).map(([code, name])=>({
|
|
21
|
+
label: name,
|
|
22
|
+
value: code
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=countries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/countries.ts"],"sourcesContent":["import iso from 'i18n-iso-countries'\n\n/**\n * Array of country options with ISO country codes and official names.\n * Can be used to populate country select inputs on the frontend.\n *\n * @example\n * ```tsx\n * import { countries } from '@mobilizehub/payload-plugin/helpers'\n *\n * <select>\n * {countries.map((country) => (\n * <option key={country.value} value={country.value}>\n * {country.label}\n * </option>\n * ))}\n * </select>\n * ```\n */\nexport const countries = Object.entries(iso.getNames('en', { select: 'official' })).map(\n ([code, name]) => ({\n label: name,\n value: code,\n }),\n)\n\nexport type Country = (typeof countries)[number]\n"],"names":["iso","countries","Object","entries","getNames","select","map","code","name","label","value"],"mappings":"AAAA,OAAOA,SAAS,qBAAoB;AAEpC;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,MAAMC,YAAYC,OAAOC,OAAO,CAACH,IAAII,QAAQ,CAAC,MAAM;IAAEC,QAAQ;AAAW,IAAIC,GAAG,CACrF,CAAC,CAACC,MAAMC,KAAK,GAAM,CAAA;QACjBC,OAAOD;QACPE,OAAOH;IACT,CAAA,GACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { countries, type Country } from './countries.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/helpers/index.ts"],"sourcesContent":["export { countries, type Country } from './countries.js'\n"],"names":["countries"],"mappings":"AAAA,SAASA,SAAS,QAAsB,iBAAgB"}
|
package/dist/index.js
CHANGED
|
@@ -4,10 +4,13 @@ import { generateEmailsCollection } from './collections/emails/generateEmailsCol
|
|
|
4
4
|
import { generateFormSubmissionsCollection } from './collections/form-submissions/generateFormSubmissionsCollection.js';
|
|
5
5
|
import { generateFormsCollection } from './collections/forms/generateFormsCollection.js';
|
|
6
6
|
import { generatePagesCollection } from './collections/pages/generatePagesCollection.js';
|
|
7
|
+
import { generatePetitionSignaturesCollection } from './collections/petition-signatures/generatePetitionSignaturesCollection.js';
|
|
8
|
+
import { generatePetitionsCollection } from './collections/petitions/generatePetitionsCollection.js';
|
|
7
9
|
import { generateTagsCollection } from './collections/tags/generateTagsCollection.js';
|
|
8
10
|
import { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js';
|
|
9
11
|
import { emailWebhookHandler } from './endpoints/emailWebhookHandler.js';
|
|
10
12
|
import { formSubmissionHandler } from './endpoints/formSubmissionHandler.js';
|
|
13
|
+
import { petitionSignatureHandler } from './endpoints/petitionSignatureHandler.js';
|
|
11
14
|
import { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js';
|
|
12
15
|
import { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js';
|
|
13
16
|
import { unsubscribeHandler } from './endpoints/unsubscribeHandler.js';
|
|
@@ -18,7 +21,7 @@ export const mobilizehubPlugin = (pluginOptions)=>(config)=>{
|
|
|
18
21
|
if (!config.collections) {
|
|
19
22
|
config.collections = [];
|
|
20
23
|
}
|
|
21
|
-
config.collections.push(generateTagsCollection(pluginOptions), generateContactsCollection(pluginOptions), generateBroadcastsCollection(pluginOptions), generateEmailsCollection(pluginOptions), generatePagesCollection(pluginOptions), generateUnsubscribeTokensCollection(), generateFormSubmissionsCollection(pluginOptions), generateFormsCollection(pluginOptions));
|
|
24
|
+
config.collections.push(generateTagsCollection(pluginOptions), generateContactsCollection(pluginOptions), generateBroadcastsCollection(pluginOptions), generateEmailsCollection(pluginOptions), generatePagesCollection(pluginOptions), generateUnsubscribeTokensCollection(), generateFormSubmissionsCollection(pluginOptions), generateFormsCollection(pluginOptions), generatePetitionSignaturesCollection(pluginOptions), generatePetitionsCollection(pluginOptions));
|
|
22
25
|
if (pluginOptions.disabled) {
|
|
23
26
|
return config;
|
|
24
27
|
}
|
|
@@ -50,6 +53,11 @@ export const mobilizehubPlugin = (pluginOptions)=>(config)=>{
|
|
|
50
53
|
handler: formSubmissionHandler(pluginOptions),
|
|
51
54
|
method: 'post',
|
|
52
55
|
path: '/forms.createSubmission'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
handler: petitionSignatureHandler(pluginOptions),
|
|
59
|
+
method: 'post',
|
|
60
|
+
path: '/petitions.createSignature'
|
|
53
61
|
}
|
|
54
62
|
];
|
|
55
63
|
config.endpoints = [
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generateFormSubmissionsCollection } from './collections/form-submissions/generateFormSubmissionsCollection.js'\nimport { generateFormsCollection } from './collections/forms/generateFormsCollection.js'\nimport { generatePagesCollection } from './collections/pages/generatePagesCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { emailWebhookHandler } from './endpoints/emailWebhookHandler.js'\nimport { formSubmissionHandler } from './endpoints/formSubmissionHandler.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generatePagesCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n generateFormSubmissionsCollection(pluginOptions),\n generateFormsCollection(pluginOptions),\n )\n\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n {\n handler: emailWebhookHandler(pluginOptions),\n method: 'post',\n path: '/webhooks/email',\n },\n {\n handler: formSubmissionHandler(pluginOptions),\n method: 'post',\n path: '/forms.createSubmission',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generateFormSubmissionsCollection","generateFormsCollection","generatePagesCollection","generateTagsCollection","generateUnsubscribeTokensCollection","emailWebhookHandler","formSubmissionHandler","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","collections","push","disabled","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,iCAAiC,QAAQ,sEAAqE;AACvH,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,mBAAmB,QAAQ,qCAAoC;AACxE,SAASC,qBAAqB,QAAQ,uCAAsC;AAC5E,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAI,CAACA,OAAOC,WAAW,EAAE;YACvBD,OAAOC,WAAW,GAAG,EAAE;QACzB;QAEAD,OAAOC,WAAW,CAACC,IAAI,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { Config, Endpoint, TaskConfig } from 'payload'\n\nimport type { MobilizehubPluginConfig } from './types/index.js'\n\nimport { generateBroadcastsCollection } from './collections/broadcasts/generateBroadcastsCollection.js'\nimport { generateContactsCollection } from './collections/contacts/generateContactsCollection.js'\nimport { generateEmailsCollection } from './collections/emails/generateEmailsCollection.js'\nimport { generateFormSubmissionsCollection } from './collections/form-submissions/generateFormSubmissionsCollection.js'\nimport { generateFormsCollection } from './collections/forms/generateFormsCollection.js'\nimport { generatePagesCollection } from './collections/pages/generatePagesCollection.js'\nimport { generatePetitionSignaturesCollection } from './collections/petition-signatures/generatePetitionSignaturesCollection.js'\nimport { generatePetitionsCollection } from './collections/petitions/generatePetitionsCollection.js'\nimport { generateTagsCollection } from './collections/tags/generateTagsCollection.js'\nimport { generateUnsubscribeTokensCollection } from './collections/unsubscribe-tokens/generateUnsubscribeTokens.js'\nimport { emailWebhookHandler } from './endpoints/emailWebhookHandler.js'\nimport { formSubmissionHandler } from './endpoints/formSubmissionHandler.js'\nimport { petitionSignatureHandler } from './endpoints/petitionSignatureHandler.js'\nimport { sendBroadcastHandler } from './endpoints/sendBroadcastHandler.js'\nimport { sendTestEmailHandler } from './endpoints/sendTestBroadcastHandler.js'\nimport { unsubscribeHandler } from './endpoints/unsubscribeHandler.js'\nimport { createSendBroadcastsTask } from './tasks/sendBroadcastsTask.js'\nimport { createSendEmailTask } from './tasks/sendEmailTask.js'\n\nexport * from './types/index.js'\n\nexport const mobilizehubPlugin =\n (pluginOptions: MobilizehubPluginConfig) =>\n (config: Config): Config => {\n if (!config.collections) {\n config.collections = []\n }\n\n config.collections.push(\n generateTagsCollection(pluginOptions),\n generateContactsCollection(pluginOptions),\n generateBroadcastsCollection(pluginOptions),\n generateEmailsCollection(pluginOptions),\n generatePagesCollection(pluginOptions),\n generateUnsubscribeTokensCollection(),\n generateFormSubmissionsCollection(pluginOptions),\n generateFormsCollection(pluginOptions),\n generatePetitionSignaturesCollection(pluginOptions),\n generatePetitionsCollection(pluginOptions),\n )\n\n if (pluginOptions.disabled) {\n return config\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n const endpoints: Endpoint[] = [\n {\n handler: sendBroadcastHandler(),\n method: 'post',\n path: '/send-broadcast',\n },\n {\n handler: sendTestEmailHandler(pluginOptions),\n method: 'post',\n path: '/send-test-email',\n },\n {\n handler: unsubscribeHandler(),\n method: 'post',\n path: '/unsubscribe',\n },\n {\n handler: emailWebhookHandler(pluginOptions),\n method: 'post',\n path: '/webhooks/email',\n },\n {\n handler: formSubmissionHandler(pluginOptions),\n method: 'post',\n path: '/forms.createSubmission',\n },\n {\n handler: petitionSignatureHandler(pluginOptions),\n method: 'post',\n path: '/petitions.createSignature',\n },\n ]\n\n config.endpoints = [...config.endpoints, ...endpoints]\n\n if (!config.jobs) {\n config.jobs = {\n tasks: [],\n }\n }\n\n const tasks: TaskConfig[] = [\n createSendBroadcastsTask(pluginOptions),\n createSendEmailTask(pluginOptions),\n ]\n\n config.jobs.tasks = [...(config.jobs.tasks ?? []), ...tasks]\n\n const incomingOnInit = config.onInit\n\n config.onInit = async (payload) => {\n if (incomingOnInit) {\n await incomingOnInit(payload)\n }\n }\n\n return config\n }\n"],"names":["generateBroadcastsCollection","generateContactsCollection","generateEmailsCollection","generateFormSubmissionsCollection","generateFormsCollection","generatePagesCollection","generatePetitionSignaturesCollection","generatePetitionsCollection","generateTagsCollection","generateUnsubscribeTokensCollection","emailWebhookHandler","formSubmissionHandler","petitionSignatureHandler","sendBroadcastHandler","sendTestEmailHandler","unsubscribeHandler","createSendBroadcastsTask","createSendEmailTask","mobilizehubPlugin","pluginOptions","config","collections","push","disabled","endpoints","handler","method","path","jobs","tasks","incomingOnInit","onInit","payload"],"mappings":"AAIA,SAASA,4BAA4B,QAAQ,2DAA0D;AACvG,SAASC,0BAA0B,QAAQ,uDAAsD;AACjG,SAASC,wBAAwB,QAAQ,mDAAkD;AAC3F,SAASC,iCAAiC,QAAQ,sEAAqE;AACvH,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,uBAAuB,QAAQ,iDAAgD;AACxF,SAASC,oCAAoC,QAAQ,4EAA2E;AAChI,SAASC,2BAA2B,QAAQ,yDAAwD;AACpG,SAASC,sBAAsB,QAAQ,+CAA8C;AACrF,SAASC,mCAAmC,QAAQ,gEAA+D;AACnH,SAASC,mBAAmB,QAAQ,qCAAoC;AACxE,SAASC,qBAAqB,QAAQ,uCAAsC;AAC5E,SAASC,wBAAwB,QAAQ,0CAAyC;AAClF,SAASC,oBAAoB,QAAQ,sCAAqC;AAC1E,SAASC,oBAAoB,QAAQ,0CAAyC;AAC9E,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,wBAAwB,QAAQ,gCAA+B;AACxE,SAASC,mBAAmB,QAAQ,2BAA0B;AAE9D,cAAc,mBAAkB;AAEhC,OAAO,MAAMC,oBACX,CAACC,gBACD,CAACC;QACC,IAAI,CAACA,OAAOC,WAAW,EAAE;YACvBD,OAAOC,WAAW,GAAG,EAAE;QACzB;QAEAD,OAAOC,WAAW,CAACC,IAAI,CACrBd,uBAAuBW,gBACvBlB,2BAA2BkB,gBAC3BnB,6BAA6BmB,gBAC7BjB,yBAAyBiB,gBACzBd,wBAAwBc,gBACxBV,uCACAN,kCAAkCgB,gBAClCf,wBAAwBe,gBACxBb,qCAAqCa,gBACrCZ,4BAA4BY;QAG9B,IAAIA,cAAcI,QAAQ,EAAE;YAC1B,OAAOH;QACT;QAEA,IAAI,CAACA,OAAOI,SAAS,EAAE;YACrBJ,OAAOI,SAAS,GAAG,EAAE;QACvB;QAEA,MAAMA,YAAwB;YAC5B;gBACEC,SAASZ;gBACTa,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASX,qBAAqBK;gBAC9BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASV;gBACTW,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASf,oBAAoBS;gBAC7BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASd,sBAAsBQ;gBAC/BO,QAAQ;gBACRC,MAAM;YACR;YACA;gBACEF,SAASb,yBAAyBO;gBAClCO,QAAQ;gBACRC,MAAM;YACR;SACD;QAEDP,OAAOI,SAAS,GAAG;eAAIJ,OAAOI,SAAS;eAAKA;SAAU;QAEtD,IAAI,CAACJ,OAAOQ,IAAI,EAAE;YAChBR,OAAOQ,IAAI,GAAG;gBACZC,OAAO,EAAE;YACX;QACF;QAEA,MAAMA,QAAsB;YAC1Bb,yBAAyBG;YACzBF,oBAAoBE;SACrB;QAEDC,OAAOQ,IAAI,CAACC,KAAK,GAAG;eAAKT,OAAOQ,IAAI,CAACC,KAAK,IAAI,EAAE;eAAMA;SAAM;QAE5D,MAAMC,iBAAiBV,OAAOW,MAAM;QAEpCX,OAAOW,MAAM,GAAG,OAAOC;YACrB,IAAIF,gBAAgB;gBAClB,MAAMA,eAAeE;YACvB;QACF;QAEA,OAAOZ;IACT,EAAC"}
|
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.ts"],"sourcesContent":["export { submitForm } from './submit-form.js'\nexport { confirmUnsubscribe } from './unsubscribe.js'\n"],"names":["submitForm","confirmUnsubscribe"],"mappings":"AAAA,SAASA,UAAU,QAAQ,mBAAkB;AAC7C,SAASC,kBAAkB,QAAQ,mBAAkB"}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts"],"sourcesContent":["export { signPetition } from './sign-petition.js'\nexport { submitForm } from './submit-form.js'\nexport { confirmUnsubscribe } from './unsubscribe.js'\n"],"names":["signPetition","submitForm","confirmUnsubscribe"],"mappings":"AAAA,SAASA,YAAY,QAAQ,qBAAoB;AACjD,SAASC,UAAU,QAAQ,mBAAkB;AAC7C,SAASC,kBAAkB,QAAQ,mBAAkB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for handling petition signature responses.
|
|
3
|
+
*/
|
|
4
|
+
type SignPetitionOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Called when the petition returns a message confirmation.
|
|
7
|
+
* @param message - The confirmation message (may be a RichText object)
|
|
8
|
+
*/
|
|
9
|
+
onMessage?: (message: unknown) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Called when the petition should redirect after signing.
|
|
12
|
+
* @param redirect - The URL to redirect to
|
|
13
|
+
*/
|
|
14
|
+
onRedirect?: (redirect: string) => void;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Signs a petition via the backend API.
|
|
18
|
+
*
|
|
19
|
+
* @param args - Petition signature arguments
|
|
20
|
+
* @param args.petitionId - The ID of the petition to sign
|
|
21
|
+
* @param args.data - The signature data (contact info)
|
|
22
|
+
* @param args.opts - Optional callbacks for handling the response
|
|
23
|
+
* @returns The signature data including confirmation and signatureId
|
|
24
|
+
* @throws Error if the signature fails
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* const handleSign = async (formData: Record<string, unknown>) => {
|
|
29
|
+
* try {
|
|
30
|
+
* const result = await signPetition({
|
|
31
|
+
* petitionId: '1',
|
|
32
|
+
* data: formData,
|
|
33
|
+
* opts: {
|
|
34
|
+
* onRedirect: (url) => router.push(url),
|
|
35
|
+
* onMessage: (message) => setConfirmation(message),
|
|
36
|
+
* },
|
|
37
|
+
* })
|
|
38
|
+
* console.log('Signature ID:', result.signatureId)
|
|
39
|
+
* } catch (error) {
|
|
40
|
+
* console.error('Petition signature failed:', error)
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function signPetition(args: {
|
|
46
|
+
data: Record<string, unknown>;
|
|
47
|
+
opts?: SignPetitionOptions;
|
|
48
|
+
petitionId: number | string;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
confirmation: {
|
|
51
|
+
message?: unknown;
|
|
52
|
+
redirect?: string;
|
|
53
|
+
type: "message" | "redirect";
|
|
54
|
+
};
|
|
55
|
+
signatureId: number | string;
|
|
56
|
+
} | undefined>;
|
|
57
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const apiUrl = '/api/petitions.createSignature';
|
|
2
|
+
/**
|
|
3
|
+
* Signs a petition via the backend API.
|
|
4
|
+
*
|
|
5
|
+
* @param args - Petition signature arguments
|
|
6
|
+
* @param args.petitionId - The ID of the petition to sign
|
|
7
|
+
* @param args.data - The signature data (contact info)
|
|
8
|
+
* @param args.opts - Optional callbacks for handling the response
|
|
9
|
+
* @returns The signature data including confirmation and signatureId
|
|
10
|
+
* @throws Error if the signature fails
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const handleSign = async (formData: Record<string, unknown>) => {
|
|
15
|
+
* try {
|
|
16
|
+
* const result = await signPetition({
|
|
17
|
+
* petitionId: '1',
|
|
18
|
+
* data: formData,
|
|
19
|
+
* opts: {
|
|
20
|
+
* onRedirect: (url) => router.push(url),
|
|
21
|
+
* onMessage: (message) => setConfirmation(message),
|
|
22
|
+
* },
|
|
23
|
+
* })
|
|
24
|
+
* console.log('Signature ID:', result.signatureId)
|
|
25
|
+
* } catch (error) {
|
|
26
|
+
* console.error('Petition signature failed:', error)
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/ export async function signPetition(args) {
|
|
31
|
+
const response = await fetch(apiUrl, {
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
data: args.data,
|
|
34
|
+
petitionId: args.petitionId
|
|
35
|
+
}),
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
},
|
|
39
|
+
method: 'POST'
|
|
40
|
+
});
|
|
41
|
+
const result = await response.json();
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
throw new Error(result.error?.message || 'Signature failed');
|
|
44
|
+
}
|
|
45
|
+
if (result.data?.confirmation.type === 'redirect' && result.data.confirmation.redirect) {
|
|
46
|
+
args.opts?.onRedirect?.(result.data.confirmation.redirect);
|
|
47
|
+
}
|
|
48
|
+
if (result.data?.confirmation.type === 'message') {
|
|
49
|
+
args.opts?.onMessage?.(result.data.confirmation.message);
|
|
50
|
+
}
|
|
51
|
+
return result.data;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//# sourceMappingURL=sign-petition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/sign-petition.ts"],"sourcesContent":["const apiUrl = '/api/petitions.createSignature'\n\n/**\n * Response type from the petition signature API.\n */\ntype PetitionSignatureResponse = {\n data?: {\n confirmation: {\n message?: unknown\n redirect?: string\n type: 'message' | 'redirect'\n }\n signatureId: number | string\n }\n error?: {\n code: string\n message: string\n }\n success: boolean\n}\n\n/**\n * Options for handling petition signature responses.\n */\ntype SignPetitionOptions = {\n /**\n * Called when the petition returns a message confirmation.\n * @param message - The confirmation message (may be a RichText object)\n */\n onMessage?: (message: unknown) => void\n /**\n * Called when the petition should redirect after signing.\n * @param redirect - The URL to redirect to\n */\n onRedirect?: (redirect: string) => void\n}\n\n/**\n * Signs a petition via the backend API.\n *\n * @param args - Petition signature arguments\n * @param args.petitionId - The ID of the petition to sign\n * @param args.data - The signature data (contact info)\n * @param args.opts - Optional callbacks for handling the response\n * @returns The signature data including confirmation and signatureId\n * @throws Error if the signature fails\n *\n * @example\n * ```tsx\n * const handleSign = async (formData: Record<string, unknown>) => {\n * try {\n * const result = await signPetition({\n * petitionId: '1',\n * data: formData,\n * opts: {\n * onRedirect: (url) => router.push(url),\n * onMessage: (message) => setConfirmation(message),\n * },\n * })\n * console.log('Signature ID:', result.signatureId)\n * } catch (error) {\n * console.error('Petition signature failed:', error)\n * }\n * }\n * ```\n */\nexport async function signPetition(args: {\n data: Record<string, unknown>\n opts?: SignPetitionOptions\n petitionId: number | string\n}) {\n const response = await fetch(apiUrl, {\n body: JSON.stringify({ data: args.data, petitionId: args.petitionId }),\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n })\n\n const result: PetitionSignatureResponse = await response.json()\n\n if (!result.success) {\n throw new Error(result.error?.message || 'Signature failed')\n }\n\n if (result.data?.confirmation.type === 'redirect' && result.data.confirmation.redirect) {\n args.opts?.onRedirect?.(result.data.confirmation.redirect)\n }\n\n if (result.data?.confirmation.type === 'message') {\n args.opts?.onMessage?.(result.data.confirmation.message)\n }\n\n return result.data\n}\n"],"names":["apiUrl","signPetition","args","response","fetch","body","JSON","stringify","data","petitionId","headers","method","result","json","success","Error","error","message","confirmation","type","redirect","opts","onRedirect","onMessage"],"mappings":"AAAA,MAAMA,SAAS;AAqCf;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,aAAaC,IAIlC;IACC,MAAMC,WAAW,MAAMC,MAAMJ,QAAQ;QACnCK,MAAMC,KAAKC,SAAS,CAAC;YAAEC,MAAMN,KAAKM,IAAI;YAAEC,YAAYP,KAAKO,UAAU;QAAC;QACpEC,SAAS;YACP,gBAAgB;QAClB;QACAC,QAAQ;IACV;IAEA,MAAMC,SAAoC,MAAMT,SAASU,IAAI;IAE7D,IAAI,CAACD,OAAOE,OAAO,EAAE;QACnB,MAAM,IAAIC,MAAMH,OAAOI,KAAK,EAAEC,WAAW;IAC3C;IAEA,IAAIL,OAAOJ,IAAI,EAAEU,aAAaC,SAAS,cAAcP,OAAOJ,IAAI,CAACU,YAAY,CAACE,QAAQ,EAAE;QACtFlB,KAAKmB,IAAI,EAAEC,aAAaV,OAAOJ,IAAI,CAACU,YAAY,CAACE,QAAQ;IAC3D;IAEA,IAAIR,OAAOJ,IAAI,EAAEU,aAAaC,SAAS,WAAW;QAChDjB,KAAKmB,IAAI,EAAEE,YAAYX,OAAOJ,IAAI,CAACU,YAAY,CAACD,OAAO;IACzD;IAEA,OAAOL,OAAOJ,IAAI;AACpB"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -148,6 +148,14 @@ export type MobilizehubPluginConfig = {
|
|
|
148
148
|
pagesOverrides?: {
|
|
149
149
|
blocks?: BlocksOverride;
|
|
150
150
|
} & CollectionOverride;
|
|
151
|
+
/**
|
|
152
|
+
* Overrides for the petition signatures collection
|
|
153
|
+
*/
|
|
154
|
+
petitionSignaturesOverrides?: CollectionOverride;
|
|
155
|
+
/**
|
|
156
|
+
* Overrides for the petitions collection
|
|
157
|
+
*/
|
|
158
|
+
petitionsOverrides?: CollectionOverride;
|
|
151
159
|
/**
|
|
152
160
|
* Overrides for the tags collection
|
|
153
161
|
*/
|
package/dist/types/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["import type { BasePayload, Block, CollectionConfig, Field, PayloadRequest } from 'payload'\n\nexport type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\nexport type CollectionOverride = { fields?: FieldsOverride } & Partial<\n Omit<CollectionConfig, 'fields'>\n>\n\nexport type BlocksOverride = (args: { defaultBlocks: Block[] }) => Block[]\n\n/**\n * Contact type\n */\nexport type Contact = {\n createdAt?: string\n email?: string\n emailOptIn: boolean\n firstName?: string\n id: number | string\n lastName?: string\n tags?: {\n createdAt?: string\n id: number | string\n name?: string\n updatedAt?: string\n }[]\n updatedAt?: string\n}\n\n/**\n * Unsubscribe token input structure\n */\nexport interface UnsubscribeTokenInput {\n timestamp: number\n tokenId: string\n}\n\n/**\n * Unsubscribe token record structure\n */\nexport type UnsubscribeTokenRecord = {\n emailId?: number | string\n expiresAt?: string\n id: string\n}\n\n/**\n * Email activity types\n */\nexport type EmailStatus =\n | 'bounced'\n | 'complained'\n | 'delivered'\n | 'failed'\n | 'queued'\n | 'sent'\n | 'unsubscribed'\n\n/**\n * Email activity types from providers\n */\nexport type EmailActivityType =\n | 'bounced'\n | 'clicked'\n | 'complained'\n | 'delivered'\n | 'delivery_delayed'\n | 'failed'\n | 'opened'\n | 'received'\n | 'sent'\n\n/**\n * Email message structure\n */\nexport type EmailMessage = {\n from: string\n html: string\n idempotencyKey?: string\n markdown?: string\n plainText?: string\n previewText?: string\n replyTo?: string\n subject: string\n to: string\n token?: string\n}\n\n/**\n * Result of a webhook call\n */\nexport type WebhookResult = {\n body?: unknown\n status: number\n}\n\n/**\n * Email adapter interface for sending emails\n */\nexport type EmailAdapter = ({ payload }: { payload: BasePayload }) => {\n defaultFromAddress: string\n defaultFromName: string\n name: string\n render: (args: EmailMessage) => string\n sendEmail: (args: EmailMessage) => Promise<{ providerId: string } | void>\n webhookHandler?: (req: PayloadRequest) => Promise<void | WebhookResult>\n}\n\nexport type MobilizehubPluginConfig = {\n /**\n * Broadcasts task configuration\n */\n broadcastConfig?: {\n /**\n * Batch size for processing contacts in the broadcasts task.\n * Higher values process faster but use more memory.\n * @default 100\n */\n batchSize?: number\n /**\n * Optional custom queue name for the broadcasts task\n * @default 'send-broadcasts'\n */\n broadcastQueueName?: string\n /**\n * Optional custom queue name for the email sending task\n * @default 'send-email'\n */\n emailQueueName?: string\n /**\n * Cron schedule for the broadcasts task\n * On schedule the task will run to process and send pending broadcasts\n * @default '5 * * * *' (every 5 minutes)\n */\n taskSchedule?: string\n }\n /**\n * Overrides for the broadcasts collection\n */\n broadcastsOverrides?: CollectionOverride\n /**\n * Overrides for the contacts collection\n */\n contactsOverrides?: CollectionOverride\n /**\n * Disable the plugin\n */\n disabled?: boolean\n /**\n * Email adapter for sending emails\n */\n email: EmailAdapter\n /**\n * Overrides for the emails collection\n */\n emailsOverrides?: CollectionOverride\n /**\n * Overrides for the forms collection\n */\n formsOverrides?: CollectionOverride\n /**\n * Overrides for the form submissions collection\n */\n formSubmissionsOverrides?: CollectionOverride\n /**\n * Overrides for the pages collection\n */\n pagesOverrides?: { blocks?: BlocksOverride } & CollectionOverride\n /**\n * Overrides for the tags collection\n */\n tagsOverrides?: CollectionOverride\n}\n"],"names":[],"mappings":"AA4GA,
|
|
1
|
+
{"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["import type { BasePayload, Block, CollectionConfig, Field, PayloadRequest } from 'payload'\n\nexport type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]\n\nexport type CollectionOverride = { fields?: FieldsOverride } & Partial<\n Omit<CollectionConfig, 'fields'>\n>\n\nexport type BlocksOverride = (args: { defaultBlocks: Block[] }) => Block[]\n\n/**\n * Contact type\n */\nexport type Contact = {\n createdAt?: string\n email?: string\n emailOptIn: boolean\n firstName?: string\n id: number | string\n lastName?: string\n tags?: {\n createdAt?: string\n id: number | string\n name?: string\n updatedAt?: string\n }[]\n updatedAt?: string\n}\n\n/**\n * Unsubscribe token input structure\n */\nexport interface UnsubscribeTokenInput {\n timestamp: number\n tokenId: string\n}\n\n/**\n * Unsubscribe token record structure\n */\nexport type UnsubscribeTokenRecord = {\n emailId?: number | string\n expiresAt?: string\n id: string\n}\n\n/**\n * Email activity types\n */\nexport type EmailStatus =\n | 'bounced'\n | 'complained'\n | 'delivered'\n | 'failed'\n | 'queued'\n | 'sent'\n | 'unsubscribed'\n\n/**\n * Email activity types from providers\n */\nexport type EmailActivityType =\n | 'bounced'\n | 'clicked'\n | 'complained'\n | 'delivered'\n | 'delivery_delayed'\n | 'failed'\n | 'opened'\n | 'received'\n | 'sent'\n\n/**\n * Email message structure\n */\nexport type EmailMessage = {\n from: string\n html: string\n idempotencyKey?: string\n markdown?: string\n plainText?: string\n previewText?: string\n replyTo?: string\n subject: string\n to: string\n token?: string\n}\n\n/**\n * Result of a webhook call\n */\nexport type WebhookResult = {\n body?: unknown\n status: number\n}\n\n/**\n * Email adapter interface for sending emails\n */\nexport type EmailAdapter = ({ payload }: { payload: BasePayload }) => {\n defaultFromAddress: string\n defaultFromName: string\n name: string\n render: (args: EmailMessage) => string\n sendEmail: (args: EmailMessage) => Promise<{ providerId: string } | void>\n webhookHandler?: (req: PayloadRequest) => Promise<void | WebhookResult>\n}\n\nexport type MobilizehubPluginConfig = {\n /**\n * Broadcasts task configuration\n */\n broadcastConfig?: {\n /**\n * Batch size for processing contacts in the broadcasts task.\n * Higher values process faster but use more memory.\n * @default 100\n */\n batchSize?: number\n /**\n * Optional custom queue name for the broadcasts task\n * @default 'send-broadcasts'\n */\n broadcastQueueName?: string\n /**\n * Optional custom queue name for the email sending task\n * @default 'send-email'\n */\n emailQueueName?: string\n /**\n * Cron schedule for the broadcasts task\n * On schedule the task will run to process and send pending broadcasts\n * @default '5 * * * *' (every 5 minutes)\n */\n taskSchedule?: string\n }\n /**\n * Overrides for the broadcasts collection\n */\n broadcastsOverrides?: CollectionOverride\n /**\n * Overrides for the contacts collection\n */\n contactsOverrides?: CollectionOverride\n /**\n * Disable the plugin\n */\n disabled?: boolean\n /**\n * Email adapter for sending emails\n */\n email: EmailAdapter\n /**\n * Overrides for the emails collection\n */\n emailsOverrides?: CollectionOverride\n /**\n * Overrides for the forms collection\n */\n formsOverrides?: CollectionOverride\n /**\n * Overrides for the form submissions collection\n */\n formSubmissionsOverrides?: CollectionOverride\n /**\n * Overrides for the pages collection\n */\n pagesOverrides?: { blocks?: BlocksOverride } & CollectionOverride\n /**\n * Overrides for the petition signatures collection\n */\n petitionSignaturesOverrides?: CollectionOverride\n /**\n * Overrides for the petitions collection\n */\n petitionsOverrides?: CollectionOverride\n /**\n * Overrides for the tags collection\n */\n tagsOverrides?: CollectionOverride\n}\n"],"names":[],"mappings":"AA4GA,WAwEC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobilizehub/payload-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Edvocacy plugin for Payload",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
"import": "./dist/adapters/index.js",
|
|
31
31
|
"types": "./dist/adapters/index.d.ts",
|
|
32
32
|
"default": "./dist/adapters/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./helpers": {
|
|
35
|
+
"import": "./dist/helpers/index.js",
|
|
36
|
+
"types": "./dist/helpers/index.d.ts",
|
|
37
|
+
"default": "./dist/helpers/index.js"
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"main": "./dist/index.js",
|