abmp-npm 2.0.63 → 2.0.65

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
@@ -2,6 +2,7 @@ const { taskManager } = require('psdev-task-manager');
2
2
 
3
3
  const { MEMBER_ACTIONS } = require('./daily-pull/consts');
4
4
  const { TASKS_NAMES } = require('./tasks/consts');
5
+ const { dailyPullExecutionCheck } = require('./tasks/daily-pull-check-methods');
5
6
  const { TASKS } = require('./tasks/tasks-configs');
6
7
 
7
8
  async function runScheduledTasks() {
@@ -96,6 +97,16 @@ async function scheduleFixUrlsWithSpacesTask() {
96
97
  }
97
98
  }
98
99
 
100
+ async function runDailyPullExecutionCheck() {
101
+ try {
102
+ console.log('runDailyPullExecutionCheck started!');
103
+ return await dailyPullExecutionCheck({});
104
+ } catch (error) {
105
+ console.error(`Failed to runDailyPullExecutionCheck: ${error.message}`);
106
+ throw new Error(`Failed to runDailyPullExecutionCheck: ${error.message}`);
107
+ }
108
+ }
109
+
99
110
  async function updateSiteMapS3() {
100
111
  try {
101
112
  return await taskManager().schedule({
@@ -115,4 +126,5 @@ module.exports = {
115
126
  scheduleCreateContactsFromMembersTask,
116
127
  scheduleFixPrimaryAddressForMembersTask,
117
128
  scheduleFixUrlsWithSpacesTask,
129
+ runDailyPullExecutionCheck,
118
130
  };
@@ -22,6 +22,7 @@ const TASKS_NAMES = {
22
22
  fixPrimaryAddressChunk: 'fixPrimaryAddressChunk',
23
23
  scheduleFixUrlsWithSpaces: 'scheduleFixUrlsWithSpaces',
24
24
  fixUrlsWithSpacesChunk: 'fixUrlsWithSpacesChunk',
25
+ dailyPullExecutionCheck: 'dailyPullExecutionCheck',
25
26
  };
26
27
 
27
28
  module.exports = {
@@ -0,0 +1,58 @@
1
+ const { taskManager } = require('psdev-task-manager');
2
+ const { COLLECTIONS } = require('psdev-task-manager/public/consts');
3
+
4
+ const { wixData } = require('../elevated-modules');
5
+ const { queryAllItems } = require('../utils');
6
+
7
+ const { TASKS_NAMES } = require('./consts');
8
+
9
+ const DEFAULT_HOURS_BACK = 4;
10
+
11
+ /**
12
+ * Detects whether the daily pull was scheduled (cron / root task).
13
+ * If no `ScheduleDailyMembersDataSync` task exists in the lookback window, schedules it.
14
+ */
15
+ async function dailyPullExecutionCheck(taskData) {
16
+ const hoursBack =
17
+ taskData?.hoursBack && Number.isFinite(taskData.hoursBack)
18
+ ? taskData.hoursBack
19
+ : DEFAULT_HOURS_BACK;
20
+ const sinceDate = new Date(Date.now() - hoursBack * 60 * 60 * 1000);
21
+
22
+ console.log('dailyPullExecutionCheck started', { hoursBack, sinceDate });
23
+
24
+ const rootTasksQuery = wixData
25
+ .query(COLLECTIONS.TASKS)
26
+ .eq('name', TASKS_NAMES.ScheduleDailyMembersDataSync)
27
+ .ge('_createdDate', sinceDate);
28
+
29
+ const rootTasks = await queryAllItems(rootTasksQuery);
30
+ const rootTaskScheduled = rootTasks.length > 0;
31
+
32
+ const result = {
33
+ success: rootTaskScheduled,
34
+ sinceDate: sinceDate.toISOString(),
35
+ rootTaskName: TASKS_NAMES.ScheduleDailyMembersDataSync,
36
+ rootTasksFound: rootTasks.length,
37
+ };
38
+
39
+ if (!rootTaskScheduled) {
40
+ console.log('ScheduleDailyMembersDataSync missing in window; scheduling root daily pull', {
41
+ hoursBack,
42
+ });
43
+ await taskManager().schedule({
44
+ name: TASKS_NAMES.ScheduleDailyMembersDataSync,
45
+ data: {},
46
+ type: 'scheduled',
47
+ });
48
+ result.fallbackScheduled = true;
49
+ }
50
+
51
+ console.log('dailyPullExecutionCheck result', JSON.stringify(result, null, 2));
52
+
53
+ return result;
54
+ }
55
+
56
+ module.exports = {
57
+ dailyPullExecutionCheck,
58
+ };
@@ -6,4 +6,5 @@ module.exports = {
6
6
  ...require('./url-migration-methods'),
7
7
  ...require('./address-primary-methods'),
8
8
  ...require('./url-space-fix-methods'),
9
+ ...require('./daily-pull-check-methods'),
9
10
  };
@@ -9,6 +9,7 @@ const {
9
9
  fixPrimaryAddressChunk,
10
10
  } = require('./address-primary-methods');
11
11
  const { TASKS_NAMES } = require('./consts');
12
+ const { dailyPullExecutionCheck } = require('./daily-pull-check-methods');
12
13
  const {
13
14
  scheduleTaskForEmptyAboutYouMembers,
14
15
  convertAboutYouHtmlToRichContent,
@@ -202,6 +203,13 @@ const TASKS = {
202
203
  shouldSkipCheck: () => false,
203
204
  estimatedDurationSec: 80,
204
205
  },
206
+ [TASKS_NAMES.dailyPullExecutionCheck]: {
207
+ name: TASKS_NAMES.dailyPullExecutionCheck,
208
+ getIdentifier: task => task.data,
209
+ process: dailyPullExecutionCheck,
210
+ shouldSkipCheck: () => false,
211
+ estimatedDurationSec: 30,
212
+ },
205
213
  };
206
214
 
207
215
  module.exports = { TASKS };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.63",
3
+ "version": "2.0.65",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
@@ -18,6 +18,33 @@ const {
18
18
  const MAX_PHONES_COUNT = 10;
19
19
  const MAX_ADDRESSES_COUNT = 10;
20
20
 
21
+ /** US 10-digit NANP: strip non-digits; drop leading 1 from 11-digit input. */
22
+ function digitsFromPhoneInput(value) {
23
+ if (!value || typeof value !== 'string') return '';
24
+ let d = value.replace(/\D/g, '');
25
+ if (d.length === 11 && d[0] === '1') d = d.slice(1);
26
+ return d.slice(0, 10);
27
+ }
28
+
29
+ /** Format as (###) ###-#### while typing. */
30
+ function formatUsPhoneInput(digits) {
31
+ if (!digits) return '';
32
+ const d = digits.slice(0, 10);
33
+ if (d.length <= 3) return `(${d}`;
34
+ if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
35
+ return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
36
+ }
37
+
38
+ function formatPhoneNumberForInput(value) {
39
+ return formatUsPhoneInput(digitsFromPhoneInput(value || ''));
40
+ }
41
+
42
+ function phoneDigitsEqual(a, b) {
43
+ const da = digitsFromPhoneInput(String(a || ''));
44
+ const db = digitsFromPhoneInput(String(b || ''));
45
+ return da.length > 0 && db.length > 0 && da === db;
46
+ }
47
+
21
48
  const ADDRESS_STATES = {
22
49
  VIEW: 'addressViewState',
23
50
  EDIT: 'addressEditState',
@@ -2024,12 +2051,16 @@ async function personalDetailsOnReady({
2024
2051
  _$w('#phoneInput').onInput(event => {
2025
2052
  const data = _$w('#phoneNumbersList').data;
2026
2053
  const clickedItemData = data.find(item => item._id === event.context.itemId);
2027
- const phoneValue = event.target.value;
2054
+ const digits = digitsFromPhoneInput(event.target.value);
2055
+ const formatted = formatUsPhoneInput(digits);
2056
+ if (event.target.value !== formatted) {
2057
+ event.target.value = formatted;
2058
+ }
2028
2059
 
2029
- updatePhoneNumber(clickedItemData._id, phoneValue);
2060
+ updatePhoneNumber(clickedItemData._id, formatted);
2030
2061
 
2031
- if (clickedItemData.isNewPhone && phoneValue.trim()) {
2032
- addNewPhoneToData(clickedItemData._id, phoneValue.trim());
2062
+ if (clickedItemData.isNewPhone && digits.length > 0) {
2063
+ addNewPhoneToData(clickedItemData._id, formatted);
2033
2064
  clickedItemData.isNewPhone = false;
2034
2065
  }
2035
2066
 
@@ -2098,7 +2129,7 @@ async function personalDetailsOnReady({
2098
2129
  }
2099
2130
 
2100
2131
  function handlePhoneItem($item, itemData) {
2101
- $item('#phoneInput').value = itemData.phoneNumber || '';
2132
+ $item('#phoneInput').value = formatPhoneNumberForInput(itemData.phoneNumber || '');
2102
2133
  $item('#showPhoneCheckbox').checked = itemData.showPhone || false;
2103
2134
  $item('#phoneNumberLabel').text = `Phone ${itemData.phoneIndex}`;
2104
2135
  }
@@ -2112,7 +2143,7 @@ async function personalDetailsOnReady({
2112
2143
  let matched = false;
2113
2144
  return phoneData.map(p => {
2114
2145
  const t = (p.phoneNumber || '').trim();
2115
- const isMatch = Boolean(t && t === cms);
2146
+ const isMatch = Boolean(t && phoneDigitsEqual(t, cms));
2116
2147
  if (isMatch && !matched) {
2117
2148
  matched = true;
2118
2149
  return { ...p, showPhone: true };
@@ -2140,7 +2171,7 @@ async function personalDetailsOnReady({
2140
2171
  phoneData = phones.map((phone, index) => ({
2141
2172
  _id: `phone_${index}`,
2142
2173
  phoneNumber: phone,
2143
- showPhone: phone === itemMemberObj.toShowPhone,
2174
+ showPhone: phoneDigitsEqual(phone, itemMemberObj.toShowPhone),
2144
2175
  isNewPhone: false,
2145
2176
  phoneIndex: index + 1,
2146
2177
  }));
@@ -2195,7 +2226,7 @@ async function personalDetailsOnReady({
2195
2226
  } else if (
2196
2227
  itemMemberObj.toShowPhone &&
2197
2228
  prevTrimmed &&
2198
- itemMemberObj.toShowPhone === prevTrimmed
2229
+ phoneDigitsEqual(itemMemberObj.toShowPhone, prevTrimmed)
2199
2230
  ) {
2200
2231
  itemMemberObj.toShowPhone = newTrimmed || null;
2201
2232
  }
@@ -2230,14 +2261,17 @@ async function personalDetailsOnReady({
2230
2261
  if (phoneToRemove) {
2231
2262
  if (itemMemberObj.phones) {
2232
2263
  itemMemberObj.phones = itemMemberObj.phones.filter(
2233
- phone => phone !== phoneToRemove.phoneNumber
2264
+ phone => !phoneDigitsEqual(phone, phoneToRemove.phoneNumber)
2234
2265
  );
2235
2266
  }
2236
2267
 
2237
2268
  // Clear toShowPhone if it was the removed phone or if it's no longer in the list
2238
2269
  // (handles format mismatch e.g. "(406)655-4940" vs "(406) 655-4940")
2239
2270
  const remainingPhones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
2240
- if (itemMemberObj.toShowPhone && !remainingPhones.includes(itemMemberObj.toShowPhone)) {
2271
+ if (
2272
+ itemMemberObj.toShowPhone &&
2273
+ !remainingPhones.some(p => phoneDigitsEqual(p, itemMemberObj.toShowPhone))
2274
+ ) {
2241
2275
  itemMemberObj.toShowPhone = null;
2242
2276
  }
2243
2277
 
@@ -2262,7 +2296,10 @@ async function personalDetailsOnReady({
2262
2296
  return;
2263
2297
  }
2264
2298
 
2265
- if (itemMemberObj.toShowPhone === trimmed) {
2299
+ if (
2300
+ itemMemberObj.toShowPhone === trimmed ||
2301
+ phoneDigitsEqual(itemMemberObj.toShowPhone, trimmed)
2302
+ ) {
2266
2303
  itemMemberObj.toShowPhone = null;
2267
2304
  }
2268
2305
  }
@@ -2273,7 +2310,7 @@ async function personalDetailsOnReady({
2273
2310
  // Never expose toShowPhone when phones is empty, so save payload clears it in CMS
2274
2311
  if (phones.length === 0) return null;
2275
2312
  // If toShowPhone is not in the list (e.g. format mismatch), treat as none selected
2276
- if (toShow && !phones.includes(toShow)) return null;
2313
+ if (toShow && !phones.some(p => phoneDigitsEqual(p, toShow))) return null;
2277
2314
  return toShow;
2278
2315
  }
2279
2316
 
@@ -2302,9 +2339,6 @@ async function personalDetailsOnReady({
2302
2339
  }
2303
2340
 
2304
2341
  async function saveContactBooking() {
2305
- // if showWixUrl value changes then update optWebsiteCheckbox value
2306
- _$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
2307
-
2308
2342
  const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
2309
2343
  const otherWebsiteValue = (_$w('#UrlInput').value || '').trim();
2310
2344
  const isOtherWebsiteInvalid =
@@ -2345,8 +2379,13 @@ async function personalDetailsOnReady({
2345
2379
  console.groupEnd();
2346
2380
 
2347
2381
  const result = await saveData(formData);
2348
- if (beforeData.showWebsite !== contactChanges.showWebsite) {
2349
- handleOptWebsiteCheckboxEnable(showExistingUrl);
2382
+ if (result.success) {
2383
+ if (beforeData.showWebsite !== contactChanges.showWebsite) {
2384
+ handleOptWebsiteCheckboxEnable(showExistingUrl);
2385
+ }
2386
+ // Sync Personal Details opt-in from saved member.
2387
+ _$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
2388
+ toggleFreeWebsiteText(itemMemberObj.showWixUrl);
2350
2389
  }
2351
2390
  formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section] = false;
2352
2391
  handleSaveDataFeedback(_$w('#contactMessage'), result.message);