abmp-npm 2.0.72 → 2.0.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/backend/jobs.js CHANGED
@@ -97,6 +97,24 @@ async function scheduleNormalizeMemberEmailsTask() {
97
97
  }
98
98
  }
99
99
 
100
+ /**
101
+ * Schedules setting ALL addresses for ALL members that have any address to STATE_CITY_ZIP
102
+ * (show city/state/zip, hide the street). Manually triggered one-off maintenance task.
103
+ */
104
+ async function scheduleSetAddressesToCityStateTask() {
105
+ try {
106
+ console.log('scheduleSetAddressesToCityState started!');
107
+ return await taskManager().schedule({
108
+ name: TASKS_NAMES.scheduleSetAddressesToCityState,
109
+ data: {},
110
+ type: 'scheduled',
111
+ });
112
+ } catch (error) {
113
+ console.error(`Failed to scheduleSetAddressesToCityState: ${error.message}`);
114
+ throw new Error(`Failed to scheduleSetAddressesToCityState: ${error.message}`);
115
+ }
116
+ }
117
+
100
118
  /**
101
119
  * Runs the daily pull execution check (watchdog).
102
120
  * @param {Object} [options]
@@ -135,5 +153,6 @@ module.exports = {
135
153
  scheduleFixPrimaryAddressForMembersTask,
136
154
  scheduleFixUrlsWithSpacesTask,
137
155
  scheduleNormalizeMemberEmailsTask,
156
+ scheduleSetAddressesToCityStateTask,
138
157
  runDailyPullExecutionCheck,
139
158
  };
@@ -0,0 +1,160 @@
1
+ const { taskManager } = require('psdev-task-manager');
2
+
3
+ const { COLLECTIONS, ADDRESS_STATUS_TYPES } = require('../../public/consts');
4
+ const { wixData } = require('../elevated-modules');
5
+ const { bulkSaveMembers, getMembersByIds } = require('../members-data-methods');
6
+ const { chunkArray, queryAllItems } = require('../utils');
7
+
8
+ const { TASKS_NAMES } = require('./consts');
9
+
10
+ const CHUNK_SIZE = 1000;
11
+
12
+ // Target visibility for every address: show city / state / zip, hide the street address.
13
+ const TARGET_STATUS = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
14
+
15
+ const getMemberAddresses = member => (Array.isArray(member.addresses) ? member.addresses : []);
16
+
17
+ /**
18
+ * Whether a member has at least one address not already at the target status.
19
+ * @param {Object} member
20
+ * @returns {boolean}
21
+ */
22
+ const needsCityStateUpdate = member =>
23
+ getMemberAddresses(member).some(address => address && address.addressStatus !== TARGET_STATUS);
24
+
25
+ /**
26
+ * Sets every address on a member to STATE_CITY_ZIP (other address fields are preserved).
27
+ * @param {Object} member
28
+ * @returns {Array} the member's addresses with addressStatus set to STATE_CITY_ZIP
29
+ */
30
+ const setAddressesToCityState = member =>
31
+ getMemberAddresses(member).map(address => ({
32
+ ...address,
33
+ addressStatus: TARGET_STATUS,
34
+ }));
35
+
36
+ /**
37
+ * Schedules tasks to set ALL addresses for ALL members to STATE_CITY_ZIP (show city/state/zip,
38
+ * hide the street). Manually triggered (not cron-wired).
39
+ */
40
+ async function scheduleSetAddressesToCityState() {
41
+ console.log('=== Scheduling Set Addresses To City/State Tasks ===');
42
+
43
+ try {
44
+ const membersQuery = await wixData
45
+ .query(COLLECTIONS.MEMBERS_DATA)
46
+ .isNotEmpty('addresses')
47
+ .limit(1000);
48
+ const members = await queryAllItems(membersQuery);
49
+ console.log(`Fetched ${members.length} members with addresses`);
50
+
51
+ // Only members that still have an address not at the target status need updating.
52
+ const memberIds = [
53
+ ...new Set(
54
+ members
55
+ .filter(needsCityStateUpdate)
56
+ .map(member => Number(member.memberId))
57
+ .filter(memberId => Number.isFinite(memberId) && memberId > 0)
58
+ ),
59
+ ];
60
+ console.log(`Members needing a city/state update: ${memberIds.length}`);
61
+
62
+ if (memberIds.length === 0) {
63
+ console.log('No members need their addresses set to city/state');
64
+ return {
65
+ success: true,
66
+ message: 'No members need their addresses set to city/state',
67
+ totalMembers: 0,
68
+ tasksScheduled: 0,
69
+ };
70
+ }
71
+
72
+ const chunks = chunkArray(memberIds, CHUNK_SIZE);
73
+ for (let i = 0; i < chunks.length; i++) {
74
+ const chunk = chunks[i];
75
+ await taskManager().schedule({
76
+ name: TASKS_NAMES.setAddressesToCityStateChunk,
77
+ data: {
78
+ memberIds: chunk,
79
+ chunkIndex: i,
80
+ totalChunks: chunks.length,
81
+ },
82
+ type: 'scheduled',
83
+ });
84
+ console.log(`Scheduled task ${i + 1}/${chunks.length} (${chunk.length} members)`);
85
+ }
86
+
87
+ const result = {
88
+ success: true,
89
+ message: `Scheduled ${chunks.length} tasks for ${memberIds.length} members`,
90
+ totalMembers: memberIds.length,
91
+ tasksScheduled: chunks.length,
92
+ };
93
+ console.log('=== Scheduling Complete ===');
94
+ console.log(JSON.stringify(result, null, 2));
95
+ return result;
96
+ } catch (error) {
97
+ console.error('Error scheduling set-addresses-to-city-state:', error);
98
+ throw error;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Processes a chunk of members and sets every address to STATE_CITY_ZIP.
104
+ * @param {Object} data
105
+ * @param {Array<number|string>} data.memberIds
106
+ * @param {number} data.chunkIndex
107
+ * @param {number} data.totalChunks
108
+ */
109
+ async function setAddressesToCityStateChunk(data) {
110
+ const { memberIds, chunkIndex, totalChunks } = data;
111
+ console.log(
112
+ `Processing set-city-state chunk ${chunkIndex + 1}/${totalChunks} (${memberIds.length} members)`
113
+ );
114
+
115
+ const result = { successful: 0, failed: 0, skipped: 0, errors: [], failedIds: [] };
116
+
117
+ try {
118
+ const members = await getMembersByIds(memberIds);
119
+ console.log(`Loaded ${members.length} members for this chunk`);
120
+
121
+ const membersToUpdate = [];
122
+ members.forEach(member => {
123
+ // Skip members that have no address or are already all at the target status.
124
+ if (!needsCityStateUpdate(member)) {
125
+ result.skipped++;
126
+ return;
127
+ }
128
+ membersToUpdate.push({
129
+ ...member,
130
+ addresses: setAddressesToCityState(member),
131
+ });
132
+ });
133
+
134
+ if (membersToUpdate.length === 0) {
135
+ console.log('No members need updating in this batch');
136
+ return result;
137
+ }
138
+
139
+ try {
140
+ await bulkSaveMembers(membersToUpdate);
141
+ result.successful += membersToUpdate.length;
142
+ console.log(`✅ Set addresses to city/state for ${membersToUpdate.length} members`);
143
+ } catch (error) {
144
+ console.error('❌ Error bulk saving members:', error);
145
+ result.failed += membersToUpdate.length;
146
+ result.failedIds.push(...membersToUpdate.map(member => member.memberId));
147
+ result.errors.push({ error: error.message, memberCount: membersToUpdate.length });
148
+ }
149
+
150
+ return result;
151
+ } catch (error) {
152
+ console.error(`Error processing set-city-state chunk ${chunkIndex}:`, error);
153
+ throw error;
154
+ }
155
+ }
156
+
157
+ module.exports = {
158
+ scheduleSetAddressesToCityState,
159
+ setAddressesToCityStateChunk,
160
+ };
@@ -22,6 +22,8 @@ const TASKS_NAMES = {
22
22
  fixPrimaryAddressChunk: 'fixPrimaryAddressChunk',
23
23
  scheduleFixUrlsWithSpaces: 'scheduleFixUrlsWithSpaces',
24
24
  fixUrlsWithSpacesChunk: 'fixUrlsWithSpacesChunk',
25
+ scheduleSetAddressesToCityState: 'scheduleSetAddressesToCityState',
26
+ setAddressesToCityStateChunk: 'setAddressesToCityStateChunk',
25
27
  scheduleNormalizeMemberEmails: 'scheduleNormalizeMemberEmails',
26
28
  normalizeMemberEmailsChunk: 'normalizeMemberEmailsChunk',
27
29
  dailyPullExecutionCheck: 'dailyPullExecutionCheck',
@@ -8,6 +8,10 @@ const {
8
8
  scheduleFixPrimaryAddressForMembers,
9
9
  fixPrimaryAddressChunk,
10
10
  } = require('./address-primary-methods');
11
+ const {
12
+ scheduleSetAddressesToCityState,
13
+ setAddressesToCityStateChunk,
14
+ } = require('./address-visibility-methods');
11
15
  const { TASKS_NAMES } = require('./consts');
12
16
  const { dailyPullExecutionCheck } = require('./daily-pull-check-methods');
13
17
  const {
@@ -207,6 +211,20 @@ const TASKS = {
207
211
  shouldSkipCheck: () => false,
208
212
  estimatedDurationSec: 80,
209
213
  },
214
+ [TASKS_NAMES.scheduleSetAddressesToCityState]: {
215
+ name: TASKS_NAMES.scheduleSetAddressesToCityState,
216
+ getIdentifier: () => 'SHOULD_NEVER_SKIP',
217
+ process: scheduleSetAddressesToCityState,
218
+ shouldSkipCheck: () => false,
219
+ estimatedDurationSec: 80,
220
+ },
221
+ [TASKS_NAMES.setAddressesToCityStateChunk]: {
222
+ name: TASKS_NAMES.setAddressesToCityStateChunk,
223
+ getIdentifier: task => task.data,
224
+ process: setAddressesToCityStateChunk,
225
+ shouldSkipCheck: () => false,
226
+ estimatedDurationSec: 80,
227
+ },
210
228
  [TASKS_NAMES.scheduleNormalizeMemberEmails]: {
211
229
  name: TASKS_NAMES.scheduleNormalizeMemberEmails,
212
230
  getIdentifier: () => 'SHOULD_NEVER_SKIP',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.72",
3
+ "version": "2.0.73",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
@@ -975,31 +975,18 @@ async function personalDetailsOnReady({
975
975
  });
976
976
  }
977
977
 
978
- async function handleItemDelete(event, getTextSelector, arrayRef, matchField, renderFn) {
979
- const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
980
-
981
- if (result && result.toDelete) {
982
- const $clickedItem = _$w.at(event.context);
983
- const textToRemove = $clickedItem(getTextSelector).text;
984
-
985
- arrayRef.splice(
986
- 0,
987
- arrayRef.length,
988
- ...arrayRef.filter(item =>
989
- typeof item === 'string' ? item !== textToRemove : item[matchField] !== textToRemove
990
- )
991
- );
992
-
993
- renderFn();
994
- checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
995
- }
996
- }
997
-
998
978
  async function setInterestData() {
999
979
  const interestsData = await getInterestAll();
1000
980
 
1001
- _$w('#removeServiceButton').onClick(event => {
1002
- handleItemDelete(event, '#serviceNameText', selectedServices, 'label', renderServices);
981
+ _$w('#removeServiceButton').onClick(async event => {
982
+ const itemId = event.context.itemId;
983
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
984
+
985
+ if (result && result.toDelete) {
986
+ selectedServices = selectedServices.filter(service => service._id !== itemId);
987
+ renderServices();
988
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
989
+ }
1003
990
  });
1004
991
 
1005
992
  if (Array.isArray(interestsData) && interestsData.length > 0) {
@@ -1077,7 +1064,7 @@ async function personalDetailsOnReady({
1077
1064
  }
1078
1065
 
1079
1066
  function renderServices() {
1080
- setupRepeater('#servicesRepeater', selectedServices);
1067
+ setupRepeater('#servicesRepeater', [...selectedServices]);
1081
1068
  }
1082
1069
 
1083
1070
  function setupRepeater(repeaterId, data) {
@@ -1339,14 +1326,21 @@ async function personalDetailsOnReady({
1339
1326
  const addTestimonialButton = _$w('#addTestimonialButton');
1340
1327
 
1341
1328
  addTestimonialButton.onClick(handleAddTestimonial);
1342
- _$w('#deleteTestimonialButton').onClick(event => {
1343
- handleItemDelete(
1344
- event,
1345
- '#testimonialText',
1346
- itemMemberObj.testimonial,
1347
- null,
1348
- renderTestimonials
1349
- );
1329
+ _$w('#deleteTestimonialButton').onClick(async event => {
1330
+ const data = _$w('#testimonialRepeater').data || [];
1331
+ const clickedIndex = data.findIndex(item => item._id === event.context.itemId);
1332
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
1333
+
1334
+ if (
1335
+ result &&
1336
+ result.toDelete &&
1337
+ clickedIndex > 0 &&
1338
+ Array.isArray(itemMemberObj.testimonial)
1339
+ ) {
1340
+ itemMemberObj.testimonial.splice(clickedIndex - 1, 1);
1341
+ renderTestimonials();
1342
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
1343
+ }
1350
1344
  });
1351
1345
 
1352
1346
  renderTestimonials();