abmp-npm 2.0.77 → 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/.claude/skills/wix-data-query/SKILL.md +0 -167
  3. package/.claude/skills/wix-data-query/references/backup-archive.md +0 -136
  4. package/.claude/skills/wix-data-query/references/members-data-latest.md +0 -125
  5. package/.claude/skills/wix-data-query/references/recipes.md +0 -264
  6. package/.claude/worktrees/fix-save-button-disable/.husky/pre-commit +0 -19
  7. package/.claude/worktrees/fix-save-button-disable/.prettierignore +0 -7
  8. package/.claude/worktrees/fix-save-button-disable/.prettierrc.json +0 -16
  9. package/.claude/worktrees/fix-save-button-disable/README.md +0 -1
  10. package/.claude/worktrees/fix-save-button-disable/backend/README-member-contact-flows.md +0 -136
  11. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  12. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  13. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/login-email-sync.test.js +0 -49
  14. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  15. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/url-uniqueness.test.js +0 -194
  16. package/.claude/worktrees/fix-save-button-disable/backend/automations-methods.js +0 -13
  17. package/.claude/worktrees/fix-save-button-disable/backend/cms-data-methods.js +0 -251
  18. package/.claude/worktrees/fix-save-button-disable/backend/consts.js +0 -58
  19. package/.claude/worktrees/fix-save-button-disable/backend/contacts-methods.js +0 -67
  20. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/bulk-process-methods.js +0 -222
  21. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/consts.js +0 -39
  22. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/index.js +0 -5
  23. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/process-member-methods.js +0 -213
  24. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/schedule-methods.js +0 -41
  25. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/sync-to-cms-methods.js +0 -161
  26. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/utils.js +0 -151
  27. package/.claude/worktrees/fix-save-button-disable/backend/data-hooks.js +0 -29
  28. package/.claude/worktrees/fix-save-button-disable/backend/dev-only-methods.js +0 -58
  29. package/.claude/worktrees/fix-save-button-disable/backend/elevated-modules.js +0 -18
  30. package/.claude/worktrees/fix-save-button-disable/backend/forms-methods.js +0 -52
  31. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/httpFunctions.js +0 -86
  32. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/index.js +0 -3
  33. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/interests.js +0 -35
  34. package/.claude/worktrees/fix-save-button-disable/backend/index.js +0 -19
  35. package/.claude/worktrees/fix-save-button-disable/backend/jobs.js +0 -158
  36. package/.claude/worktrees/fix-save-button-disable/backend/login/generate-member-session-token.js +0 -27
  37. package/.claude/worktrees/fix-save-button-disable/backend/login/index.js +0 -8
  38. package/.claude/worktrees/fix-save-button-disable/backend/login/qa-login-methods.js +0 -54
  39. package/.claude/worktrees/fix-save-button-disable/backend/login/sso-methods.js +0 -159
  40. package/.claude/worktrees/fix-save-button-disable/backend/member-contact-orchestration.js +0 -111
  41. package/.claude/worktrees/fix-save-button-disable/backend/members-area-methods.js +0 -88
  42. package/.claude/worktrees/fix-save-button-disable/backend/members-data-methods.js +0 -789
  43. package/.claude/worktrees/fix-save-button-disable/backend/pac-api-methods.js +0 -54
  44. package/.claude/worktrees/fix-save-button-disable/backend/routers/index.js +0 -3
  45. package/.claude/worktrees/fix-save-button-disable/backend/routers/methods.js +0 -191
  46. package/.claude/worktrees/fix-save-button-disable/backend/routers/utils.js +0 -128
  47. package/.claude/worktrees/fix-save-button-disable/backend/search-filters-methods.js +0 -124
  48. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-primary-methods.js +0 -256
  49. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-visibility-methods.js +0 -160
  50. package/.claude/worktrees/fix-save-button-disable/backend/tasks/consts.js +0 -34
  51. package/.claude/worktrees/fix-save-button-disable/backend/tasks/daily-pull-check-methods.js +0 -78
  52. package/.claude/worktrees/fix-save-button-disable/backend/tasks/email-normalize-methods.js +0 -134
  53. package/.claude/worktrees/fix-save-button-disable/backend/tasks/index.js +0 -10
  54. package/.claude/worktrees/fix-save-button-disable/backend/tasks/migration-methods.js +0 -46
  55. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-configs.js +0 -251
  56. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-helpers-methods.js +0 -433
  57. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-process-methods.js +0 -606
  58. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-migration-methods.js +0 -378
  59. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-space-fix-methods.js +0 -174
  60. package/.claude/worktrees/fix-save-button-disable/backend/utils.js +0 -277
  61. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  62. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-ids.js +0 -159
  63. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-urls.js +0 -201
  64. package/.claude/worktrees/fix-save-button-disable/eslint.config.js +0 -120
  65. package/.claude/worktrees/fix-save-button-disable/index.js +0 -5
  66. package/.claude/worktrees/fix-save-button-disable/package-lock.json +0 -12160
  67. package/.claude/worktrees/fix-save-button-disable/package.json +0 -60
  68. package/.claude/worktrees/fix-save-button-disable/pages/ContactUs.js +0 -129
  69. package/.claude/worktrees/fix-save-button-disable/pages/Home.js +0 -779
  70. package/.claude/worktrees/fix-save-button-disable/pages/LearnMore.js +0 -27
  71. package/.claude/worktrees/fix-save-button-disable/pages/LoadingPage.js +0 -29
  72. package/.claude/worktrees/fix-save-button-disable/pages/Profile.js +0 -354
  73. package/.claude/worktrees/fix-save-button-disable/pages/QAPage.js +0 -39
  74. package/.claude/worktrees/fix-save-button-disable/pages/SaveAlerts.js +0 -13
  75. package/.claude/worktrees/fix-save-button-disable/pages/SelectBannerImages.js +0 -46
  76. package/.claude/worktrees/fix-save-button-disable/pages/deleteConfirm.js +0 -19
  77. package/.claude/worktrees/fix-save-button-disable/pages/index.js +0 -12
  78. package/.claude/worktrees/fix-save-button-disable/pages/personalDetails.js +0 -2486
  79. package/.claude/worktrees/fix-save-button-disable/public/Utils/homePage.js +0 -798
  80. package/.claude/worktrees/fix-save-button-disable/public/Utils/personalDetailsUtils.js +0 -25
  81. package/.claude/worktrees/fix-save-button-disable/public/Utils/sharedUtils.js +0 -235
  82. package/.claude/worktrees/fix-save-button-disable/public/consts.js +0 -113
  83. package/.claude/worktrees/fix-save-button-disable/public/index.js +0 -5
  84. package/.claude/worktrees/fix-save-button-disable/public/messages.js +0 -16
  85. package/.claude/worktrees/fix-save-button-disable/public/sso-auth-methods.js +0 -43
  86. package/.husky/pre-commit +0 -19
  87. package/.prettierignore +0 -7
  88. package/.prettierrc.json +0 -16
  89. package/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  90. package/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  91. package/backend/__tests__/login-email-sync.test.js +0 -49
  92. package/backend/__tests__/map-link.test.js +0 -115
  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/backend/__tests__/url-validation.test.js +0 -69
  96. package/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  97. package/dev-only-scripts/find-duplicate-ids.js +0 -159
  98. package/dev-only-scripts/find-duplicate-urls.js +0 -201
  99. package/eslint.config.js +0 -120
