@rachelallyson/planning-center-people-ts 1.0.0 → 2.0.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/CHANGELOG.md +248 -1
- package/README.md +28 -0
- package/dist/auth.d.ts +64 -0
- package/dist/auth.js +98 -0
- package/dist/batch.d.ts +47 -0
- package/dist/batch.js +376 -0
- package/dist/client-manager.d.ts +66 -0
- package/dist/client-manager.js +150 -0
- package/dist/client.d.ts +71 -0
- package/dist/client.js +123 -0
- package/dist/core/http.d.ts +47 -0
- package/dist/core/http.js +242 -0
- package/dist/core/pagination.d.ts +34 -0
- package/dist/core/pagination.js +164 -0
- package/dist/core.d.ts +5 -0
- package/dist/core.js +12 -0
- package/dist/helpers.d.ts +125 -100
- package/dist/helpers.js +315 -275
- package/dist/index.d.ts +17 -5
- package/dist/index.js +39 -5
- package/dist/matching/matcher.d.ts +41 -0
- package/dist/matching/matcher.js +161 -0
- package/dist/matching/scoring.d.ts +35 -0
- package/dist/matching/scoring.js +141 -0
- package/dist/matching/strategies.d.ts +35 -0
- package/dist/matching/strategies.js +79 -0
- package/dist/modules/base.d.ts +46 -0
- package/dist/modules/base.js +82 -0
- package/dist/modules/contacts.d.ts +103 -0
- package/dist/modules/contacts.js +130 -0
- package/dist/modules/fields.d.ts +157 -0
- package/dist/modules/fields.js +294 -0
- package/dist/modules/households.d.ts +42 -0
- package/dist/modules/households.js +74 -0
- package/dist/modules/lists.d.ts +62 -0
- package/dist/modules/lists.js +92 -0
- package/dist/modules/notes.d.ts +74 -0
- package/dist/modules/notes.js +125 -0
- package/dist/modules/people.d.ts +196 -0
- package/dist/modules/people.js +221 -0
- package/dist/modules/workflows.d.ts +131 -0
- package/dist/modules/workflows.js +221 -0
- package/dist/monitoring.d.ts +53 -0
- package/dist/monitoring.js +142 -0
- package/dist/people/contacts.d.ts +43 -0
- package/dist/people/contacts.js +122 -0
- package/dist/people/core.d.ts +28 -0
- package/dist/people/core.js +69 -0
- package/dist/people/fields.d.ts +62 -0
- package/dist/people/fields.js +293 -0
- package/dist/people/households.d.ts +15 -0
- package/dist/people/households.js +31 -0
- package/dist/people/index.d.ts +8 -0
- package/dist/people/index.js +25 -0
- package/dist/people/lists.d.ts +30 -0
- package/dist/people/lists.js +37 -0
- package/dist/people/notes.d.ts +30 -0
- package/dist/people/notes.js +37 -0
- package/dist/people/organization.d.ts +12 -0
- package/dist/people/organization.js +15 -0
- package/dist/people/workflows.d.ts +37 -0
- package/dist/people/workflows.js +75 -0
- package/dist/testing/index.d.ts +9 -0
- package/dist/testing/index.js +24 -0
- package/dist/testing/recorder.d.ts +58 -0
- package/dist/testing/recorder.js +195 -0
- package/dist/testing/simple-builders.d.ts +33 -0
- package/dist/testing/simple-builders.js +124 -0
- package/dist/testing/simple-factories.d.ts +91 -0
- package/dist/testing/simple-factories.js +279 -0
- package/dist/testing/types.d.ts +160 -0
- package/dist/testing/types.js +5 -0
- package/dist/types/batch.d.ts +50 -0
- package/dist/types/batch.js +5 -0
- package/dist/types/client.d.ts +81 -0
- package/dist/types/client.js +5 -0
- package/dist/types/events.d.ts +85 -0
- package/dist/types/events.js +5 -0
- package/dist/types/people.d.ts +73 -79
- package/package.json +14 -3
- package/dist/people.d.ts +0 -205
- package/dist/people.js +0 -598
package/dist/helpers.js
CHANGED
|
@@ -1,359 +1,399 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Helper Functions for Common PCO People API Operations
|
|
4
|
-
*
|
|
5
|
-
* This module provides convenient helper functions for common operations
|
|
6
|
-
* that combine multiple API calls or provide higher-level abstractions.
|
|
7
|
-
*/
|
|
8
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
3
|
+
exports.buildQueryParams = buildQueryParams;
|
|
4
|
+
exports.calculateAge = calculateAge;
|
|
5
|
+
exports.isValidEmail = isValidEmail;
|
|
6
|
+
exports.isValidPhone = isValidPhone;
|
|
7
|
+
exports.formatPersonName = formatPersonName;
|
|
8
|
+
exports.formatDate = formatDate;
|
|
9
|
+
exports.validatePersonData = validatePersonData;
|
|
10
|
+
exports.getPrimaryContact = getPrimaryContact;
|
|
10
11
|
exports.createPersonWithContact = createPersonWithContact;
|
|
11
12
|
exports.searchPeople = searchPeople;
|
|
12
13
|
exports.getPeopleByHousehold = getPeopleByHousehold;
|
|
14
|
+
exports.getCompletePersonProfile = getCompletePersonProfile;
|
|
15
|
+
exports.getOrganizationInfo = getOrganizationInfo;
|
|
16
|
+
exports.getListsWithCategories = getListsWithCategories;
|
|
13
17
|
exports.getPersonWorkflowCardsWithNotes = getPersonWorkflowCardsWithNotes;
|
|
14
18
|
exports.createWorkflowCardWithNote = createWorkflowCardWithNote;
|
|
15
|
-
exports.getListsWithCategories = getListsWithCategories;
|
|
16
|
-
exports.getOrganizationInfo = getOrganizationInfo;
|
|
17
19
|
exports.exportAllPeopleData = exportAllPeopleData;
|
|
18
|
-
exports.
|
|
19
|
-
exports.
|
|
20
|
-
exports.
|
|
21
|
-
exports.
|
|
22
|
-
exports.
|
|
23
|
-
exports.
|
|
24
|
-
exports.formatDate = formatDate;
|
|
20
|
+
exports.extractFileUrl = extractFileUrl;
|
|
21
|
+
exports.isFileUrl = isFileUrl;
|
|
22
|
+
exports.getFileExtension = getFileExtension;
|
|
23
|
+
exports.getFilename = getFilename;
|
|
24
|
+
exports.isFileUpload = isFileUpload;
|
|
25
|
+
exports.processFileValue = processFileValue;
|
|
25
26
|
const people_1 = require("./people");
|
|
26
|
-
// ===== Person Management Helpers =====
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* Transform complex params object into flat query params for API calls
|
|
29
29
|
*/
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
(0, people_1.getPersonSocialProfiles)(client, personId),
|
|
37
|
-
(0, people_1.getPersonFieldData)(client, personId),
|
|
38
|
-
(0, people_1.getWorkflowCards)(client, personId),
|
|
39
|
-
(0, people_1.getNotes)(client, { where: { person_id: personId } }),
|
|
40
|
-
]);
|
|
41
|
-
if (!personResponse.data) {
|
|
42
|
-
throw new Error(`Person with ID ${personId} not found`);
|
|
30
|
+
function buildQueryParams(params) {
|
|
31
|
+
const queryParams = {};
|
|
32
|
+
if (params?.where) {
|
|
33
|
+
Object.entries(params.where).forEach(([key, value]) => {
|
|
34
|
+
queryParams[`where[${key}]`] = value;
|
|
35
|
+
});
|
|
43
36
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
if (params?.include) {
|
|
38
|
+
queryParams.include = params.include.join(',');
|
|
39
|
+
}
|
|
40
|
+
if (params?.per_page) {
|
|
41
|
+
queryParams.per_page = params.per_page;
|
|
42
|
+
}
|
|
43
|
+
if (params?.page) {
|
|
44
|
+
queryParams.page = params.page;
|
|
45
|
+
}
|
|
46
|
+
if (params?.filter) {
|
|
47
|
+
queryParams.filter = params.filter;
|
|
48
|
+
}
|
|
49
|
+
return queryParams;
|
|
54
50
|
}
|
|
55
51
|
/**
|
|
56
|
-
*
|
|
52
|
+
* Calculate age from birthdate string
|
|
57
53
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
throw new Error('Failed to create person');
|
|
54
|
+
function calculateAge(birthdate) {
|
|
55
|
+
const birth = new Date(birthdate);
|
|
56
|
+
const today = new Date();
|
|
57
|
+
let age = today.getFullYear() - birth.getFullYear();
|
|
58
|
+
const monthDiff = today.getMonth() - birth.getMonth();
|
|
59
|
+
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
|
60
|
+
age--;
|
|
66
61
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
return age;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Validate email format
|
|
66
|
+
*/
|
|
67
|
+
function isValidEmail(email) {
|
|
68
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
69
|
+
return emailRegex.test(email);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Validate phone number format (basic validation)
|
|
73
|
+
*/
|
|
74
|
+
function isValidPhone(phone) {
|
|
75
|
+
const phoneRegex = /^[\+]?[1-9][\d]{6,14}$/;
|
|
76
|
+
return phoneRegex.test(phone.replace(/[\s\-\(\)]/g, ''));
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Format person name from attributes
|
|
80
|
+
*/
|
|
81
|
+
function formatPersonName(person) {
|
|
82
|
+
const firstName = person.nickname || person.first_name || '';
|
|
83
|
+
const lastName = person.last_name || '';
|
|
84
|
+
if (firstName && lastName) {
|
|
85
|
+
return `${firstName} ${lastName}`;
|
|
77
86
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const phoneResponse = await (0, people_1.createPersonPhoneNumber)(client, personId, {
|
|
81
|
-
location: 'Mobile',
|
|
82
|
-
number: personData.phone,
|
|
83
|
-
primary: true,
|
|
84
|
-
});
|
|
85
|
-
results.phone = phoneResponse.data;
|
|
87
|
+
else if (firstName) {
|
|
88
|
+
return firstName;
|
|
86
89
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const addressResponse = await (0, people_1.createPersonAddress)(client, personId, {
|
|
90
|
-
...personData.address,
|
|
91
|
-
location: 'Home',
|
|
92
|
-
primary: true,
|
|
93
|
-
});
|
|
94
|
-
results.address = addressResponse.data;
|
|
90
|
+
else if (lastName) {
|
|
91
|
+
return lastName;
|
|
95
92
|
}
|
|
96
|
-
return
|
|
93
|
+
return 'Unknown';
|
|
97
94
|
}
|
|
98
95
|
/**
|
|
99
|
-
*
|
|
96
|
+
* Format date string in various formats
|
|
100
97
|
*/
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
if (
|
|
104
|
-
|
|
98
|
+
function formatDate(dateString, format = 'short') {
|
|
99
|
+
const date = new Date(dateString);
|
|
100
|
+
if (isNaN(date.getTime())) {
|
|
101
|
+
return 'Invalid Date';
|
|
105
102
|
}
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
switch (format) {
|
|
104
|
+
case 'short':
|
|
105
|
+
return date.toLocaleDateString();
|
|
106
|
+
case 'long':
|
|
107
|
+
return date.toLocaleDateString('en-US', {
|
|
108
|
+
year: 'numeric',
|
|
109
|
+
month: 'long',
|
|
110
|
+
day: 'numeric'
|
|
111
|
+
});
|
|
112
|
+
case 'iso':
|
|
113
|
+
return date.toISOString().split('T')[0];
|
|
114
|
+
default:
|
|
115
|
+
return date.toLocaleDateString();
|
|
108
116
|
}
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Validate person data
|
|
120
|
+
*/
|
|
121
|
+
function validatePersonData(data) {
|
|
122
|
+
const errors = [];
|
|
123
|
+
if (data.email && typeof data.email === 'string' && !isValidEmail(data.email)) {
|
|
124
|
+
errors.push('Invalid email format');
|
|
111
125
|
}
|
|
112
|
-
if (
|
|
113
|
-
|
|
126
|
+
if (data.phone && typeof data.phone === 'string' && !isValidPhone(data.phone)) {
|
|
127
|
+
errors.push('Invalid phone format');
|
|
114
128
|
}
|
|
115
|
-
if (
|
|
116
|
-
|
|
129
|
+
if (data.birthdate) {
|
|
130
|
+
const birthDate = new Date(data.birthdate);
|
|
131
|
+
if (isNaN(birthDate.getTime())) {
|
|
132
|
+
errors.push('Invalid birthdate format');
|
|
133
|
+
}
|
|
117
134
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
return response.data;
|
|
135
|
+
return {
|
|
136
|
+
isValid: errors.length === 0,
|
|
137
|
+
errors
|
|
138
|
+
};
|
|
123
139
|
}
|
|
124
140
|
/**
|
|
125
|
-
* Get
|
|
141
|
+
* Get primary contact information for a person
|
|
126
142
|
*/
|
|
127
|
-
async function
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
async function getPrimaryContact(client, personId, context) {
|
|
144
|
+
const [emails, phones, addresses] = await Promise.all([
|
|
145
|
+
(0, people_1.getPersonEmails)(client, personId, context),
|
|
146
|
+
(0, people_1.getPersonPhoneNumbers)(client, personId, context),
|
|
147
|
+
(0, people_1.getPersonAddresses)(client, personId, context)
|
|
148
|
+
]);
|
|
149
|
+
const primaryEmail = emails.data.find((e) => e.attributes.primary);
|
|
150
|
+
const primaryPhone = phones.data.find((p) => p.attributes.primary);
|
|
151
|
+
const primaryAddress = addresses.data.find((a) => a.attributes.primary);
|
|
152
|
+
return {
|
|
153
|
+
email: primaryEmail?.attributes?.address || emails.data[0]?.attributes?.address,
|
|
154
|
+
phone: primaryPhone?.attributes?.number || phones.data[0]?.attributes?.number,
|
|
155
|
+
address: (primaryAddress?.attributes?.street || addresses.data[0]?.attributes?.street)
|
|
156
|
+
};
|
|
133
157
|
}
|
|
134
|
-
// ===== Workflow Management Helpers =====
|
|
135
158
|
/**
|
|
136
|
-
*
|
|
159
|
+
* Create a person with contact information
|
|
137
160
|
*/
|
|
138
|
-
async function
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
161
|
+
async function createPersonWithContact(client, personData, contactData, context) {
|
|
162
|
+
const person = await (0, people_1.createPerson)(client, personData, context);
|
|
163
|
+
const results = { person };
|
|
164
|
+
if (contactData?.email) {
|
|
165
|
+
results.email = await (0, people_1.createPersonEmail)(client, person.data.id, contactData.email, context);
|
|
166
|
+
}
|
|
167
|
+
if (contactData?.phone) {
|
|
168
|
+
results.phone = await (0, people_1.createPersonPhoneNumber)(client, person.data.id, contactData.phone, context);
|
|
169
|
+
}
|
|
170
|
+
if (contactData?.address) {
|
|
171
|
+
results.address = await (0, people_1.createPersonAddress)(client, person.data.id, contactData.address, context);
|
|
172
|
+
}
|
|
173
|
+
return results;
|
|
148
174
|
}
|
|
149
175
|
/**
|
|
150
|
-
*
|
|
176
|
+
* Search people by multiple criteria
|
|
151
177
|
*/
|
|
152
|
-
async function
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
178
|
+
async function searchPeople(client, criteria, context) {
|
|
179
|
+
const where = {};
|
|
180
|
+
if (criteria.status) {
|
|
181
|
+
where.status = criteria.status;
|
|
182
|
+
}
|
|
183
|
+
if (criteria.name) {
|
|
184
|
+
where.name = criteria.name;
|
|
156
185
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const noteResponse = await (0, people_1.createWorkflowCardNote)(client, personId, cardResponse.data.id, { note: cardData.initialNote });
|
|
160
|
-
results.note = noteResponse.data;
|
|
186
|
+
if (criteria.email) {
|
|
187
|
+
where.email = criteria.email;
|
|
161
188
|
}
|
|
162
|
-
return
|
|
189
|
+
return (0, people_1.getPeople)(client, {
|
|
190
|
+
where,
|
|
191
|
+
per_page: criteria.per_page || 25
|
|
192
|
+
}, context);
|
|
163
193
|
}
|
|
164
|
-
// ===== List Management Helpers =====
|
|
165
194
|
/**
|
|
166
|
-
* Get
|
|
195
|
+
* Get people by household
|
|
167
196
|
*/
|
|
168
|
-
async function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
197
|
+
async function getPeopleByHousehold(client, householdId, context) {
|
|
198
|
+
return (0, people_1.getPeople)(client, {
|
|
199
|
+
where: { household_id: householdId },
|
|
200
|
+
include: ['household']
|
|
201
|
+
}, context);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get complete person profile with all related data
|
|
205
|
+
*/
|
|
206
|
+
async function getCompletePersonProfile(client, personId, context) {
|
|
207
|
+
const [person, emails, phones, addresses, fieldData, workflowCards] = await Promise.all([
|
|
208
|
+
(0, people_1.getPerson)(client, personId, ['household'], context),
|
|
209
|
+
(0, people_1.getPersonEmails)(client, personId, context),
|
|
210
|
+
(0, people_1.getPersonPhoneNumbers)(client, personId, context),
|
|
211
|
+
(0, people_1.getPersonAddresses)(client, personId, context),
|
|
212
|
+
(0, people_1.getPersonFieldData)(client, personId, context),
|
|
213
|
+
(0, people_1.getWorkflowCards)(client, personId, context)
|
|
172
214
|
]);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
215
|
+
return {
|
|
216
|
+
person,
|
|
217
|
+
emails,
|
|
218
|
+
phones,
|
|
219
|
+
addresses,
|
|
220
|
+
fieldData,
|
|
221
|
+
workflowCards
|
|
222
|
+
};
|
|
178
223
|
}
|
|
179
|
-
// ===== Organization Helpers =====
|
|
180
224
|
/**
|
|
181
|
-
* Get organization
|
|
225
|
+
* Get organization info with statistics
|
|
182
226
|
*/
|
|
183
|
-
async function getOrganizationInfo(client) {
|
|
184
|
-
const [
|
|
185
|
-
(0, people_1.getOrganization)(client),
|
|
186
|
-
(0, people_1.getPeople)(client, { per_page: 1 }),
|
|
187
|
-
(0, people_1.getHouseholds)(client, { per_page: 1 }),
|
|
188
|
-
(0, people_1.getLists)(client, { per_page: 1 })
|
|
189
|
-
(0, people_1.getWorkflows)(client, { per_page: 1 }),
|
|
227
|
+
async function getOrganizationInfo(client, context) {
|
|
228
|
+
const [organization, people, households, lists] = await Promise.all([
|
|
229
|
+
(0, people_1.getOrganization)(client, undefined, context),
|
|
230
|
+
(0, people_1.getPeople)(client, { per_page: 1 }, context),
|
|
231
|
+
(0, people_1.getHouseholds)(client, { per_page: 1 }, context),
|
|
232
|
+
(0, people_1.getLists)(client, { per_page: 1 }, context)
|
|
190
233
|
]);
|
|
191
|
-
if (!organizationResponse.data) {
|
|
192
|
-
throw new Error('Failed to get organization data');
|
|
193
|
-
}
|
|
194
234
|
return {
|
|
195
|
-
organization
|
|
235
|
+
organization,
|
|
196
236
|
stats: {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
},
|
|
237
|
+
totalPeople: Number(people.meta?.total_count) || 0,
|
|
238
|
+
totalHouseholds: Number(households.meta?.total_count) || 0,
|
|
239
|
+
totalLists: Number(lists.meta?.total_count) || 0
|
|
240
|
+
}
|
|
202
241
|
};
|
|
203
242
|
}
|
|
204
|
-
// ===== Data Export Helpers =====
|
|
205
243
|
/**
|
|
206
|
-
*
|
|
244
|
+
* Get lists with their categories
|
|
245
|
+
*/
|
|
246
|
+
async function getListsWithCategories(client, context) {
|
|
247
|
+
const [lists, categories] = await Promise.all([
|
|
248
|
+
(0, people_1.getLists)(client, { include: ['list_category'] }, context),
|
|
249
|
+
(0, people_1.getListCategories)(client, undefined, context)
|
|
250
|
+
]);
|
|
251
|
+
return { lists, categories };
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get workflow cards with notes for a person
|
|
207
255
|
*/
|
|
208
|
-
async function
|
|
209
|
-
const
|
|
210
|
-
const
|
|
256
|
+
async function getPersonWorkflowCardsWithNotes(client, personId, context) {
|
|
257
|
+
const workflowCards = await (0, people_1.getWorkflowCards)(client, personId, context);
|
|
258
|
+
const notes = {};
|
|
259
|
+
for (const card of workflowCards.data) {
|
|
260
|
+
try {
|
|
261
|
+
notes[card.id] = await (0, people_1.getWorkflowCardNotes)(client, personId, card.id, context);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
notes[card.id] = { data: [], meta: { total_count: 0 } };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return { workflowCards, notes };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Create a workflow card with a note
|
|
271
|
+
*/
|
|
272
|
+
async function createWorkflowCardWithNote(client, workflowId, personId, noteData, context) {
|
|
273
|
+
const workflowCard = await (0, people_1.createWorkflowCard)(client, workflowId, personId, context);
|
|
274
|
+
const note = await (0, people_1.createWorkflowCardNote)(client, personId, workflowCard.data.id, noteData, context);
|
|
275
|
+
return { workflowCard, note };
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Export all people data in a structured format
|
|
279
|
+
*/
|
|
280
|
+
async function exportAllPeopleData(client, options = {}, context) {
|
|
281
|
+
const { includeInactive = false, includeFieldData = false, includeWorkflowCards = false, perPage = 100 } = options;
|
|
282
|
+
const where = {};
|
|
211
283
|
if (!includeInactive) {
|
|
212
|
-
|
|
284
|
+
where.status = 'active';
|
|
213
285
|
}
|
|
214
|
-
const
|
|
215
|
-
'emails',
|
|
216
|
-
'phone_numbers',
|
|
217
|
-
'addresses',
|
|
218
|
-
'social_profiles',
|
|
219
|
-
];
|
|
286
|
+
const include = ['household'];
|
|
220
287
|
if (includeFieldData) {
|
|
221
|
-
|
|
288
|
+
include.push('field_data');
|
|
222
289
|
}
|
|
223
290
|
if (includeWorkflowCards) {
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
let allPeople = [];
|
|
227
|
-
let page = 1;
|
|
228
|
-
let hasMore = true;
|
|
229
|
-
while (hasMore) {
|
|
230
|
-
const response = await (0, people_1.getPeople)(client, {
|
|
231
|
-
include: includeArray,
|
|
232
|
-
page,
|
|
233
|
-
per_page: batchSize,
|
|
234
|
-
where: whereConditions,
|
|
235
|
-
});
|
|
236
|
-
allPeople = allPeople.concat(response.data);
|
|
237
|
-
hasMore = response.links?.next ? true : false;
|
|
238
|
-
page++;
|
|
291
|
+
include.push('workflow_cards');
|
|
239
292
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
293
|
+
const [people, households, lists, organization] = await Promise.all([
|
|
294
|
+
(0, people_1.getPeople)(client, { where, include, per_page: perPage }, context),
|
|
295
|
+
(0, people_1.getHouseholds)(client, { per_page: perPage }, context),
|
|
296
|
+
(0, people_1.getLists)(client, { per_page: perPage }, context),
|
|
297
|
+
(0, people_1.getOrganization)(client, undefined, context)
|
|
298
|
+
]);
|
|
299
|
+
return {
|
|
300
|
+
people: people.data,
|
|
301
|
+
households: households.data,
|
|
302
|
+
lists: lists.data,
|
|
303
|
+
organization: organization.data,
|
|
304
|
+
exportDate: new Date().toISOString(),
|
|
305
|
+
totalCount: Number(people.meta?.total_count) || 0
|
|
306
|
+
};
|
|
253
307
|
}
|
|
254
|
-
// =====
|
|
308
|
+
// ===== File Handling Utilities =====
|
|
255
309
|
/**
|
|
256
|
-
*
|
|
310
|
+
* Extracts clean URL from HTML markup that contains file links
|
|
311
|
+
* Handles cases like: <a href="https://onark.s3.us-east-1.amazonaws.com/file.pdf" download>View File: https://onark.s3.us-east-1.amazonaws.com/file.pdf</a>
|
|
257
312
|
*/
|
|
258
|
-
function
|
|
259
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
313
|
+
function extractFileUrl(value) {
|
|
314
|
+
// If it's already a clean URL, return it
|
|
315
|
+
if (value.startsWith('http') && !value.includes('<')) {
|
|
316
|
+
return value;
|
|
262
317
|
}
|
|
263
|
-
|
|
264
|
-
|
|
318
|
+
// Extract URL from HTML anchor tag
|
|
319
|
+
const hrefMatch = /href=["']([^"']+)["']/.exec(value);
|
|
320
|
+
if (hrefMatch) {
|
|
321
|
+
return hrefMatch[1];
|
|
265
322
|
}
|
|
266
|
-
|
|
267
|
-
|
|
323
|
+
// Extract URL from text content (fallback)
|
|
324
|
+
const urlMatch = /(https?:\/\/[^\s<>"']+)/.exec(value);
|
|
325
|
+
if (urlMatch) {
|
|
326
|
+
return urlMatch[1];
|
|
268
327
|
}
|
|
269
|
-
return
|
|
270
|
-
|
|
271
|
-
isValid: errors.length === 0,
|
|
272
|
-
};
|
|
328
|
+
// If no URL found, return original value
|
|
329
|
+
return value;
|
|
273
330
|
}
|
|
274
331
|
/**
|
|
275
|
-
*
|
|
332
|
+
* Determines if a value contains a file URL
|
|
276
333
|
*/
|
|
277
|
-
function
|
|
278
|
-
const
|
|
279
|
-
return
|
|
334
|
+
function isFileUrl(value) {
|
|
335
|
+
const cleanUrl = extractFileUrl(value);
|
|
336
|
+
return (cleanUrl.includes('s3.') ||
|
|
337
|
+
cleanUrl.includes('amazonaws.com') ||
|
|
338
|
+
cleanUrl.includes('onark.s3.'));
|
|
280
339
|
}
|
|
281
340
|
/**
|
|
282
|
-
*
|
|
341
|
+
* Gets file extension from URL
|
|
283
342
|
*/
|
|
284
|
-
function
|
|
285
|
-
const
|
|
286
|
-
|
|
343
|
+
function getFileExtension(url) {
|
|
344
|
+
const cleanUrl = extractFileUrl(url);
|
|
345
|
+
const match = /\.([a-zA-Z0-9]+)(?:[?#]|$)/.exec(cleanUrl);
|
|
346
|
+
return match ? match[1].toLowerCase() : '';
|
|
287
347
|
}
|
|
288
|
-
// ===== Utility Helpers =====
|
|
289
348
|
/**
|
|
290
|
-
*
|
|
349
|
+
* Gets filename from URL
|
|
291
350
|
*/
|
|
292
|
-
function
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
if (attrs?.first_name) {
|
|
298
|
-
return attrs.first_name;
|
|
299
|
-
}
|
|
300
|
-
if (attrs?.last_name) {
|
|
301
|
-
return attrs.last_name;
|
|
302
|
-
}
|
|
303
|
-
if (attrs?.name) {
|
|
304
|
-
return attrs.name;
|
|
305
|
-
}
|
|
306
|
-
return 'Unknown';
|
|
351
|
+
function getFilename(url) {
|
|
352
|
+
const cleanUrl = extractFileUrl(url);
|
|
353
|
+
const urlParts = cleanUrl.split('/');
|
|
354
|
+
return urlParts[urlParts.length - 1] || 'file';
|
|
307
355
|
}
|
|
308
356
|
/**
|
|
309
|
-
*
|
|
357
|
+
* Determines if a field value represents a file upload
|
|
310
358
|
*/
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
const primaryPhone = relatedData.phoneNumbers?.find(phone => phone.attributes?.primary);
|
|
314
|
-
return {
|
|
315
|
-
email: primaryEmail?.attributes?.address,
|
|
316
|
-
phone: primaryPhone?.attributes?.number,
|
|
317
|
-
};
|
|
359
|
+
function isFileUpload(value) {
|
|
360
|
+
return isFileUrl(value) || value.includes('<a href=');
|
|
318
361
|
}
|
|
319
362
|
/**
|
|
320
|
-
*
|
|
363
|
+
* Gets MIME type from file extension
|
|
321
364
|
*/
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
365
|
+
function getMimeType(extension) {
|
|
366
|
+
const mimeTypes = {
|
|
367
|
+
csv: 'text/csv',
|
|
368
|
+
doc: 'application/msword',
|
|
369
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
370
|
+
gif: 'image/gif',
|
|
371
|
+
jpeg: 'image/jpeg',
|
|
372
|
+
jpg: 'image/jpeg',
|
|
373
|
+
pdf: 'application/pdf',
|
|
374
|
+
png: 'image/png',
|
|
375
|
+
txt: 'text/plain',
|
|
376
|
+
xls: 'application/vnd.ms-excel',
|
|
377
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
378
|
+
};
|
|
379
|
+
return mimeTypes[extension] || 'application/octet-stream';
|
|
335
380
|
}
|
|
336
381
|
/**
|
|
337
|
-
*
|
|
382
|
+
* Processes file upload value for PCO field
|
|
383
|
+
* Returns clean URL for text fields, or file data for file fields
|
|
338
384
|
*/
|
|
339
|
-
function
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if (isNaN(date.getTime()))
|
|
344
|
-
return dateString;
|
|
345
|
-
switch (format) {
|
|
346
|
-
case 'short':
|
|
347
|
-
return date.toLocaleDateString();
|
|
348
|
-
case 'long':
|
|
349
|
-
return date.toLocaleDateString('en-US', {
|
|
350
|
-
day: 'numeric',
|
|
351
|
-
month: 'long',
|
|
352
|
-
year: 'numeric',
|
|
353
|
-
});
|
|
354
|
-
case 'iso':
|
|
355
|
-
return date.toISOString();
|
|
356
|
-
default:
|
|
357
|
-
return dateString;
|
|
385
|
+
function processFileValue(value, fieldType = 'text') {
|
|
386
|
+
const cleanUrl = extractFileUrl(value);
|
|
387
|
+
if (fieldType === 'text') {
|
|
388
|
+
return cleanUrl;
|
|
358
389
|
}
|
|
390
|
+
// For file fields, return metadata object
|
|
391
|
+
const extension = getFileExtension(cleanUrl);
|
|
392
|
+
const filename = getFilename(cleanUrl);
|
|
393
|
+
const contentType = getMimeType(extension);
|
|
394
|
+
return {
|
|
395
|
+
contentType,
|
|
396
|
+
filename,
|
|
397
|
+
url: cleanUrl,
|
|
398
|
+
};
|
|
359
399
|
}
|