abmp-npm 2.0.76 → 2.0.78

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/package.json +8 -1
  2. package/pages/Home.js +27 -55
  3. package/pages/index.js +0 -1
  4. package/public/Utils/personalDetailsUtils.js +5 -1
  5. package/public/Utils/sharedUtils.js +32 -0
  6. package/public/consts.js +0 -1
  7. package/.claude/worktrees/fix-save-button-disable/.husky/pre-commit +0 -19
  8. package/.claude/worktrees/fix-save-button-disable/.prettierignore +0 -7
  9. package/.claude/worktrees/fix-save-button-disable/.prettierrc.json +0 -16
  10. package/.claude/worktrees/fix-save-button-disable/README.md +0 -1
  11. package/.claude/worktrees/fix-save-button-disable/backend/README-member-contact-flows.md +0 -136
  12. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  13. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  14. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/login-email-sync.test.js +0 -49
  15. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  16. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/url-uniqueness.test.js +0 -194
  17. package/.claude/worktrees/fix-save-button-disable/backend/automations-methods.js +0 -13
  18. package/.claude/worktrees/fix-save-button-disable/backend/cms-data-methods.js +0 -251
  19. package/.claude/worktrees/fix-save-button-disable/backend/consts.js +0 -58
  20. package/.claude/worktrees/fix-save-button-disable/backend/contacts-methods.js +0 -67
  21. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/bulk-process-methods.js +0 -222
  22. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/consts.js +0 -39
  23. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/index.js +0 -5
  24. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/process-member-methods.js +0 -213
  25. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/schedule-methods.js +0 -41
  26. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/sync-to-cms-methods.js +0 -161
  27. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/utils.js +0 -151
  28. package/.claude/worktrees/fix-save-button-disable/backend/data-hooks.js +0 -29
  29. package/.claude/worktrees/fix-save-button-disable/backend/dev-only-methods.js +0 -58
  30. package/.claude/worktrees/fix-save-button-disable/backend/elevated-modules.js +0 -18
  31. package/.claude/worktrees/fix-save-button-disable/backend/forms-methods.js +0 -52
  32. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/httpFunctions.js +0 -86
  33. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/index.js +0 -3
  34. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/interests.js +0 -35
  35. package/.claude/worktrees/fix-save-button-disable/backend/index.js +0 -19
  36. package/.claude/worktrees/fix-save-button-disable/backend/jobs.js +0 -158
  37. package/.claude/worktrees/fix-save-button-disable/backend/login/generate-member-session-token.js +0 -27
  38. package/.claude/worktrees/fix-save-button-disable/backend/login/index.js +0 -8
  39. package/.claude/worktrees/fix-save-button-disable/backend/login/qa-login-methods.js +0 -54
  40. package/.claude/worktrees/fix-save-button-disable/backend/login/sso-methods.js +0 -159
  41. package/.claude/worktrees/fix-save-button-disable/backend/member-contact-orchestration.js +0 -111
  42. package/.claude/worktrees/fix-save-button-disable/backend/members-area-methods.js +0 -88
  43. package/.claude/worktrees/fix-save-button-disable/backend/members-data-methods.js +0 -789
  44. package/.claude/worktrees/fix-save-button-disable/backend/pac-api-methods.js +0 -54
  45. package/.claude/worktrees/fix-save-button-disable/backend/routers/index.js +0 -3
  46. package/.claude/worktrees/fix-save-button-disable/backend/routers/methods.js +0 -191
  47. package/.claude/worktrees/fix-save-button-disable/backend/routers/utils.js +0 -128
  48. package/.claude/worktrees/fix-save-button-disable/backend/search-filters-methods.js +0 -124
  49. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-primary-methods.js +0 -256
  50. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-visibility-methods.js +0 -160
  51. package/.claude/worktrees/fix-save-button-disable/backend/tasks/consts.js +0 -34
  52. package/.claude/worktrees/fix-save-button-disable/backend/tasks/daily-pull-check-methods.js +0 -78
  53. package/.claude/worktrees/fix-save-button-disable/backend/tasks/email-normalize-methods.js +0 -134
  54. package/.claude/worktrees/fix-save-button-disable/backend/tasks/index.js +0 -10
  55. package/.claude/worktrees/fix-save-button-disable/backend/tasks/migration-methods.js +0 -46
  56. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-configs.js +0 -251
  57. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-helpers-methods.js +0 -433
  58. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-process-methods.js +0 -606
  59. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-migration-methods.js +0 -378
  60. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-space-fix-methods.js +0 -174
  61. package/.claude/worktrees/fix-save-button-disable/backend/utils.js +0 -277
  62. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  63. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-ids.js +0 -159
  64. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-urls.js +0 -201
  65. package/.claude/worktrees/fix-save-button-disable/eslint.config.js +0 -120
  66. package/.claude/worktrees/fix-save-button-disable/index.js +0 -5
  67. package/.claude/worktrees/fix-save-button-disable/package-lock.json +0 -12160
  68. package/.claude/worktrees/fix-save-button-disable/package.json +0 -60
  69. package/.claude/worktrees/fix-save-button-disable/pages/ContactUs.js +0 -129
  70. package/.claude/worktrees/fix-save-button-disable/pages/Home.js +0 -779
  71. package/.claude/worktrees/fix-save-button-disable/pages/LearnMore.js +0 -27
  72. package/.claude/worktrees/fix-save-button-disable/pages/LoadingPage.js +0 -29
  73. package/.claude/worktrees/fix-save-button-disable/pages/Profile.js +0 -354
  74. package/.claude/worktrees/fix-save-button-disable/pages/QAPage.js +0 -39
  75. package/.claude/worktrees/fix-save-button-disable/pages/SaveAlerts.js +0 -13
  76. package/.claude/worktrees/fix-save-button-disable/pages/SelectBannerImages.js +0 -46
  77. package/.claude/worktrees/fix-save-button-disable/pages/deleteConfirm.js +0 -19
  78. package/.claude/worktrees/fix-save-button-disable/pages/index.js +0 -12
  79. package/.claude/worktrees/fix-save-button-disable/pages/personalDetails.js +0 -2486
  80. package/.claude/worktrees/fix-save-button-disable/public/Utils/homePage.js +0 -798
  81. package/.claude/worktrees/fix-save-button-disable/public/Utils/personalDetailsUtils.js +0 -25
  82. package/.claude/worktrees/fix-save-button-disable/public/Utils/sharedUtils.js +0 -235
  83. package/.claude/worktrees/fix-save-button-disable/public/consts.js +0 -113
  84. package/.claude/worktrees/fix-save-button-disable/public/index.js +0 -5
  85. package/.claude/worktrees/fix-save-button-disable/public/messages.js +0 -16
  86. package/.claude/worktrees/fix-save-button-disable/public/sso-auth-methods.js +0 -43
  87. package/.husky/pre-commit +0 -19
  88. package/.prettierignore +0 -7
  89. package/.prettierrc.json +0 -16
  90. package/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  91. package/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  92. package/backend/__tests__/login-email-sync.test.js +0 -49
  93. package/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  94. package/backend/__tests__/url-uniqueness.test.js +0 -194
  95. package/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  96. package/dev-only-scripts/find-duplicate-ids.js +0 -159
  97. package/dev-only-scripts/find-duplicate-urls.js +0 -201
  98. package/eslint.config.js +0 -120
  99. package/pages/contactForLocation.js +0 -28