@@ -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 };
@@ -1,161 +0,0 @@
1
- const { taskManager } = require('psdev-task-manager');
2
-
3
- const { CONFIG_KEYS } = require('../consts');
4
- const { fetchPACMembers } = require('../pac-api-methods');
5
- const { TASKS_NAMES } = require('../tasks/consts');
6
- const { getSiteConfigs } = require('../utils');
7
-
8
- const { bulkProcessAndSaveMemberData } = require('./bulk-process-methods');
9
- const { MEMBER_ACTIONS } = require('./consts');
10
- const { isUpdatedMember, isSiteAssociatedMember } = require('./utils');
11
-
12
- const filterLicensesByAssociation = (member, siteAssociation) => {
13
- if (!member?.licenses || !Array.isArray(member.licenses)) {
14
- return member;
15
- }
16
- const filteredLicenses = member.licenses.filter(license => {
17
- if (!license || !license.association) {
18
- return true;
19
- }
20
- return license.association === siteAssociation;
21
- });
22
- return {
23
- ...member,
24
- licenses: filteredLicenses,
25
- };
26
- };
27
-
28
- async function syncMembersDataPerAction(taskData) {
29
- const { action, backupDate, isTestEnvironment, includeNone } = taskData;
30
- try {
31
- const firstPageResponse = await fetchPACMembers({
32
- page: 1,
33
- action,
34
- backupDate,
35
- isTestEnvironment,
36
- });
37
-
38
- if (
39
- !firstPageResponse ||
40
- !firstPageResponse.results ||
41
- firstPageResponse.results.length === 0
42
- ) {
43
- return {
44
- success: true,
45
- totalPagesProcessed: 0,
46
- lastPageProcessed: 0,
47
- completedAt: new Date().toISOString(),
48
- message: 'No data found',
49
- };
50
- }
51
-
52
- // Calculate total pages from API response
53
- const totalResults = firstPageResponse.total_results || 0;
54
- const perPage = firstPageResponse.results.length;
55
- const totalPages = firstPageResponse.total_pages || 0;
56
-
57
- // Cap at 1000 pages as safety measure
58
- const pagesToProcess = Math.min(totalPages, 1000);
59
-
60
- console.log(
61
- `Scheduling ${pagesToProcess} pages for processing (${totalResults} total records, ${perPage} per page)`
62
- );
63
-
64
- // Schedule tasks for all pages at once
65
- const toScheduleTasks = Array.from(
66
- { length: pagesToProcess },
67
- (_, i) => i + 1 // API expects page number to start from 1
68
- ).map(pageNumber => ({
69
- name: TASKS_NAMES.SyncMembers,
70
- data: {
71
- pageNumber,
72
- action,
73
- ...(backupDate ? { backupDate } : {}),
74
- ...(isTestEnvironment ? { isTestEnvironment } : {}),
75
- ...(includeNone ? { includeNone } : {}),
76
- },
77
- type: 'scheduled',
78
- }));
79
-
80
- // Wait for all scheduling to complete
81
- await taskManager().scheduleInBulk(toScheduleTasks);
82
-
83
- return {
84
- success: true,
85
- totalPagesProcessed: pagesToProcess,
86
- lastPageProcessed: pagesToProcess,
87
- totalRecords: totalResults,
88
- recordsPerPage: perPage,
89
- completedAt: new Date().toISOString(),
90
- };
91
- } catch (error) {
92
- throw new Error(`Synchronization failed: ${error.message}`);
93
- }
94
- }
95
-
96
- /**
97
- * Synchronizes a single page of member data
98
- * @param {Object} taskObject - Task object containing page data
99
- * @returns {Promise<Object>} - Page synchronization result
100
- */
101
- async function synchronizeSinglePage(taskObject) {
102
- const { pageNumber, action, backupDate, isTestEnvironment, includeNone } = taskObject.data;
103
- try {
104
- const [siteAssociation, memberDataResponse] = await Promise.all([
105
- getSiteConfigs(CONFIG_KEYS.SITE_ASSOCIATION),
106
- fetchPACMembers({
107
- page: pageNumber,
108
- action,
109
- backupDate,
110
- isTestEnvironment,
111
- }),
112
- ]);
113
- if (
114
- !memberDataResponse ||
115
- !memberDataResponse.results ||
116
- memberDataResponse.results.length === 0
117
- ) {
118
- throw new Error(`No data found for page ${pageNumber}`);
119
- }
120
- const toSyncMembers = memberDataResponse.results.filter(member => {
121
- if (!isSiteAssociatedMember(member, siteAssociation)) {
122
- return false;
123
- }
124
- if (action === MEMBER_ACTIONS.NONE && includeNone) {
125
- return true;
126
- }
127
- return isUpdatedMember(member);
128
- });
129
- const toSyncMembersWithFilteredLicenses = toSyncMembers.map(member =>
130
- filterLicensesByAssociation(member, siteAssociation)
131
- );
132
- if (toSyncMembers.length === 0) {
133
- return {
134
- success: true,
135
- pageNumber,
136
- totalPageSize: memberDataResponse.results.length,
137
- filteredPageSize: toSyncMembers.length,
138
- message: `No to be updated, or members of association: '${siteAssociation}' found`,
139
- };
140
- }
141
- const result = await bulkProcessAndSaveMemberData({
142
- memberDataList: toSyncMembersWithFilteredLicenses,
143
- currentPageNumber: pageNumber,
144
- });
145
-
146
- return {
147
- success: true,
148
- pageNumber,
149
- totalPageSize: memberDataResponse.results.length,
150
- filteredPageSize: toSyncMembers.length,
151
- ...result,
152
- };
153
- } catch (error) {
154
- throw new Error(`Page ${pageNumber} synchronization failed: ${error.message}`);
155
- }
156
- }
157
-
158
- module.exports = {
159
- syncMembersDataPerAction,
160
- synchronizeSinglePage,
161
- };
@@ -1,151 +0,0 @@
1
- const { LOGIN_EMAIL_SYNC_STATUS } = require('../consts');
2
- const { updateWixMemberLoginEmail } = require('../members-area-methods');
3
- const { extractUrlCounter } = require('../utils');
4
-
5
- const { MEMBER_ACTIONS } = require('./consts');
6
-
7
- const isUpdatedMember = member => member.action !== MEMBER_ACTIONS.NONE;
8
- const isSiteAssociatedMember = (member, siteAssociation) =>
9
- member.memberships.some(membership => membership.association === siteAssociation);
10
-
11
- /**
12
- * Attempts to change Wix login emails for the given members and returns one structured
13
- * outcome per member (see updateWixMemberLoginEmail). Never throws for an individual member.
14
- * @param {Array} toChangeWixMembersEmails
15
- * @returns {Promise<Array>} outcomes
16
- */
17
- const changeWixMembersEmails = async toChangeWixMembersEmails => {
18
- console.log(
19
- `[loginEmailSync] changing login emails for ${toChangeWixMembersEmails.length} members with ids: [${toChangeWixMembersEmails.map(member => member.memberId).join(', ')}]`
20
- );
21
- const outcomes = await Promise.all(
22
- toChangeWixMembersEmails.map(member => updateWixMemberLoginEmail(member))
23
- );
24
- const summary = outcomes.reduce((acc, outcome) => {
25
- acc[outcome.status] = (acc[outcome.status] || 0) + 1;
26
- return acc;
27
- }, {});
28
- console.log(`[loginEmailSync] results summary: ${JSON.stringify(summary)}`);
29
- return outcomes;
30
- };
31
-
32
- /**
33
- * Summarizes login-email sync outcomes for manual handling via the task result.
34
- * Pure function so it can be unit-tested without Wix.
35
- * @param {Array} outcomes - from updateWixMemberLoginEmail / changeWixMembersEmails
36
- * @returns {{ failedMemberIds: Set, failures: Array }} set of failed memberIds (whose CMS login
37
- * email must be left unchanged) and the failure records to surface in the task result
38
- */
39
- const summarizeLoginEmailOutcomes = (outcomes = []) => {
40
- const failedMemberIds = new Set();
41
- const failures = [];
42
- outcomes.forEach(outcome => {
43
- if (outcome.status === LOGIN_EMAIL_SYNC_STATUS.FAILED) {
44
- failedMemberIds.add(outcome.memberId);
45
- failures.push({
46
- memberId: outcome.memberId,
47
- wixMemberId: outcome.wixMemberId,
48
- desiredEmail: outcome.desiredEmail,
49
- error: outcome.error || 'unknown error',
50
- });
51
- }
52
- });
53
- return { failedMemberIds, failures };
54
- };
55
-
56
- const extractBaseUrl = url => {
57
- if (!url) return url;
58
- const lastCounter = extractUrlCounter(url);
59
- if (lastCounter > 0) {
60
- // Remove the numeric counter to get the base URL
61
- return url.split('-').slice(0, -1).join('-');
62
- }
63
- // No counter found, return the URL as-is
64
- return url;
65
- };
66
- const incrementUrlCounter = (existingUrl, baseUrl) => {
67
- if (!existingUrl || !baseUrl) {
68
- return baseUrl;
69
- }
70
- // Normalize for comparison (case-insensitive)
71
- const normalizedExisting = existingUrl.toLowerCase();
72
- const normalizedBase = baseUrl.toLowerCase();
73
-
74
- if (
75
- normalizedExisting === normalizedBase ||
76
- normalizedExisting.startsWith(`${normalizedBase}-`)
77
- ) {
78
- console.log(
79
- `Found member with same url ${existingUrl} for baseUrl ${baseUrl}, increasing counter by 1`
80
- );
81
- const lastCounter = Math.max(0, extractUrlCounter(existingUrl));
82
- return `${baseUrl}-${lastCounter + 1}`;
83
- }
84
-
85
- // No conflict, return baseUrl with counter 1 to be safe
86
- return `${baseUrl}-1`;
87
- };
88
- /**
89
- * Validates core member data requirements
90
- * @param {Object} inputMemberData - Raw member data from API to validate
91
- * @returns {boolean} - True if all required fields are valid, false otherwise
92
- */
93
- const validateCoreMemberData = inputMemberData => {
94
- // Check memberid
95
- if (!inputMemberData?.memberid) {
96
- console.warn('validateCoreMemberData: Missing required field - memberid is mandatory');
97
- return false;
98
- }
99
-
100
- // Check email
101
- if (
102
- !inputMemberData?.email ||
103
- typeof inputMemberData.email !== 'string' ||
104
- !inputMemberData.email.trim()
105
- ) {
106
- console.warn(
107
- 'validateCoreMemberData: Missing required field - email (valid string) is mandatory'
108
- );
109
- return false;
110
- }
111
-
112
- // Check memberships
113
- if (
114
- !inputMemberData?.memberships ||
115
- !Array.isArray(inputMemberData.memberships) ||
116
- inputMemberData.memberships.length === 0
117
- ) {
118
- console.warn(
119
- 'validateCoreMemberData: Missing required field - memberships (non-empty array) is mandatory'
120
- );
121
- return false;
122
- }
123
-
124
- return true;
125
- };
126
-
127
- const containsNonEnglish = str => /[^a-zA-Z0-9-]/.test(str); // if it contains any non-english characters or invalid URL chars, test1 is allowed, hyphens are allowed
128
-
129
- /**
130
- * Creates a full name from first and last name components
131
- * @param {string} firstName - First name
132
- * @param {string} lastName - Last name
133
- * @returns {string} - Combined full name
134
- */
135
- const createFullName = (firstName, lastName) => {
136
- const trimmedFirst = firstName?.trim() || '';
137
- const trimmedLast = lastName?.trim() || '';
138
- return `${trimmedFirst} ${trimmedLast}`.trim();
139
- };
140
-
141
- module.exports = {
142
- isUpdatedMember,
143
- isSiteAssociatedMember,
144
- changeWixMembersEmails,
145
- summarizeLoginEmailOutcomes,
146
- validateCoreMemberData,
147
- containsNonEnglish,
148
- createFullName,
149
- incrementUrlCounter,
150
- extractBaseUrl,
151
- };
@@ -1,29 +0,0 @@
1
- const { taskManager, TASK_TYPE } = require('psdev-task-manager');
2
-
3
- const { COMPILED_FILTERS_FIELDS } = require('./consts');
4
- const { TASKS_NAMES } = require('./tasks/consts');
5
-
6
- const scheduleCompileFiltersTask = field =>
7
- taskManager().schedule({
8
- name: TASKS_NAMES.CompileFiltersOptions,
9
- data: { field },
10
- type: TASK_TYPE.SCHEDULED,
11
- });
12
-
13
- function scheduleCompileInterestsTask() {
14
- return scheduleCompileFiltersTask(COMPILED_FILTERS_FIELDS.COMPILED_AREAS_OF_PRACTICES);
15
- }
16
-
17
- function scheduleCompileStatesTask() {
18
- return scheduleCompileFiltersTask(COMPILED_FILTERS_FIELDS.COMPILED_STATE_LIST);
19
- }
20
-
21
- function scheduleCompileCitiesTask() {
22
- return scheduleCompileFiltersTask(COMPILED_FILTERS_FIELDS.COMPILED_STATE_CITY_MAP);
23
- }
24
-
25
- module.exports = {
26
- scheduleCompileInterestsTask,
27
- scheduleCompileStatesTask,
28
- scheduleCompileCitiesTask,
29
- };
@@ -1,58 +0,0 @@
1
- const { COLLECTIONS } = require('../public/consts');
2
-
3
- const { ensureUniqueUrlsInBatch } = require('./daily-pull/bulk-process-methods');
4
- const { ensureUniqueUrl } = require('./daily-pull/process-member-methods');
5
- const { wixData } = require('./elevated-modules');
6
- const { bulkSaveMembers } = require('./members-data-methods');
7
- const { queryAllItems } = require('./utils');
8
-
9
- async function deduplicateURls(collectionName, duplicateUrlsList) {
10
- const query = await wixData.query(collectionName).hasSome('url', duplicateUrlsList).limit(1000);
11
- const membersWithSameUrl = await queryAllItems(query);
12
-
13
- console.log({ membersWithSameUrl });
14
- const membersWithUniqueUrls = await ensureUniqueUrlsInBatch(membersWithSameUrl);
15
- console.log({ membersWithUniqueUrls });
16
- const deduplicatedUrls = membersWithUniqueUrls.map(m => m.url);
17
- console.log({ deduplicatedUrls });
18
- return await bulkSaveMembers(membersWithUniqueUrls, collectionName);
19
- }
20
-
21
- async function copyContactIdToWixMemberId() {
22
- const query = wixData.query(COLLECTIONS.MEMBERS_DATA).isNotEmpty('contactId');
23
- const members = await queryAllItems(query);
24
- const updatedMembers = members.map(member => ({
25
- ...member,
26
- wixMemberId: member.contactId,
27
- }));
28
- return await bulkSaveMembers(updatedMembers, COLLECTIONS.MEMBERS_DATA);
29
- }
30
-
31
- async function createMissingUrls() {
32
- const query = wixData.query(COLLECTIONS.MEMBERS_DATA).isEmpty('url');
33
- const members = await queryAllItems(query);
34
- console.log(
35
- 'membersWithoutUrls info',
36
- JSON.stringify({
37
- count: members.length,
38
- membersIds: members.map(m => m.memberId),
39
- })
40
- );
41
-
42
- const membersWithGeneratedUrlsPromises = members.map(async member => ({
43
- ...member,
44
- url: await ensureUniqueUrl({
45
- url: member.url,
46
- memberId: member.memberId,
47
- fullName: member.fullName,
48
- }),
49
- }));
50
- const membersWithGeneratedUrls = await Promise.all(membersWithGeneratedUrlsPromises);
51
- //recheck urls in same batch to avoid duplicates
52
- const uniqueUrlsUpdatedMembers = await ensureUniqueUrlsInBatch(membersWithGeneratedUrls);
53
- const urls = uniqueUrlsUpdatedMembers.map(m => m.url).filter(Boolean);
54
- console.log('unique urls', [...new Set(urls)]);
55
- return await bulkSaveMembers(uniqueUrlsUpdatedMembers, COLLECTIONS.MEMBERS_DATA);
56
- }
57
-
58
- module.exports = { deduplicateURls, copyContactIdToWixMemberId, createMissingUrls };
@@ -1,18 +0,0 @@
1
- const { items } = require('@wix/data');
2
- const { auth } = require('@wix/essentials');
3
-
4
- // @wix/data does not support suppressAuth currently, so we need to elevate it
5
- const wixData = {
6
- insert: auth.elevate(items.insert),
7
- update: auth.elevate(items.update),
8
- bulkInsert: auth.elevate(items.bulkInsert),
9
- query: auth.elevate(items.query),
10
- save: auth.elevate(items.save),
11
- remove: auth.elevate(items.remove),
12
- get: auth.elevate(items.get),
13
- truncate: auth.elevate(items.truncate),
14
- bulkSave: auth.elevate(items.bulkSave),
15
- search: auth.elevate(items.search),
16
- //TODO: add other methods here as needed
17
- };
18
- module.exports = { wixData };