abmp-npm 2.0.21 → 2.0.22

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.
@@ -3,7 +3,33 @@ const { auth } = require('@wix/essentials');
3
3
 
4
4
  const elevatedGetContact = auth.elevate(contacts.getContact);
5
5
  const elevatedUpdateContact = auth.elevate(contacts.updateContact);
6
+ const elevatedCreateContact = auth.elevate(contacts.createContact);
6
7
 
8
+ /**
9
+ * Create a contact in Wix CRM
10
+ * @param {Object} contactData - Contact data
11
+ * @param {boolean} allowDuplicates - Allow duplicates if contact with same email already exists, will be true only when handling existing members, after that should be removed
12
+ * @returns {Promise<Object>} - Contact data
13
+ */
14
+ async function createSiteContact(contactData, allowDuplicates = false) {
15
+ if (!contactData || !(contactData.contactFormEmail || contactData.email)) {
16
+ throw new Error('Contact data is required');
17
+ }
18
+ const phones =
19
+ Array.isArray(contactData.phones) && contactData.phones.length > 0 ? contactData.phones : [];
20
+ const contactInfo = {
21
+ name: {
22
+ first: contactData.firstName,
23
+ last: contactData.lastName,
24
+ },
25
+ emails: {
26
+ items: [{ email: contactData.contactFormEmail || contactData.email, primary: true }],
27
+ },
28
+ phones: { items: phones.map(phone => ({ phone })) },
29
+ };
30
+ const createContactResponse = await elevatedCreateContact(contactInfo, { allowDuplicates });
31
+ return createContactResponse.contact._id;
32
+ }
7
33
  /**
8
34
  * Generic contact update helper function
9
35
  * @param {string} contactId - The contact ID in Wix CRM
@@ -55,27 +81,27 @@ async function updateContactEmail(contactId, newEmail) {
55
81
  }
56
82
 
57
83
  /**
58
- * Updates contact names in Wix CRM
59
- * @param {string} contactId - The contact ID in Wix CRM
60
- * @param {string} firstName - The new first name
61
- * @param {string} lastName - The new last name
84
+ * Updates contact names in Wix CRM for both contact and member
85
+ * @param {Object} params - Parameters object
86
+ * @param {string} params.wixContactId - The contact ID in Wix CRM
87
+ * @param {string} params.wixMemberId - The member ID in Wix CRM
88
+ * @param {string} params.firstName - The new first name
89
+ * @param {string} params.lastName - The new last name
62
90
  */