@@ -1,67 +0,0 @@
1
- const { contacts } = require('@wix/crm');
2
- const { auth } = require('@wix/essentials');
3
-
4
- const elevatedGetContact = auth.elevate(contacts.getContact);
5
- const elevatedUpdateContact = auth.elevate(contacts.updateContact);
6
- const elevatedCreateContact = auth.elevate(contacts.createContact);
7
- const elevatedDeleteContact = auth.elevate(contacts.deleteContact);
8
-
9
- const deleteSiteContact = contactId => elevatedDeleteContact(contactId);
10
-
11
- /**
12
- * Create a contact in Wix CRM
13
- * @param {Object} contactData - Contact data
14
- * @returns {Promise<Object>} - Contact data
15
- */
16
- async function createSiteContact(contactData) {
17
- console.log('[createSiteContact]contactData', JSON.stringify(contactData, null, 2));
18
- if (!contactData || !contactData.contactFormEmail) {
19
- throw new Error('Contact data is required');
20
- }
21
- const contactInfo = {
22
- name: {
23
- first: contactData.firstName,
24
- last: contactData.lastName,
25
- },
26
- emails: {
27
- items: [{ email: contactData.contactFormEmail, primary: true }],
28
- },
29
- };
30
- console.log('[createSiteContact]contactInfo', JSON.stringify(contactInfo, null, 2));
31
- // Intentionally NOT passing allowDuplicates: Wix CRM enforces email uniqueness
32
- // case-insensitively, which is the guard we want against two different members sharing an
33
- // email. Same-member cases never reach here — they collapse to a single entity upstream.
34
- const createContactResponse = await elevatedCreateContact(contactInfo);
35
- console.log(
36
- '[createSiteContact]createContactResponse',
37
- JSON.stringify(createContactResponse, null, 2)
38
- );
39
- return createContactResponse.contact._id;
40
- }
41
- /**
42
- * Generic contact update helper function
43
- * @param {string} contactId - The contact ID in Wix CRM
44
- * @param {function} updateInfoCallback - Function that returns the updated info object
45
- * @param {string} operationName - Name of the operation for logging
46
- */
47
- async function updateContactInfo(contactId, updateInfoCallback, operationName) {
48
- if (!contactId) {
49
- throw new Error('Contact ID is required');
50
- }
51
-
52
- try {
53
- const contact = await elevatedGetContact(contactId);
54
- const currentInfo = contact.info;
55
- const updatedInfo = updateInfoCallback(currentInfo);
56
- await elevatedUpdateContact(contactId, updatedInfo, contact.revision);
57
- } catch (error) {
58
- console.error(`Error in ${operationName}:`, error);
59
- throw new Error(`Failed to ${operationName}: ${error.message}`);
60
- }
61
- }
62
-
63
- module.exports = {
64
- createSiteContact,
65
- updateContactInfo,
66
- deleteSiteContact,
67
- };
@@ -1,222 +0,0 @@
1
- const { bulkSaveMembers, getMemberBySlug, findMembersByIds } = require('../members-data-methods');
2
- const { extractUrlCounter } = require('../utils');
3
-
4
- const { generateUpdatedMemberData } = require('./process-member-methods');
5
- const {
6
- changeWixMembersEmails,
7
- summarizeLoginEmailOutcomes,
8
- incrementUrlCounter,
9
- extractBaseUrl,
10
- } = require('./utils');
11
-
12
- /**
13
- * Ensures unique URLs within a batch of members by deduplicating URLs
14
- * Groups members by their base URL (normalized) and assigns unique counters
15
- * Also checks database to handle cross-page conflicts
16
- * @param {Array} memberDataList - Array of processed member data
17
- * @returns {Promise<Array>} - Array of members with unique URLs assigned
18
- */
19
- async function ensureUniqueUrlsInBatch(memberDataList) {
20
- if (!Array.isArray(memberDataList) || memberDataList.length === 0) {
21
- return memberDataList;
22
- }
23
-
24
- // Group members by their normalized base URL (case-insensitive to avoid
25
- // "John-12" and "john-12" being treated as different when slugs are matched case-insensitively)
26
- const urlGroups = new Map();
27
-
28
- memberDataList.forEach(member => {
29
- if (!member || !member.url) {
30
- return;
31
- }
32
-
33
- const baseUrl = extractBaseUrl(member.url);
34
- const groupKey = baseUrl.toLowerCase();
35
- if (!urlGroups.has(groupKey)) {
36
- urlGroups.set(groupKey, { members: [], baseUrl });
37
- }
38
- urlGroups.get(groupKey).members.push(member);
39
- });
40
-
41
- // For each group, check database and assign unique URLs sequentially
42
- for (const [, { members, baseUrl }] of urlGroups.entries()) {
43
- if (members.length <= 1) {
44
- // Single member - still check DB to ensure it doesn't conflict with other pages
45
- const member = members[0];
46
- if (member) {
47
- const dbMember = await getMemberBySlug({
48
- slug: baseUrl,
49
- excludeDropped: false,
50
- normalizeSlugForComparison: true,
51
- });
52
-
53
- if (dbMember && dbMember.url) {
54
- // Conflict found in DB, need to add counter
55
- member.url = incrementUrlCounter(dbMember.url, baseUrl);
56
- console.log(
57
- `Found DB conflict for single member with base URL "${baseUrl}", assigned: ${member.url}`
58
- );
59
- }
60
- }
61
- continue;
62
- }
63
-
64
- // Sort members to ensure consistent ordering
65
- members.sort((a, b) => {
66
- if (a.url && b.url) {
67
- return String(a.url).localeCompare(String(b.url));
68
- }
69
- return 0;
70
- });
71
-
72
- // Check database for existing members with this base URL to find highest counter
73
- const dbMember = await getMemberBySlug({
74
- slug: baseUrl,
75
- excludeDropped: false,
76
- normalizeSlugForComparison: true,
77
- });
78
-
79
- const dbMaxCounter = extractUrlCounter(dbMember?.url);
80
-
81
- // Find the highest existing counter among all members in this batch group
82
- let batchMaxCounter = -1;
83
- members.forEach(member => {
84
- const originalUrl = member.url;
85
- const urlCounter = extractUrlCounter(originalUrl);
86
- if (urlCounter > batchMaxCounter) {
87
- batchMaxCounter = urlCounter;
88
- }
89
- });
90
-
91
- // Start index from the maximum of DB counter and batch counter + 1
92
- const maxCounter = Math.max(dbMaxCounter, batchMaxCounter);
93
- const startIndex = maxCounter + 1;
94
-
95
- // Assign unique URLs: start from the appropriate index
96
- members.forEach((member, index) => {
97
- const assignedIndex = startIndex + index;
98
- if (assignedIndex === 0) {
99
- // Index 0 means no counter, use baseUrl
100
- member.url = baseUrl;
101
- } else {
102
- // Index > 0 means add counter
103
- member.url = `${baseUrl}-${assignedIndex}`;
104
- }
105
- });
106
-
107
- console.log(
108
- `Deduplicated ${
109
- members.length
110
- } members with base URL "${baseUrl}" (DB max: ${dbMaxCounter}, batch max: ${batchMaxCounter}, start: ${startIndex}): ${members
111
- .map(m => m.url)
112
- .join(', ')}`
113
- );
114
- }
115
-
116
- return memberDataList;
117
- }
118
-
119
- /**
120
- * Processes and saves multiple member records in bulk
121
- * @param {Object} options - The options object
122
- * @param {Array} options.memberDataList - Array of member data from API
123
- * @param {number} options.currentPageNumber - Current page number being processed
124
- * @param {Array} memberDataList - Array of member data from API
125
- * @returns {Promise<Object>} - Bulk save operation result with statistics
126
- */
127
- const bulkProcessAndSaveMemberData = async ({ memberDataList, currentPageNumber }) => {
128
- if (!Array.isArray(memberDataList) || memberDataList.length === 0) {
129
- throw new Error('Invalid member data list provided');
130
- }
131
-
132
- const startTime = Date.now();
133
-
134
- try {
135
- // Prefetch all existing members for this page in a few chunked queries instead of
136
- // one query per member — thousands of parallel lookups made the whole page fail
137
- // whenever a single one hit a transient network error.
138
- const existingMembersById = await findMembersByIds(
139
- memberDataList.map(memberData => memberData?.memberid)
140
- );
141
-
142
- const processedMemberDataPromises = memberDataList.map(memberData =>
143
- generateUpdatedMemberData({
144
- inputMemberData: memberData,
145
- currentPageNumber,
146
- existingDbMember: existingMembersById.get(String(memberData?.memberid)) ?? null,
147
- })
148
- );
149
-
150
- const processedMemberDataList = await Promise.all(processedMemberDataPromises);
151
- const validMemberData = processedMemberDataList.filter(
152
- data => data !== null && data !== undefined
153
- );
154
- if (validMemberData.length === 0) {
155
- return {
156
- totalProcessed: memberDataList.length,
157
- totalSaved: 0,
158
- totalFailed: memberDataList.length,
159
- processingTime: Date.now() - startTime,
160
- };
161
- }
162
- const newMembers = validMemberData.filter(data => data.isNewToDb);
163
- const existingMembers = validMemberData.filter(data => !data.isNewToDb);
164
- // Ensure unique URLs within the batch to prevent duplicates (also checks DB for cross-page conflicts)
165
- const uniqueUrlsNewToDBMembersList = await ensureUniqueUrlsInBatch(newMembers);
166
- const uniqueUrlsMembersData = [...uniqueUrlsNewToDBMembersList, ...existingMembers];
167
-
168
- // Change Wix login emails FIRST so we know which succeeded before saving the CMS records.
169
- const toChangeWixMembersEmails = uniqueUrlsMembersData.filter(
170
- member => member.wixMemberId && member.isLoginEmailChanged
171
- );
172
- let failedLoginEmailIds = new Set();
173
- let loginEmailFailures = [];
174
- if (toChangeWixMembersEmails.length > 0) {
175
- const outcomes = await changeWixMembersEmails(toChangeWixMembersEmails);
176
- ({ failedMemberIds: failedLoginEmailIds, failures: loginEmailFailures } =
177
- summarizeLoginEmailOutcomes(outcomes));
178
- }
179
-
180
- const toSaveMembersData = uniqueUrlsMembersData.map(member => {
181
- // Transient flags only drive the sync above; never persist them.
182
- const {
183
- isLoginEmailChanged: _isLoginEmailChanged,
184
- isNewToDb: _isNewToDb,
185
- previousLoginEmail,
186
- ...restMemberData
187
- } = member;
188
- // If the Wix login-email change failed, keep the CMS login email unchanged (revert to the
189
- // previous value) so the CMS stays consistent with Wix; the failure is reported below for
190
- // manual handling.
191
- if (failedLoginEmailIds.has(member.memberId) && previousLoginEmail !== undefined) {
192
- return { ...restMemberData, email: previousLoginEmail };
193
- }
194
- return restMemberData;
195
- });
196
- const saveResult = await bulkSaveMembers(toSaveMembersData);
197
- const totalFailed = memberDataList.length - validMemberData.length;
198
- const processingTime = Date.now() - startTime;
199
-
200
- if (loginEmailFailures.length > 0) {
201
- console.error(
202
- `[loginEmailSync] ${loginEmailFailures.length} login-email change(s) failed on page ${currentPageNumber}; CMS login email left unchanged for memberIds: [${loginEmailFailures
203
- .map(failure => failure.memberId)
204
- .join(', ')}]`
205
- );
206
- }
207
-
208
- return {
209
- ...saveResult,
210
- totalProcessed: memberDataList.length,
211
- totalSaved: validMemberData.length,
212
- totalFailed: totalFailed,
213
- processingTime: processingTime,
214
- loginEmailFailedCount: loginEmailFailures.length,
215
- loginEmailFailures,
216
- };
217
- } catch (error) {
218
- throw new Error(`Bulk operation failed: ${error.message}`);
219
- }
220
- };
221
-
222
- module.exports = { bulkProcessAndSaveMemberData, ensureUniqueUrlsInBatch };
@@ -1,39 +0,0 @@
1
- const MEMBER_ACTIONS = {
2
- UPDATE: 'update',
3
- NEW: 'new',
4
- DROP: 'drop',
5
- NONE: 'none',
6
- };
7
-
8
- /**
9
- * Address visibility configuration options
10
- */
11
- const ADDRESS_VISIBILITY_OPTIONS = {
12
- ALL: 'all',
13
- NONE: 'none',
14
- };
15
-
16
- /**
17
- * Default display settings for member profiles
18
- */
19
- const DEFAULT_MEMBER_DISPLAY_SETTINGS = {
20
- showLicenseNo: true,
21
- showName: true,
22
- showBookingUrl: false,
23
- showWebsite: false,
24
- showWixUrl: true,
25
- };
26
-
27
- const PAC_ASSOCIATIONS = {
28
- ABMP: 'ABMP',
29
- ASCP: 'ASCP',
30
- ANP: 'ANP',
31
- AHP: 'AHP',
32
- };
33
-
34
- module.exports = {
35
- MEMBER_ACTIONS,
36
- ADDRESS_VISIBILITY_OPTIONS,
37
- DEFAULT_MEMBER_DISPLAY_SETTINGS,
38
- PAC_ASSOCIATIONS,
39
- };
@@ -1,5 +0,0 @@
1
- module.exports = {
2
- ...require('./sync-to-cms-methods'),
3
- ...require('./consts'), //TODO: remove it once we finish NPM movement
4
- ...require('./bulk-process-methods'),
5
- };
@@ -1,213 +0,0 @@
1
- const { ADDRESS_STATUS_TYPES } = require('../../public/consts');
2
- const { findMemberById, getMemberBySlug } = require('../members-data-methods');
3
- const { isValidArray, generateGeoHash } = require('../utils');
4
-
5
- const { MEMBER_ACTIONS, DEFAULT_MEMBER_DISPLAY_SETTINGS } = require('./consts');
6
- const { incrementUrlCounter } = require('./utils');
7
- const { validateCoreMemberData, containsNonEnglish, createFullName } = require('./utils');
8
-
9
- /**
10
- * Ensures a URL is unique by appending a counter if necessary
11
- * @param {Object} options - The options object
12
- * @param {string} options.url - The base URL to make unique
13
- * @param {string|number} options.memberId - The member ID requesting this URL
14
- * @param {string} options.fullName - The full name of the member
15
- * @returns {Promise<string>} - A unique URL
16
- */
17
- const ensureUniqueUrl = async ({ url, memberId, fullName }) => {
18
- const baseUrl = url;
19
- let uniqueUrl = url;
20
- if (!url) {
21
- console.log(`member with id ${memberId} has no url, creating one`);
22
- const fullNameWithoutSpace = fullName?.replace(/ /g, '');
23
- if (!fullNameWithoutSpace || containsNonEnglish(fullNameWithoutSpace)) {
24
- console.log(
25
- `member with id ${memberId} has non-english full name, will use fallback url: 'firstNameLastName'`
26
- );
27
- uniqueUrl = 'firstNameLastName';
28
- } else {
29
- uniqueUrl = fullNameWithoutSpace; //fallback if there is no full name for this user
30
- }
31
- console.log(
32
- `member with id ${memberId} and no API provided url, will have this initial url ${uniqueUrl}`
33
- );
34
- }
35
- if (!memberId) throw new Error('Member ID is required');
36
-
37
- const existingMember = await getMemberBySlug({
38
- slug: uniqueUrl,
39
- excludeDropped: false,
40
- excludeSearchedMember: true,
41
- memberId,
42
- normalizeSlugForComparison: true,
43
- });
44
- if (existingMember && existingMember.url) {
45
- console.log(
46
- `Found member with same url ${existingMember.url} for memberId ${memberId} and URL ${uniqueUrl}, increasing counter by 1`
47
- );
48
- uniqueUrl = incrementUrlCounter(existingMember.url, uniqueUrl);
49
- }
50
- if (uniqueUrl !== baseUrl) {
51
- console.log(`URL conflict resolved: ${baseUrl} -> ${uniqueUrl} for member ${memberId}`);
52
- }
53
- return uniqueUrl;
54
- };
55
-
56
- /**
57
- * Generates complete updated member data by combining existing and migration data
58
- * @param {Object} options - The options object
59
- * @param {Object} options.inputMemberData - Raw member data from API
60
- * @param {number} options.currentPageNumber - Current page number being processed
61
- * @param {Object|null} [options.existingDbMember] - Prefetched DB member (null if none exists).
62
- * Pass it when processing members in bulk to avoid one DB query per member; when omitted,
63
- * the member is looked up individually.
64
- * @returns {Promise<Object|null>} - Complete updated member data or null if validation fails
65
- */
66
- async function generateUpdatedMemberData({
67
- inputMemberData,
68
- currentPageNumber,
69
- existingDbMember: prefetchedDbMember,
70
- }) {
71
- if (!validateCoreMemberData(inputMemberData)) {
72
- throw new Error(
73
- 'Invalid member data: memberid, email (valid string), and memberships (array) are required'
74
- );
75
- }
76
-
77
- const existingDbMember =
78
- prefetchedDbMember === undefined
79
- ? await findMemberById(inputMemberData.memberid)
80
- : prefetchedDbMember;
81
-
82
- const updatedMemberData = await createCoreMemberData(
83
- inputMemberData,
84
- existingDbMember,
85
- currentPageNumber
86
- );
87
-
88
- // If createCoreMemberData returns null due to validation failure, return null
89
- if (!updatedMemberData) {
90
- return null;
91
- }
92
-
93
- // Only add address data for new members
94
- if (!existingDbMember && isValidArray(inputMemberData.addresses)) {
95
- const normalizedAddresses = inputMemberData.addresses.map((address, index) => {
96
- const key = address?.key || `address_${index}`;
97
- const rawStatus = address?.addressStatus;
98
- const resolvedStatus =
99
- index === 0 && rawStatus === ADDRESS_STATUS_TYPES.DONT_SHOW
100
- ? ADDRESS_STATUS_TYPES.STATE_CITY_ZIP
101
- : rawStatus || ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
102
- return {
103
- ...address,
104
- key,
105
- addressStatus: resolvedStatus,
106
- };
107
- });
108
- updatedMemberData.addresses = normalizedAddresses;
109
- updatedMemberData.addressDisplayOption = [{ key: normalizedAddresses[0].key, isMain: true }];
110
- }
111
-
112
- return { ...updatedMemberData, isNewToDb: !existingDbMember };
113
- }
114
- /**
115
- * Helper function to get fields that should only be set for new members
116
- * @param {Object} inputMemberData - Raw member data from API
117
- * @param {Object} existingDbMember - Existing member data from database
118
- * @returns {Promise<Object>} - Object with fields that should only be set for new members
119
- */
120
- async function getNewMemberOnlyFields(inputMemberData, existingDbMember) {
121
- if (existingDbMember) {
122
- return {};
123
- }
124
-
125
- // Only set these fields for new members
126
- const sanitizedFirstName = inputMemberData.firstname?.trim() || '';
127
- const sanitizedLastName = inputMemberData.lastname?.trim() || '';
128
- const fullName = createFullName(sanitizedFirstName, sanitizedLastName);
129
-
130
- const uniqueUrl = await ensureUniqueUrl({
131
- url: inputMemberData.url,
132
- memberId: inputMemberData.memberid,
133
- fullName,
134
- });
135
- return {
136
- memberId: inputMemberData.memberid,
137
- firstName: sanitizedFirstName,
138
- lastName: sanitizedLastName,
139
- fullName,
140
- phones: inputMemberData.phones || [],
141
- optOut: false,
142
- url: uniqueUrl,
143
- showContactForm: true,
144
- showABMP: false,
145
- locHash: generateGeoHash(inputMemberData.addresses || []),
146
- ...DEFAULT_MEMBER_DISPLAY_SETTINGS,
147
- };
148
- }
149
- /**
150
- * Creates base member data structure with core properties
151
- * @param {Object} inputMemberData - Raw member data from API
152
- * @param {Object} existingDbMember - Existing member data from database
153
- * @param {number} currentPageNumber - Current page number being processed
154
- * @returns {Promise<Object|null>} - Structured base member data or null if required fields are missing
155
- */
156
- async function createCoreMemberData(inputMemberData, existingDbMember, currentPageNumber) {
157
- if (!validateCoreMemberData(inputMemberData)) {
158
- return null;
159
- }
160
-
161
- const getMemberEmails = () => {
162
- // Update both loginEmail & contactFormEmail only for new members who don't exist in DB
163
- // Note: PAC API member actions are not reliable, so we need to check if the member exists in DB to know if it's a new member or not
164
- const newEmail = inputMemberData.email.trim();
165
- const isMemberExistInDb = Boolean(existingDbMember?._id);
166
- if (!isMemberExistInDb) {
167
- return {
168
- email: newEmail,
169
- contactFormEmail: newEmail,
170
- };
171
- }
172
- //If exists in DB then only update loginEmail for those who came in with action new based on logic below, otherwise we don't update emails
173
- const isMemberReinstatedWithNewEmail =
174
- inputMemberData.action === MEMBER_ACTIONS.NEW &&
175
- newEmail &&
176
- existingDbMember.email !== newEmail;
177
- if (isMemberReinstatedWithNewEmail) {
178
- // If exists in DB, and email was changed means this user was dropped before that's why it exists in DB, then only update loginEmail not contactFormEmail.
179
- // previousLoginEmail lets the caller keep the old email if the Wix login-email change fails.
180
- return {
181
- email: newEmail,
182
- isLoginEmailChanged: true,
183
- previousLoginEmail: existingDbMember.email,
184
- };
185
- }
186
- //If exists in DB but not reinstated with new email, then don't update emails
187
- return {};
188
- };
189
-
190
- const newMemberFields = await getNewMemberOnlyFields(inputMemberData, existingDbMember);
191
-
192
- return {
193
- ...existingDbMember,
194
-
195
- // Always update these core fields from API
196
- action: inputMemberData.action,
197
- licenses: inputMemberData.licenses || [],
198
- memberships: inputMemberData.memberships,
199
- pageNumber: currentPageNumber,
200
- isVisible: inputMemberData.action !== MEMBER_ACTIONS.DROP,
201
-
202
- // Handle Member emails
203
- ...getMemberEmails(),
204
-
205
- // Handle fields that should only be set for new members
206
- ...newMemberFields,
207
- };
208
- }
209
-
210
- module.exports = {
211
- generateUpdatedMemberData,
212
- ensureUniqueUrl,
213
- };
@@ -1,41 +0,0 @@
1
- const { taskManager } = require('psdev-task-manager');
2
-
3
- const { TASKS_NAMES } = require('../tasks/consts');
4
-
5
- const { MEMBER_ACTIONS } = require('./consts');
6
-
7
- /**
8
- * Builds the per-action daily pull tasks. Single source of truth for what a
9
- * "daily pull" schedules, shared by the cron job and the execution-check
10
- * fallback so both produce identical tasks (same names, same environment flags).
11
- * @param {Object} [options]
12
- * @param {string} [options.backupDate] - Optional backup date (YYYY-MM-DD) to pull from the backup endpoint
13
- * @param {boolean} [options.isTestEnvironment=false] - Whether to pull from the test PAC API
14
- * @param {boolean} [options.includeNone=false] - Whether to also sync the NONE action
15
- * @returns {Array<Object>} - Task definitions for taskManager().scheduleInBulk
16
- */
17
- const buildDailyPullTasks = ({ backupDate, isTestEnvironment, includeNone } = {}) => {
18
- const actionsToSync = includeNone
19
- ? Object.values(MEMBER_ACTIONS)
20
- : Object.values(MEMBER_ACTIONS).filter(action => action !== MEMBER_ACTIONS.NONE);
21
- return actionsToSync.map(action => ({
22
- name: TASKS_NAMES.ScheduleMembersDataPerAction,
23
- data: {
24
- action,
25
- ...(backupDate ? { backupDate } : {}),
26
- ...(isTestEnvironment ? { isTestEnvironment } : {}),
27
- ...(includeNone ? { includeNone } : {}),
28
- },
29
- type: 'scheduled',
30
- }));
31
- };
32
-
33
- /**
34
- * Schedules the daily pull (one ScheduleMembersDataPerAction task per action).
35
- * @param {Object} [options] - Same options as buildDailyPullTasks
36
- * @returns {Promise<any>}
37
- */
38
- const scheduleDailyPullTasks = options =>
39
- taskManager().scheduleInBulk(buildDailyPullTasks(options));
40
-
41
- module.exports = { buildDailyPullTasks, scheduleDailyPullTasks };