abmp-npm 1.8.25 → 1.8.26

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.
@@ -1,9 +1,17 @@
1
+ const { contacts } = require('@wix/crm');
2
+
1
3
  const { COLLECTIONS } = require('../public/consts');
2
4
 
3
5
  const { MEMBER_ACTIONS } = require('./consts');
4
6
  const { wixData } = require('./elevated-modules');
5
7
  const { createSiteMember, getCurrentMember } = require('./members-area-methods');
6
- const { formatDateToMonthYear, getAddressDisplayOptions, isStudent } = require('./utils');
8
+ const {
9
+ formatDateToMonthYear,
10
+ getAddressDisplayOptions,
11
+ isStudent,
12
+ generateGeoHash,
13
+ urlExists,
14
+ } = require('./utils');
7
15
 
8
16
  /**
9
17
  * Retrieves member data by member ID
@@ -124,8 +132,172 @@ async function validateMemberToken(memberIdInput) {
124
132
  }
125
133
  }
126
134
 
135
+ /**
136
+ * Generic contact update helper function
137
+ * @param {string} contactId - The contact ID in Wix CRM
138
+ * @param {function} updateInfoCallback - Function that returns the updated info object
139
+ * @param {string} operationName - Name of the operation for logging
140
+ */
141
+ async function updateContactInfo(contactId, updateInfoCallback, operationName) {
142
+ if (!contactId) {
143
+ throw new Error('Contact ID is required');
144
+ }
145
+
146
+ try {
147
+ const contact = await contacts.getContact(contactId);
148
+ const currentInfo = contact.info;
149
+ const updatedInfo = updateInfoCallback(currentInfo);
150
+
151
+ await contacts.updateContact(contactId, { info: updatedInfo });
152
+ } catch (error) {
153
+ console.error(`Error in ${operationName}:`, error);
154
+ throw new Error(`Failed to ${operationName}: ${error.message}`);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Updates contact email in Wix CRM
160
+ * @param {string} contactId - The contact ID in Wix CRM
161
+ * @param {string} newEmail - The new email address
162
+ */
163
+ async function updateContactEmail(contactId, newEmail) {
164
+ if (!newEmail) {
165
+ throw new Error('New email is required');
166
+ }
167
+
168
+ return updateContactInfo(
169
+ contactId,
170
+ currentInfo => ({
171
+ ...currentInfo,
172
+ emails: {
173
+ items: [
174
+ {
175
+ email: newEmail,
176
+ primary: true,
177
+ },
178
+ ],
179
+ },
180
+ }),
181
+ 'update contact email'
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Updates contact names in Wix CRM
187
+ * @param {string} contactId - The contact ID in Wix CRM
188
+ * @param {string} firstName - The new first name
189
+ * @param {string} lastName - The new last name
190
+ */
191
+ async function updateContactNames(contactId, firstName, lastName) {
192
+ if (!firstName && !lastName) {
193
+ throw new Error('At least one name field is required');
194
+ }
195
+
196
+ return updateContactInfo(
197
+ contactId,
198
+ currentInfo => ({
199
+ ...currentInfo,
200
+ name: {
201
+ first: firstName || currentInfo?.name?.first || '',
202
+ last: lastName || currentInfo?.name?.last || '',
203
+ },
204
+ }),
205
+ 'update contact names'
206
+ );
207
+ }
208
+
209
+ /**
210
+ * Update fields if they have changed
211
+ * @param {Array} existingValues - Current values for comparison
212
+ * @param {Array} newValues - New values to compare against
213
+ * @param {Function} updater - Function to call if values changed
214
+ * @param {Function} argsBuilder - Function to build arguments for updater
215
+ */
216
+ const updateIfChanged = (existingValues, newValues, updater, argsBuilder) => {
217
+ const hasChanged = existingValues.some((val, idx) => val !== newValues[idx]);
218
+ if (!hasChanged) return null;
219
+ return updater(...argsBuilder(newValues));
220
+ };
221
+
222
+ /**
223
+ * Updates member contact information in CRM if fields have changed
224
+ * @param {string} id - Member ID
225
+ * @param {Object} data - New member data
226
+ */
227
+ const updateMemberContactInfo = async (id, data) => {
228
+ const existing = await findMemberByWixDataId(id);
229
+ const { contactId } = existing;
230
+
231
+ const updateConfig = [
232
+ {
233
+ fields: ['contactFormEmail'],
234
+ updater: updateContactEmail,
235
+ args: ([email]) => [contactId, email],
236
+ },
237
+ {
238
+ fields: ['firstName', 'lastName'],
239
+ updater: updateContactNames,
240
+ args: ([firstName, lastName]) => [contactId, firstName, lastName],
241
+ },
242
+ ];
243
+
244
+ const updatePromises = updateConfig
245
+ .map(({ fields, updater, args }) => {
246
+ const existingValues = fields.map(field => existing[field]);
247
+ const newValues = fields.map(field => data[field]);
248
+ return updateIfChanged(existingValues, newValues, updater, args);
249
+ })
250
+ .filter(Boolean);
251
+
252
+ await Promise.all(updatePromises);
253
+ };
254
+
255
+ /**
256
+ * Saves member registration data
257
+ * @param {Object} data - Member data to save
258
+ * @param {string} id - Member ID
259
+ * @returns {Promise<Object>} Result object with type and data/error
260
+ */
261
+ async function saveRegistrationData(data, id) {
262
+ try {
263
+ console.log(' saveRegistrationData data._id', data._id);
264
+ console.log(' saveRegistrationData id', id);
265
+ if (data._id !== id) return { type: 'notAuthorized' };
266
+
267
+ if (data.url) {
268
+ const isDuplicate = await urlExists(data.url, data.memberId);
269
+
270
+ if (isDuplicate) {
271
+ return {
272
+ type: 'error',
273
+ error: 'URL slug is already taken. Please choose a different one.',
274
+ };
275
+ }
276
+ }
277
+
278
+ if (data.addresses && Array.isArray(data.addresses)) {
279
+ data.locHash = generateGeoHash(data.addresses);
280
+ }
281
+
282
+ await updateMemberContactInfo(id, data);
283
+
284
+ const saveData = await wixData.update(COLLECTIONS.MEMBERS_DATA, data);
285
+ return {
286
+ type: 'success',
287
+ saveData,
288
+ };
289
+ } catch (error) {
290
+ console.error(error);
291
+ return {
292
+ type: 'error',
293
+ error,
294
+ };
295
+ }
296
+ }
297
+
127
298
  module.exports = {
128
299
  findMemberByWixDataId,
129
300
  createContactAndMemberIfNew,
130
301
  validateMemberToken,
302
+ saveRegistrationData,
131
303
  };
package/backend/utils.js CHANGED
@@ -1,6 +1,8 @@
1
+ const { encode } = require('ngeohash');
2
+
1
3
  const { COLLECTIONS } = require('../public/consts');
2
4
 
3
- const { CONFIG_KEYS } = require('./consts');
5
+ const { CONFIG_KEYS, PRECISION } = require('./consts');
4
6
  const { wixData } = require('./elevated-modules');
5
7
 
6
8
  /**
@@ -104,6 +106,48 @@ async function getInterestAll() {
104
106
  }
105
107
  }
106
108
 
109
+ /**
110
+ * Generate geohash from addresses
111
+ * @param {Array} addresses - Array of address objects with latitude and longitude
112
+ * @returns {Array} Array of geohash strings
113
+ */
114
+ function generateGeoHash(addresses) {
115
+ const geohash = addresses
116
+ ?.filter(address => (isNaN(address?.latitude) && isNaN(address?.longitude) ? false : address))
117
+ ?.map(address => encode(address.latitude, address.longitude, PRECISION));
118
+ return geohash && geohash.length > 0 ? geohash : [];
119
+ }
120
+
121
+ /**
122
+ * Checks if a URL already exists in the database for a different member (case-insensitive)
123
+ * @param {string} url - The URL to check
124
+ * @param {string|number} excludeMemberId - Member ID to exclude from the check
125
+ * @returns {Promise<boolean>} - True if URL exists for another member
126
+ */
127
+ async function urlExists(url, excludeMemberId) {
128
+ if (!url) return false;
129
+
130
+ try {
131
+ let query = wixData.query(COLLECTIONS.MEMBERS_DATA).contains('url', url).ne('action', 'drop');
132
+
133
+ if (excludeMemberId) {
134
+ query = query.ne('memberId', excludeMemberId);
135
+ }
136
+
137
+ const { items } = await query.find();
138
+
139
+ // Case-insensitive comparison
140
+ const matchingMembers = items.filter(
141
+ item => item.url && item.url.toLowerCase() === url.toLowerCase()
142
+ );
143
+
144
+ return matchingMembers.length > 0;
145
+ } catch (error) {
146
+ console.error('Error checking URL existence:', error);
147
+ return false;
148
+ }
149
+ }
150
+
107
151
  module.exports = {
108
152
  getSiteConfigs,
109
153
  retrieveAllItems,
@@ -111,4 +155,6 @@ module.exports = {
111
155
  isStudent,
112
156
  getAddressDisplayOptions,
113
157
  getInterestAll,
158
+ generateGeoHash,
159
+ urlExists,
114
160
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "1.8.25",
3
+ "version": "1.8.26",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",