63
- async function updateContactNames(contactId, firstName, lastName) {
91
+ async function updateContactNames({ wixContactId, firstName, lastName }) {
64
92
  if (!firstName && !lastName) {
65
- throw new Error('At least one name field is required');
93
+ throw new Error('First name or last name is required');
66
94
  }
67
95
 
68
- return await updateContactInfo(
69
- contactId,
70
- currentInfo => ({
71
- ...currentInfo,
72
- name: {
73
- first: firstName || currentInfo?.name?.first || '',
74
- last: lastName || currentInfo?.name?.last || '',
75
- },
76
- }),
77
- 'update contact names'
78
- );
96
+ const createNameUpdate = currentInfo => ({
97
+ ...currentInfo,
98
+ name: {
99
+ first: firstName || currentInfo?.name?.first || '',
100
+ last: lastName || currentInfo?.name?.last || '',
101
+ },
102
+ });
103
+
104
+ return await updateContactInfo(wixContactId, createNameUpdate, 'update contact names');
79
105
  }
80
106
 
81
107
  /**
@@ -97,18 +123,20 @@ const updateIfChanged = (existingValues, newValues, updater, argsBuilder) => {
97
123
  * @param {Object} existingMemberData - Existing member data
98
124
  */
99
125
  const updateMemberContactInfo = async (data, existingMemberData) => {
100
- const { contactId } = existingMemberData;
101
-
126
+ const { wixContactId } = existingMemberData;
127
+ if (!wixContactId) {
128
+ throw new Error('Wix Contact ID is required');
129
+ }
102
130
  const updateConfig = [
103
131
  {
104
132
  fields: ['contactFormEmail'],
105
133
  updater: updateContactEmail,
106
- args: ([email]) => [contactId, email],
134
+ args: ([email]) => [wixContactId, email],
107
135
  },
108
136
  {
109
137
  fields: ['firstName', 'lastName'],
110
138
  updater: updateContactNames,
111
- args: ([firstName, lastName]) => [contactId, firstName, lastName],
139
+ args: ([firstName, lastName]) => [{ firstName, lastName, wixContactId }],
112
140
  },
113
141
  ];
114
142
 
@@ -125,4 +153,5 @@ const updateMemberContactInfo = async (data, existingMemberData) => {
125
153
 
126
154
  module.exports = {
127
155
  updateMemberContactInfo,
156
+ createSiteContact,
128
157
  };
@@ -168,7 +168,7 @@ const bulkProcessAndSaveMemberData = async ({
168
168
  const toChangeWixMembersEmails = [];
169
169
  const toSaveMembersData = uniqueUrlsMembersData.map(member => {
170
170
  const { isLoginEmailChanged, isNewToDb: _isNewToDb, ...restMemberData } = member;
171
- if (member.contactId && isLoginEmailChanged) {
171
+ if (member.wixMemberId && isLoginEmailChanged) {
172
172
  toChangeWixMembersEmails.push(member);
173
173
  }
174
174
  return restMemberData; //we don't want to store the isLoginEmailChanged in the database, it's just a flag to know if we need to change the login email in Members area
@@ -1,3 +1,5 @@
1
+ const { COLLECTIONS } = require('../public/consts');
2
+
1
3
  const { ensureUniqueUrlsInBatch } = require('./daily-pull/bulk-process-methods');
2
4
  const { wixData } = require('./elevated-modules');
3
5
  const { bulkSaveMembers } = require('./members-data-methods');
@@ -15,4 +17,14 @@ async function deduplicateURls(collectionName, duplicateUrlsList) {
15
17
  return await bulkSaveMembers(membersWithUniqueUrls, collectionName);
16
18
  }
17
19
 
18
- module.exports = { deduplicateURls };
20
+ async function copyContactIdToWixMemberId() {
21
+ const query = wixData.query(COLLECTIONS.MEMBERS_DATA).isNotEmpty('contactId');
22
+ const members = await queryAllItems(query);
23
+ const updatedMembers = members.map(member => ({
24
+ ...member,
25
+ wixMemberId: member.contactId,
26
+ }));
27
+ return await bulkSaveMembers(updatedMembers, COLLECTIONS.MEMBERS_DATA);
28
+ }
29
+
30
+ module.exports = { deduplicateURls, copyContactIdToWixMemberId };
@@ -16,7 +16,7 @@ const contactSubmission = async (data, memberDataId) => {
16
16
  console.log('Member contact form is not enabled for user, skipping contact submission!');
17
17
  return;
18
18
  }
19
- let memberContactId = memberData.contactId;
19
+ let memberContactId = memberData.wixContactId;
20
20
  if (!memberContactId) {
21
21
  /**
22
22
  * Create a member contact here since some members may have never logged in
@@ -27,7 +27,7 @@ const contactSubmission = async (data, memberDataId) => {
27
27
  */
28
28
  console.info('Member contact id not found for user, creating new contact!');
29
29
  const member = await createContactAndMemberIfNew(memberData);
30
- memberContactId = member.contactId;
30
+ memberContactId = member.wixContactId;
31
31
  }
32
32
  console.log('memberContactId', memberContactId);
33
33
  const emailTriggered = await triggerAutomation(automationEmailTriggerId, {
@@ -40,7 +40,7 @@ const contactSubmission = async (data, memberDataId) => {
40
40
  data = {
41
41
  ...data,
42
42
  phone: Number(data.phone),
43
- memberContactId: memberContactId,
43
+ memberContactId,
44
44
  memberEmail: memberData.contactFormEmail,
45
45
  };
46
46
  await wixData.insert(COLLECTIONS.CONTACT_US_SUBMISSIONS, data);
@@ -1,4 +1,4 @@
1
- const { getMemberByEmail, getQAUsers } = require('../members-data-methods');
1
+ const { prepareMemberForQALogin, getQAUsers } = require('../members-data-methods');
2
2
  const { getSecret } = require('../utils');
3
3
 
4
4
  const validateQAUser = async userEmail => {
@@ -20,27 +20,23 @@ const validateQAUser = async userEmail => {
20
20
  */
21
21
  const loginQAMember = async ({ userEmail, secret }, generateSessionToken) => {
22
22
  try {
23
- const userValidation = await validateQAUser(userEmail);
23
+ const [userValidation, qaSecret] = await Promise.all([
24
+ validateQAUser(userEmail),
25
+ getSecret('ABMP_QA_SECRET'),
26
+ ]);
24
27
  if (userValidation.error) {
25
28
  return { success: false, error: userValidation.error };
26
29
  }
27
-
28
- const qaSecret = await getSecret('ABMP_QA_SECRET');
29
30
  if (secret !== qaSecret) {
30
31
  return { success: false, error: 'Invalid secret' };
31
32
  }
32
33
 
33
- const token = await generateSessionToken(userValidation.email, qaSecret);
34
-
35
- const result = await getMemberCMSId(userEmail);
36
- if (!result.success) {
37
- return { success: false, error: result.error };
38
- }
39
-
34
+ const memberData = await prepareMemberForQALogin(userValidation.email);
35
+ const token = await generateSessionToken(memberData.email, qaSecret);
40
36
  return {
41
37
  success: true,
42
38
  token,
43
- memberCMSId: result.memberCMSId,
39
+ memberCMSId: memberData._id,
44
40
  };
45
41
  } catch (error) {
46
42
  console.error('QA login error:', error);
@@ -48,25 +44,6 @@ const loginQAMember = async ({ userEmail, secret }, generateSessionToken) => {
48
44
  }
49
45
  };
50
46
 
51
- async function getMemberCMSId(userEmail) {
52
- try {
53
- const userValidation = await validateQAUser(userEmail);
54
- if (userValidation.error) {
55
- return { success: false, error: userValidation.error };
56
- }
57
-
58
- const member = await getMemberByEmail(userEmail);
59
-
60
- if (!member) {
61
- return { success: false, error: `No Member found in DB matching email: ${userEmail}` };
62
- }
63
- return { success: true, memberCMSId: member._id };
64
- } catch (error) {
65
- console.error('Error getting member CMS ID:', error);
66
- return { success: false, error: 'Failed to retrieve member data' };
67
- }
68
- }
69
-
70
47
  module.exports = {
71
48
  loginQAMember,
72
49
  };
@@ -5,7 +5,7 @@ const { decode } = require('jwt-js-decode');
5
5
  const { CONFIG_KEYS, SSO_TOKEN_AUTH_API_URL } = require('../consts');
6
6
  const { MEMBER_ACTIONS } = require('../daily-pull/consts');
7
7
  const { getCurrentMember } = require('../members-area-methods');
8
- const { getMemberByContactId, getSiteMemberId } = require('../members-data-methods');
8
+ const { getCMSMemberByWixMemberId, prepareMemberForSSOLogin } = require('../members-data-methods');
9
9
  const {
10
10
  formatDateToMonthYear,
11
11
  getAddressDisplayOptions,
@@ -37,12 +37,12 @@ async function validateMemberToken(memberIdInput) {
37
37
  }
38
38
 
39
39
  const [dbMember, siteConfigs] = await Promise.all([
40
- getMemberByContactId(member._id),
40
+ getCMSMemberByWixMemberId(member._id),
41
41
  getSiteConfigs(),
42
42
  ]);
43
43
  const siteAssociation = siteConfigs[CONFIG_KEYS.SITE_ASSOCIATION];
44
44
  const membersExternalPortalUrl = siteConfigs[CONFIG_KEYS.MEMBERS_EXTERNAL_PORTAL_URL];
45
- console.log('dbMember by contact id is:', dbMember);
45
+ console.log('dbMember by wix member id is:', dbMember);
46
46
  console.log('member._id', member._id);
47
47
 
48
48
  if (!dbMember?._id) {
@@ -133,12 +133,12 @@ const authenticateSSOToken = async ({ token }, generateSessionToken) => {
133
133
  if (isValidToken) {
134
134
  const jwt = decode(responseToken);
135
135
  const payload = jwt.payload;
136
- const membersData = await getSiteMemberId(payload);
137
- console.log('membersDataCollectionId', membersData._id);
138
- const sessionToken = await generateSessionToken(membersData.email);
136
+ const memberData = await prepareMemberForSSOLogin(payload);
137
+ console.log('memberDataCollectionId', memberData._id);
138
+ const sessionToken = await generateSessionToken(memberData.email);
139
139
  const authObj = {
140
140
  type: 'success',
141
- memberId: membersData._id,
141
+ memberId: memberData._id,
142
142
  sessionToken,
143
143
  };
144
144
  return authObj;
@@ -1,16 +1,11 @@
1
1
  const { auth } = require('@wix/essentials');
2
2
  const { members, authentication } = require('@wix/members');
3
3
  const elevatedCreateMember = auth.elevate(members.createMember);
4
+ const elevatedChangeLoginEmail = auth.elevate(authentication.changeLoginEmail);
4
5
 
5
- function prepareContactData(partner) {
6
- const phones = Array.isArray(partner.phones) ? partner.phones : []; //some users don't have phones
6
+ function prepareMemberData(partner) {
7
7
  const options = {
8
8
  member: {
9
- contact: {
10
- ...partner,
11
- phones,
12
- emails: [partner.contactFormEmail || partner.email],
13
- },
14
9
  loginEmail: partner.email,
15
10
  },
16
11
  };
@@ -22,9 +17,8 @@ async function createMemberFunction(member) {
22
17
  }
23
18
  const createSiteMember = async memberDetails => {
24
19
  try {
25
- const options = prepareContactData(memberDetails);
26
- const contactId = await createMemberFunction(options);
27
- return contactId;
20
+ const options = prepareMemberData(memberDetails);
21
+ return await createMemberFunction(options);
28
22
  } catch (error) {
29
23
  console.error(`Error in createSiteMember ${error.message}`);
30
24
  throw error;
@@ -37,22 +31,22 @@ const getCurrentMember = async () => {
37
31
  };
38
32
 
39
33
  /**
40
- * Updates Wix member login email if the member has a contactId (registered Wix member)
41
- * @param {Object} member - Member object with contactId and email
34
+ * Updates Wix member login email if the member has a wixMemberId (registered Wix member)
35
+ * @param {Object} member - Member object with wixMemberId and email
42
36
  * @param {Object} result - Result object to track Wix member updates
43
37
  */
44
38
  async function updateWixMemberLoginEmail(member, result = {}) {
45
- if (!member.contactId) {
46
- console.log(`Member ${member.memberId} has no contactId - skipping Wix login email update`);
39
+ if (!member.wixMemberId) {
40
+ console.log(`Member ${member.memberId} has no wixMemberId - skipping Wix login email update`);
47
41
  return;
48
42
  }
49
43
 
50
44
  try {
51
45
  console.log(
52
- `Updating Wix login email for member ${member.memberId} (contactId: ${member.contactId})`
46
+ `Updating Wix login email for member ${member.memberId} (wixMemberId: ${member.wixMemberId})`
53
47
  );
54
48
 
55
- const updatedWixMember = await authentication.changeLoginEmail(member.contactId, member.email);
49
+ const updatedWixMember = await elevatedChangeLoginEmail(member.wixMemberId, member.email);
56
50
 
57
51
  console.log(
58
52
  `✅ Successfully updated Wix login email for member ${member.memberId}: ${updatedWixMember.loginEmail}`
@@ -75,7 +69,7 @@ async function updateWixMemberLoginEmail(member, result = {}) {
75
69
  }
76
70
  result.wixMemberErrors.push({
77
71
  memberId: member.memberId,
78
- contactId: member.contactId,
72
+ wixMemberId: member.wixMemberId,
79
73
  email: member.email,
80
74
  error: error.message,
81
75
  });
@@ -2,7 +2,7 @@ const { COLLECTIONS } = require('../public/consts');
2
2
  const { isWixHostedImage } = require('../public/Utils/sharedUtils');
3
3
 
4
4
  const { MEMBERSHIPS_TYPES } = require('./consts');
5
- const { updateMemberContactInfo } = require('./contacts-methods');
5
+ const { updateMemberContactInfo, createSiteContact } = require('./contacts-methods');
6
6
  const { MEMBER_ACTIONS } = require('./daily-pull/consts');
7
7
  const { wixData } = require('./elevated-modules');
8
8
  const { createSiteMember, getCurrentMember } = require('./members-area-methods');
@@ -43,10 +43,16 @@ async function createContactAndMemberIfNew(memberData) {
43
43
  phones: memberData.phones,
44
44
  contactFormEmail: memberData.contactFormEmail || memberData.email,
45
45
  };
46
- const contactId = await createSiteMember(toCreateMemberData);
46
+ const needsWixMember = !memberData.wixMemberId;
47
+ const needsWixContact = !memberData.wixContactId;
48
+ const [newWixMemberId, newWixContactId] = await Promise.all([
49
+ needsWixMember ? createSiteMember(toCreateMemberData) : Promise.resolve(null),
50
+ needsWixContact ? createSiteContact(toCreateMemberData) : Promise.resolve(null),
51
+ ]);
47
52
  let memberDataWithContactId = {
48
53
  ...memberData,
49
- contactId,
54
+ wixMemberId: newWixMemberId || memberData.wixMemberId,
55
+ wixContactId: newWixContactId || memberData.wixContactId,
50
56
  };
51
57
  const updatedResult = await updateMember(memberDataWithContactId);
52
58
  memberDataWithContactId = {
@@ -94,10 +100,16 @@ async function findMemberById(memberId) {
94
100
  const queryResult = await wixData
95
101
  .query(COLLECTIONS.MEMBERS_DATA)
96
102
  .eq('memberId', memberId)
103
+ .limit(2)
97
104
  .find();
98
-
99
- return queryResult.items.length > 0 ? queryResult.items[0] : null;
105
+ if (queryResult.items.length > 1) {
106
+ throw new Error(
107
+ `Multiple members found with memberId ${memberId} members _ids are : [${queryResult.items.map(member => member._id).join(', ')}]`
108
+ );
109
+ }
110
+ return queryResult.items.length === 1 ? queryResult.items[0] : null;
100
111
  } catch (error) {
112
+ console.error('Error finding member by ID:', error);
101
113
  throw new Error(`Failed to retrieve member data: ${error.message}`);
102
114
  }
103
115
  }
@@ -159,31 +171,32 @@ async function getMemberBySlug({
159
171
  }
160
172
  return matchingMembers[0] || null;
161
173
  } catch (error) {
162
- console.error('Error getting member by slug:', error);
163
- throw error;
174
+ const errorMessage = `Error getting member by slug: ${slug} : ${error.message}`;
175
+ console.error(errorMessage);
176
+ throw new Error(errorMessage);
164
177
  }
165
178
  }
166
179
 
167
- async function getMemberByContactId(contactId) {
168
- if (!contactId) {
169
- throw new Error('Contact ID is required');
180
+ async function getCMSMemberByWixMemberId(wixMemberId) {
181
+ if (!wixMemberId) {
182
+ throw new Error('Wix Member ID is required');
170
183
  }
171
184
  try {
172
185
  const members = await wixData
173
186
  .query(COLLECTIONS.MEMBERS_DATA)
174
- .eq('contactId', contactId)
187
+ .eq('wixMemberId', wixMemberId)
175
188
  .limit(2)
176
189
  .find()
177
190
  .then(res => res.items);
178
191
  if (members.length > 1) {
179
192
  throw new Error(
180
- `[getMemberByContactId] Multiple members found with contactId ${contactId} membersIds are : [${members.map(member => member.memberId).join(', ')}]`
193
+ `[getCMSMemberByWixMemberId] Multiple members found with wixMemberId ${wixMemberId} membersIds are : [${members.map(member => member.memberId).join(', ')}]`
181
194
  );
182
195
  }
183
196
  return members[0] || null;
184
197
  } catch (error) {
185
198
  throw new Error(
186
- `[getMemberByContactId] Failed to retrieve member by contactId ${contactId} data: ${error.message}`
199
+ `[getCMSMemberByWixMemberId] Failed to retrieve member by wixMemberId ${wixMemberId} data: ${error.message}`
187
200
  );
188
201
  }
189
202
  }
@@ -218,8 +231,8 @@ async function updateMember(memberToUpdate) {
218
231
  }
219
232
  }
220
233
  /**
221
- * Saves member registration data
222
- * @param {Object} data - Member data to save
234
+ * Saves member registration data (supports partial updates)
235
+ * @param {Object} data - Member data to save (can be partial - only fields being updated)
223
236
  * @param {string} id - Member ID
224
237
  * @returns {Promise<Object>} Result object with type and data/error
225
238
  */
@@ -240,15 +253,29 @@ async function saveRegistrationData(data, id) {
240
253
  }
241
254
  }
242
255
 
243
- if (data.addresses && Array.isArray(data.addresses)) {
244
- data.locHash = generateGeoHash(data.addresses);
256
+ // Fetch existing data to merge with partial update
257
+ const existingMemberData = await findMemberByWixDataId(id);
258
+
259
+ if (!existingMemberData) {
260
+ return {
261
+ type: 'error',
262
+ error: 'Member not found',
263
+ };
245
264
  }
246
265
 
247
- const existingMemberData = await findMemberByWixDataId(id);
266
+ // Merge partial data with existing data (incoming data takes precedence)
267
+ const mergedData = {
268
+ ...existingMemberData,
269
+ ...data,
270
+ };
248
271
 
249
- await updateMemberContactInfo(data, existingMemberData);
272
+ if (data.addresses && Array.isArray(data.addresses)) {
273
+ mergedData.locHash = generateGeoHash(data.addresses);
274
+ }
250
275
 
251
- const saveData = await updateMember(data);
276
+ await updateMemberContactInfo(mergedData, existingMemberData);
277
+
278
+ const saveData = await updateMember(mergedData);
252
279
  return {
253
280
  type: 'success',
254
281
  saveData,
@@ -442,41 +469,57 @@ const getQAUsers = async () => {
442
469
  throw new Error(`Failed to get QA users: ${error.message}`);
443
470
  }
444
471
  };
445
- async function getSiteMemberId(data) {
472
+ /**
473
+ * Ensures member has a contact - creates one if missing
474
+ * @param {Object} memberData - Member data from DB
475
+ * @returns {Promise<Object>} - Member data with contact and member IDs
476
+ */
477
+ async function ensureWixMemberAndContactExist(memberData) {
478
+ if (!memberData) {
479
+ throw new Error('Member data is required');
480
+ }
481
+ if (!memberData.wixContactId || !memberData.wixMemberId) {
482
+ const memberDataWithContactId = await createContactAndMemberIfNew(memberData);
483
+ return memberDataWithContactId;
484
+ }
485
+ return memberData;
486
+ }
487
+ async function prepareMemberForSSOLogin(data) {
446
488
  try {
447
489
  console.log('data', data);
448
490
  const memberId = data?.pac?.cst_recno;
449
491
  if (!memberId) {
450
- const errorMessage = `Member ID is missing in passed data ${JSON.stringify(data)}`;
451
- console.error(errorMessage);
452
- throw new Error(errorMessage);
492
+ throw new Error(`Member ID is missing in passed data ${JSON.stringify(data)}`);
453
493
  }
454
- const queryMemberResult = await wixData
455
- .query(COLLECTIONS.MEMBERS_DATA)
456
- .eq('memberId', Number(memberId))
457
- .find()
458
- .then(res => res.items);
459
- if (!queryMemberResult.length || queryMemberResult.length > 1) {
460
- throw new Error(
461
- `Invalid Members count found in DB for email ${data.email} members count is : [${
462
- queryMemberResult.length
463
- }] membersIds are : [${queryMemberResult.map(member => member.memberId).join(', ')}]`
464
- );
494
+ const memberData = await findMemberById(Number(memberId));
495
+ if (!memberData) {
496
+ throw new Error(`Member data not found for memberId ${memberId}`);
465
497
  }
466
- let memberData = queryMemberResult[0];
467
498
  console.log('memberData', memberData);
468
- const isNewUser = !memberData.contactId;
469
- if (isNewUser) {
470
- const memberDataWithContactId = await createContactAndMemberIfNew(memberData);
471
- console.log('memberDataWithContactId', memberDataWithContactId);
472
- memberData = memberDataWithContactId;
473
- }
474
- return memberData;
499
+ return await ensureWixMemberAndContactExist(memberData);
475
500
  } catch (error) {
476
- console.error('Error in getSiteMemberId', error.message);
501
+ console.error(`Error in prepareMemberForSSOLogin: ${error.message}`);
477
502
  throw error;
478
503
  }
479
504
  }
505
+ async function prepareMemberForQALogin(email) {
506
+ try {
507
+ console.log('qa email:', email);
508
+ if (!email) {
509
+ throw new Error(`Email is missing in passed data ${email}`);
510
+ }
511
+ const memberData = await getMemberByEmail(email);
512
+ if (!memberData) {
513
+ throw new Error(`Member data not found for email ${email}`);
514
+ }
515
+ console.log('memberData', memberData);
516
+ return await ensureWixMemberAndContactExist(memberData);
517
+ } catch (error) {
518
+ const errMsg = `[prepareMemberForQALogin] QA Login failed with error: ${error.message} for email: ${email}`;
519
+ console.error(errMsg);
520
+ throw new Error(errMsg);
521
+ }
522
+ }
480
523
 
481
524
  /**
482
525
  * Tracks a button click with member and location info.
@@ -493,11 +536,11 @@ async function trackButtonClick({ pageName, buttonName }) {
493
536
  return null;
494
537
  }
495
538
 
496
- const dbMember = await getMemberByContactId(wixMember._id);
539
+ const dbMember = await getCMSMemberByWixMemberId(wixMember._id);
497
540
 
498
541
  if (!dbMember) {
499
542
  console.warn(
500
- `[trackButtonClick]: Member not found in MembersDataLatest for contactId: ${wixMember._id}`
543
+ `[trackButtonClick]: Member not found in MembersDataLatest for wixMemberId: ${wixMember._id}`
501
544
  );
502
545
  return null;
503
546
  }
@@ -523,14 +566,20 @@ async function trackButtonClick({ pageName, buttonName }) {
523
566
  }
524
567
  }
525
568
 
569
+ async function getAllMembersWithWixMemberId() {
570
+ const membersQuery = wixData.query(COLLECTIONS.MEMBERS_DATA).isNotEmpty('wixMemberId');
571
+ return await queryAllItems(membersQuery);
572
+ }
573
+
526
574
  module.exports = {
527
575
  findMemberByWixDataId,
528
576
  createContactAndMemberIfNew,
577
+ getAllMembersWithWixMemberId,
529
578
  saveRegistrationData,
530
579
  bulkSaveMembers,
531
580
  findMemberById,
532
581
  getMemberBySlug,
533
- getMemberByContactId,
582
+ getCMSMemberByWixMemberId,
534
583
  getAllEmptyAboutYouMembers,
535
584
  updateMember,
536
585
  getAllMembersWithExternalImages,
@@ -540,7 +589,8 @@ module.exports = {
540
589
  getMembersByIds,
541
590
  getMemberByEmail,
542
591
  getQAUsers,
543
- getSiteMemberId,
592
+ prepareMemberForSSOLogin,
593
+ prepareMemberForQALogin,
544
594
  checkUrlUniqueness,
545
595
  trackButtonClick,
546
596
  };
@@ -126,9 +126,16 @@ const createRoutersHandlers = wixRouterMethods => {
126
126
  };
127
127
  return ok('profile', { ...profileData, defaultProfileImage }, seoData);
128
128
  }
129
+ console.log(
130
+ `[profileRouter] Profile not found returning 404 for: ${JSON.stringify({
131
+ slug,
132
+ profileData,
133
+ showWixUrl: profileData?.showWixUrl,
134
+ })}`
135
+ );
129
136
  return notFound();
130
137
  } catch (error) {
131
- console.error(error);
138
+ console.error(`Error in profileRouter for slug: ${slug} : ${error.message}`);
132
139
  return sendStatus('500', 'Internal Server Error');
133
140
  }
134
141
  }
@@ -104,13 +104,15 @@ const getMemberProfileData = async (slug, siteAssociation) => {
104
104
  });
105
105
 
106
106
  if (!member) {
107
+ console.log(`[getMemberProfileData] Member not found for slug: ${slug}`);
107
108
  return null;
108
109
  }
109
110
 
110
111
  return transformMemberToProfileData(member, siteAssociation);
111
112
  } catch (error) {
112
- console.error(error);
113
- throw error;
113
+ const errorMessage = `Error in getMemberProfileData for slug: ${slug} : ${error.message}`;
114
+ console.error(errorMessage);
115
+ throw new Error(errorMessage);
114
116
  }
115
117
  };
116
118
 
@@ -16,6 +16,8 @@ const TASKS_NAMES = {
16
16
  migrateUrlsChunk: 'migrateUrlsChunk',
17
17
  scheduleGenerateMissingUrls: 'scheduleGenerateMissingUrls',
18
18
  generateUrlsChunk: 'generateUrlsChunk',
19
+ scheduleCreateContactsFromMembers: 'scheduleCreateContactsFromMembers',
20
+ createContactsFromMembers: 'createContactsFromMembers',
19
21
  };
20
22
 
21
23
  module.exports = {
@@ -16,6 +16,8 @@ const {
16
16
  migrateContactFormEmails,
17
17
  scheduleEmailSync,
18
18
  syncMemberLoginEmails,
19
+ scheduleCreateContactsFromMembers,
20
+ createContactsFromMembers,
19
21
  } = require('./tasks-process-methods');
20
22
  const {
21
23
  scheduleMigrateExistingUrls,
@@ -153,6 +155,20 @@ const TASKS = {
153
155
  shouldSkipCheck: () => false,
154
156
  estimatedDurationSec: 80,
155
157
  },
158
+ [TASKS_NAMES.scheduleCreateContactsFromMembers]: {
159
+ name: TASKS_NAMES.scheduleCreateContactsFromMembers,
160
+ getIdentifier: () => 'SHOULD_NEVER_SKIP',
161
+ process: scheduleCreateContactsFromMembers,
162
+ shouldSkipCheck: () => false,
163
+ estimatedDurationSec: 80,
164
+ },
165
+ [TASKS_NAMES.createContactsFromMembers]: {
166
+ name: TASKS_NAMES.createContactsFromMembers,
167
+ getIdentifier: task => task.data,
168
+ process: createContactsFromMembers,
169
+ shouldSkipCheck: () => false,
170
+ estimatedDurationSec: 80,
171
+ },
156
172
  };
157
173
 
158
174
  module.exports = { TASKS };
@@ -13,6 +13,8 @@ const {
13
13
  bulkSaveMembers,
14
14
  getAllUpdatedLoginEmails,
15
15
  getMembersByIds,
16
+ createContactAndMemberIfNew,
17
+ getAllMembersWithWixMemberId,
16
18
  } = require('../members-data-methods');
17
19
  const {
18
20
  getCompleteStateList,
@@ -530,6 +532,46 @@ const syncMemberLoginEmails = async data => {
530
532
  throw new Error(errorMessage);
531
533
  }
532
534
  };
535
+ /**
536
+ * Schedules tasks to create contacts from members
537
+ * dev-only task, run only once by the developers
538
+ */
539
+ const scheduleCreateContactsFromMembers = async () => {
540
+ const members = await getAllMembersWithWixMemberId();
541
+ console.log(
542
+ `Starting to schedule create contacts from members tasks for ${members.length} members in chunks of 500 members`
543
+ );
544
+ const membersChunks = chunkArray(members, 500);
545
+ for (let chunkIndex = 0; chunkIndex < membersChunks.length; chunkIndex++) {
546
+ const chunk = membersChunks[chunkIndex];
547
+ const toScheduleTask = {
548
+ name: TASKS_NAMES.createContactsFromMembers,
549
+ data: { chunk, chunkIndex },
550
+ };
551
+ await taskManager().schedule(toScheduleTask);
552
+ console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} members`);
553
+ }
554
+ console.log(`Successfully scheduled ${membersChunks.length} tasks for ${members.length} members`);
555
+ };
556
+
557
+ /**
558
+ * Creates contacts from members
559
+ * dev-only task, run only once by the developers
560
+ */
561
+ const createContactsFromMembers = async data => {
562
+ const { chunk, chunkIndex } = data;
563
+ console.log(`Creating contacts from ${chunk.length} members in chunk ${chunkIndex}`);
564
+ const createPromises = chunk.map(member => createContactAndMemberIfNew(member));
565
+ const createResults = await Promise.all(createPromises);
566
+ console.log(
567
+ `Created ${createResults.length} contacts from ${chunk.length} members in chunk ${chunkIndex}`
568
+ );
569
+ const saveResult = await bulkSaveMembers(createResults);
570
+ console.log(
571
+ `Successfully saved ${saveResult.totalSaved} contacts from ${chunk.length} members in chunk ${chunkIndex}`
572
+ );
573
+ return saveResult;
574
+ };
533
575
 
534
576
  module.exports = {
535
577
  scheduleTaskForEmptyAboutYouMembers,
@@ -542,4 +584,6 @@ module.exports = {
542
584
  migrateContactFormEmails,
543
585
  scheduleEmailSync,
544
586
  syncMemberLoginEmails,
587
+ scheduleCreateContactsFromMembers, // run only once by the developers
588
+ createContactsFromMembers,
545
589
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.21",
3
+ "version": "2.0.22",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
@@ -1087,8 +1087,10 @@ async function personalDetailsOnReady({
1087
1087
  const personalChanges = getPersonalData();
1088
1088
  const originalUrl = beforeData.url;
1089
1089
 
1090
+ // Only send personal fields + required identifiers (partial update)
1090
1091
  const formData = {
1091
- ...itemMemberObj,
1092
+ _id: itemMemberObj._id,
1093
+ memberId: itemMemberObj.memberId,
1092
1094
  ...personalChanges,
1093
1095
  };
1094
1096
 
@@ -1096,7 +1098,7 @@ async function personalDetailsOnReady({
1096
1098
  console.group('Personal Details Save Attempt');
1097
1099
  console.log('Current Data:', beforeData);
1098
1100
  console.log('Changes Being Applied:', personalChanges);
1099
- console.log('Final Form Data:', formData);
1101
+ console.log('Final Form Data (partial):', formData);
1100
1102
  console.groupEnd();
1101
1103
 
1102
1104
  const result = await saveData(formData);
@@ -1128,8 +1130,10 @@ async function personalDetailsOnReady({
1128
1130
  const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
1129
1131
  const businessChanges = getBusinessAndServicesData();
1130
1132
 
1133
+ // Only send business fields + required identifiers (partial update)
1131
1134
  const formData = {
1132
- ...itemMemberObj,
1135
+ _id: itemMemberObj._id,
1136
+ memberId: itemMemberObj.memberId,
1133
1137
  ...businessChanges,
1134
1138
  };
1135
1139
 
@@ -1137,7 +1141,7 @@ async function personalDetailsOnReady({
1137
1141
  console.group('Business Services Save Attempt');
1138
1142
  console.log('Current Data:', beforeData);
1139
1143
  console.log('Changes Being Applied:', businessChanges);
1140
- console.log('Final Form Data:', formData);
1144
+ console.log('Final Form Data (partial):', formData);
1141
1145
  console.log('Image Changes:', {
1142
1146
  profileImage: uploadedImages.profileImage,
1143
1147
  logoImage: uploadedImages.logoImage,
@@ -1959,8 +1963,10 @@ async function personalDetailsOnReady({
1959
1963
  const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
1960
1964
  const contactChanges = getContactAndBookingData();
1961
1965
 
1966
+ // Only send contact fields + required identifiers (partial update)
1962
1967
  const formData = {
1963
- ...itemMemberObj,
1968
+ _id: itemMemberObj._id,
1969
+ memberId: itemMemberObj.memberId,
1964
1970
  ...contactChanges,
1965
1971
  };
1966
1972
 
@@ -1968,7 +1974,7 @@ async function personalDetailsOnReady({
1968
1974
  console.group('Contact & Booking Save Attempt');
1969
1975
  console.log('Current Data:', beforeData);
1970
1976
  console.log('Changes Being Applied:', contactChanges);
1971
- console.log('Final Form Data:', formData);
1977
+ console.log('Final Form Data (partial):', formData);
1972
1978
  console.log('Address Changes:', {
1973
1979
  addressCount: contactChanges.addresses?.length || 0,
1974
1980
  addressDisplayOptions: contactChanges.addressDisplayOption,
@@ -2032,8 +2038,10 @@ async function personalDetailsOnReady({
2032
2038
  }
2033
2039
 
2034
2040
  async function saveGalleryToCMS() {
2041
+ // Only send gallery field + required identifiers (partial update)
2035
2042
  const formData = {
2036
- ...itemMemberObj,
2043
+ _id: itemMemberObj._id,
2044
+ memberId: itemMemberObj.memberId,
2037
2045
  gallery: itemMemberObj.gallery,
2038
2046
  };
2039
2047
 
@@ -120,9 +120,11 @@ const createHomepageUtils = (_$w, filterProfiles) => {
120
120
  });
121
121
  async function handlePagination({ delta, pagination, searchResults, filter }) {
122
122
  const newPage = pagination.currentPage + delta;
123
- if (newPage < 0 || newPage > 9) return;
123
+ const maxPage = pagination.totalPages ? pagination.totalPages - 1 : 0;
124
+ if (newPage < 0 || newPage > maxPage) return;
125
+
124
126
  newPage === 0 ? _$w('#previousPage').disable() : _$w('#previousPage').enable();
125
- newPage === 9 ? _$w('#nextPage').disable() : _$w('#nextPage').enable();
127
+ newPage === maxPage ? _$w('#nextPage').disable() : _$w('#nextPage').enable();
126
128
  pagination.currentPage = newPage;
127
129
 
128
130
  paginateSearchResults(searchResults, pagination);
@@ -466,22 +468,21 @@ const createHomepageUtils = (_$w, filterProfiles) => {
466
468
  currentPageButtonIterator.collapse();
467
469
  }
468
470
  }
469
- if (noOfPages === 0 || noOfPages === 1) {
471
+ if (totalPages === 0 || totalPages === 1) {
470
472
  _$w('#previousPage').disable();
471
473
  _$w('#nextPage').disable();
472
474
  return;
473
475
  }
474
476
  if (currentPage === 0) {
475
477
  _$w('#previousPage').disable();
476
- return;
478
+ } else {
479
+ _$w('#previousPage').enable();
477
480
  }
478
- if (currentPage === noOfPages - 1) {
481
+ if (currentPage >= totalPages - 1) {
479
482
  _$w('#nextPage').disable();
480
- return;
483
+ } else {
484
+ _$w('#nextPage').enable();
481
485
  }
482
-
483
- _$w('#previousPage').enable();
484
- _$w('#nextPage').enable();
485
486
  }
486
487
  function paginateSearchResults(searchResults, pagination) {
487
488
  updatePaginationUI(pagination);