abmp-npm 10.3.7 → 10.3.9
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/__tests__/login-email-sync.test.js +49 -0
- package/backend/__tests__/transient-retry-and-bulk-lookup.test.js +129 -0
- package/backend/consts.js +10 -0
- package/backend/contacts-methods.js +3 -0
- package/backend/daily-pull/bulk-process-methods.js +51 -11
- package/backend/daily-pull/process-member-methods.js +15 -3
- package/backend/daily-pull/utils.js +41 -3
- package/backend/http-functions/interests.js +4 -6
- package/backend/jobs.js +15 -0
- package/backend/login/generate-member-session-token.js +27 -0
- package/backend/login/index.js +4 -3
- package/backend/login/qa-login-methods.js +4 -3
- package/backend/login/sso-methods.js +11 -10
- package/backend/member-contact-orchestration.js +6 -1
- package/backend/members-area-methods.js +33 -34
- package/backend/members-data-methods.js +152 -13
- package/backend/pac-api-methods.js +12 -12
- package/backend/tasks/consts.js +2 -0
- package/backend/tasks/email-normalize-methods.js +134 -0
- package/backend/tasks/tasks-configs.js +18 -0
- package/backend/tasks/tasks-helpers-methods.js +24 -20
- package/backend/tasks/tasks-process-methods.js +33 -17
- package/backend/utils.js +42 -0
- package/package.json +4 -3
- package/pages/Home.js +0 -19
- package/public/Utils/homePage.js +1 -30
- package/public/Utils/sharedUtils.js +24 -0
- package/backend/login/login-methods-factory.js +0 -24
- package/public/Utils/homePageLoadTrace.js +0 -58
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const { taskManager } = require('psdev-task-manager');
|
|
2
2
|
|
|
3
3
|
const { COLLECTIONS } = require('../../public/consts');
|
|
4
|
-
const { COMPILED_FILTERS_FIELDS, CONFIG_KEYS } = require('../consts');
|
|
4
|
+
const { COMPILED_FILTERS_FIELDS, CONFIG_KEYS, LOGIN_EMAIL_SYNC_STATUS } = require('../consts');
|
|
5
|
+
const { changeWixMembersEmails, summarizeLoginEmailOutcomes } = require('../daily-pull/utils');
|
|
5
6
|
const { wixData } = require('../elevated-modules');
|
|
6
|
-
const { updateWixMemberLoginEmail } = require('../members-area-methods');
|
|
7
7
|
const {
|
|
8
8
|
getAllEmptyAboutYouMembers,
|
|
9
9
|
getAllMembersWithExternalImages,
|
|
@@ -481,6 +481,7 @@ const syncMemberLoginEmails = async data => {
|
|
|
481
481
|
membersToUpdate.push({
|
|
482
482
|
...member,
|
|
483
483
|
email: newEmail,
|
|
484
|
+
previousLoginEmail: member.email,
|
|
484
485
|
});
|
|
485
486
|
}
|
|
486
487
|
|
|
@@ -497,14 +498,35 @@ const syncMemberLoginEmails = async data => {
|
|
|
497
498
|
|
|
498
499
|
for (const chunk of updateChunks) {
|
|
499
500
|
try {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
501
|
+
// Change Wix login emails first; only advance the CMS login email for the ones that
|
|
502
|
+
// succeeded. Failures keep their previous email (CMS stays consistent with Wix) and are
|
|
503
|
+
// reported in result.errors for manual handling.
|
|
504
|
+
const outcomes = await changeWixMembersEmails(chunk);
|
|
505
|
+
const { failedMemberIds } = summarizeLoginEmailOutcomes(outcomes);
|
|
506
|
+
const toSave = chunk.map(member => {
|
|
507
|
+
const { previousLoginEmail, ...restMember } = member;
|
|
508
|
+
if (failedMemberIds.has(member.memberId) && previousLoginEmail !== undefined) {
|
|
509
|
+
return { ...restMember, email: previousLoginEmail };
|
|
510
|
+
}
|
|
511
|
+
return restMember;
|
|
512
|
+
});
|
|
513
|
+
await bulkSaveMembers(toSave);
|
|
514
|
+
|
|
515
|
+
outcomes.forEach(outcome => {
|
|
516
|
+
if (outcome.status === LOGIN_EMAIL_SYNC_STATUS.UPDATED) {
|
|
517
|
+
result.successful++;
|
|
518
|
+
} else if (outcome.status === LOGIN_EMAIL_SYNC_STATUS.FAILED) {
|
|
519
|
+
result.failed++;
|
|
520
|
+
result.errors.push({
|
|
521
|
+
memberId: outcome.memberId,
|
|
522
|
+
wixMemberId: outcome.wixMemberId,
|
|
523
|
+
desiredEmail: outcome.desiredEmail,
|
|
524
|
+
error: outcome.error,
|
|
525
|
+
});
|
|
526
|
+
} else {
|
|
527
|
+
result.skipped++;
|
|
528
|
+
}
|
|
529
|
+
});
|
|
508
530
|
} catch (error) {
|
|
509
531
|
console.error(`❌ Error updating chunk ${chunkIndex}:`, error);
|
|
510
532
|
result.failed += chunk.length;
|
|
@@ -515,14 +537,8 @@ const syncMemberLoginEmails = async data => {
|
|
|
515
537
|
});
|
|
516
538
|
}
|
|
517
539
|
}
|
|
518
|
-
// Log comprehensive results including Wix member updates
|
|
519
|
-
const wixStats = result.wixMemberUpdates || { successful: 0, failed: 0 };
|
|
520
|
-
console.log(`Login Emails sync task completed:`);
|
|
521
|
-
console.log(
|
|
522
|
-
` - Member data updates: ${result.successful} successful, ${result.failed} failed, ${result.skipped} skipped`
|
|
523
|
-
);
|
|
524
540
|
console.log(
|
|
525
|
-
`
|
|
541
|
+
`Login Emails sync task completed: ${result.successful} synced, ${result.failed} failed, ${result.skipped} skipped`
|
|
526
542
|
);
|
|
527
543
|
|
|
528
544
|
return result;
|
package/backend/utils.js
CHANGED
|
@@ -210,6 +210,46 @@ function formatDateOnly(dateStr) {
|
|
|
210
210
|
|
|
211
211
|
const runIf = (condition, asyncFn) => (condition ? asyncFn() : Promise.resolve(null));
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Whether an error looks like a transient network failure that is safe to retry,
|
|
215
|
+
* e.g. undici's generic "fetch failed" thrown by the Wix SDKs, connection resets
|
|
216
|
+
* or DNS hiccups under heavy load.
|
|
217
|
+
* @param {Error} error
|
|
218
|
+
* @returns {boolean}
|
|
219
|
+
*/
|
|
220
|
+
const isTransientNetworkError = error => {
|
|
221
|
+
const message = `${error?.message || ''} ${error?.cause?.message || ''} ${error?.code || ''} ${error?.cause?.code || ''}`;
|
|
222
|
+
return /fetch failed|ECONNRESET|ETIMEDOUT|ECONNREFUSED|EAI_AGAIN|EPIPE|UND_ERR|socket hang up|network error/i.test(
|
|
223
|
+
message
|
|
224
|
+
);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Runs an async operation, retrying with exponential backoff when it fails with a
|
|
229
|
+
* transient network error. Non-transient errors are rethrown immediately.
|
|
230
|
+
* @param {Function} operation - Async function to run
|
|
231
|
+
* @param {Object} [options]
|
|
232
|
+
* @param {number} [options.retries=2] - Maximum number of retries after the first attempt
|
|
233
|
+
* @param {number} [options.baseDelayMs=500] - Delay before the first retry, doubled each retry
|
|
234
|
+
* @returns {Promise<any>} - The operation's resolved value
|
|
235
|
+
*/
|
|
236
|
+
const withTransientErrorRetry = async (operation, { retries = 2, baseDelayMs = 500 } = {}) => {
|
|
237
|
+
for (let attempt = 0; ; attempt++) {
|
|
238
|
+
try {
|
|
239
|
+
return await operation();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (attempt >= retries || !isTransientNetworkError(error)) {
|
|
242
|
+
throw error;
|
|
243
|
+
}
|
|
244
|
+
const delayMs = baseDelayMs * 2 ** attempt;
|
|
245
|
+
console.warn(
|
|
246
|
+
`Transient network error (attempt ${attempt + 1}/${retries + 1}): ${error.message}. Retrying in ${delayMs}ms`
|
|
247
|
+
);
|
|
248
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
213
253
|
module.exports = {
|
|
214
254
|
getSiteConfigs,
|
|
215
255
|
retrieveAllItems,
|
|
@@ -232,4 +272,6 @@ module.exports = {
|
|
|
232
272
|
isPAC_STAFF,
|
|
233
273
|
searchAllItems,
|
|
234
274
|
runIf,
|
|
275
|
+
isTransientNetworkError,
|
|
276
|
+
withTransientErrorRetry,
|
|
235
277
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abmp-npm",
|
|
3
|
-
"version": "10.3.
|
|
3
|
+
"version": "10.3.9",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"check-cycles": "madge --circular .",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@wix/automations": "^1.0.261",
|
|
36
36
|
"@wix/crm": "^1.0.1061",
|
|
37
37
|
"@wix/data": "^1.0.349",
|
|
38
|
-
"@wix/essentials": "^0.
|
|
39
|
-
"@wix/identity": "^1.0.
|
|
38
|
+
"@wix/essentials": "^1.0.7",
|
|
39
|
+
"@wix/identity": "^1.0.195",
|
|
40
40
|
"@wix/media": "^1.0.213",
|
|
41
41
|
"@wix/members": "^1.0.365",
|
|
42
42
|
"@wix/secrets": "^1.0.62",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"axios": "^1.13.1",
|
|
50
50
|
"crypto": "^1.0.1",
|
|
51
51
|
"csv-parser": "^3.0.0",
|
|
52
|
+
"debug": "^4.4.3",
|
|
52
53
|
"jwt-js-decode": "^1.9.0",
|
|
53
54
|
"lodash": "^4.17.21",
|
|
54
55
|
"ngeohash": "^0.6.3",
|
package/pages/Home.js
CHANGED
|
@@ -5,7 +5,6 @@ const { withWarmUpData } = require('psdev-utils/frontend');
|
|
|
5
5
|
|
|
6
6
|
const { ADDRESS_STATUS_TYPES, DEFAULT_FILTER, DROPDOWN_OPTIONS } = require('../public/consts.js');
|
|
7
7
|
const { createHomepageUtils } = require('../public/Utils/homePage.js');
|
|
8
|
-
const { logHomePageLoadPhase } = require('../public/Utils/homePageLoadTrace.js');
|
|
9
8
|
const {
|
|
10
9
|
getMainAddress,
|
|
11
10
|
formatPracticeAreasForDisplay,
|
|
@@ -14,8 +13,6 @@ const {
|
|
|
14
13
|
normalizeExternalUrl,
|
|
15
14
|
} = require('../public/Utils/sharedUtils.js');
|
|
16
15
|
|
|
17
|
-
logHomePageLoadPhase('home_js_module_evaluated');
|
|
18
|
-
|
|
19
16
|
let filter = JSON.parse(JSON.stringify(DEFAULT_FILTER));
|
|
20
17
|
let dropDownOptions = JSON.parse(JSON.stringify(DROPDOWN_OPTIONS));
|
|
21
18
|
let stateCityMap;
|
|
@@ -39,7 +36,6 @@ const homePageOnReady = async ({
|
|
|
39
36
|
getNonCompiledFiltersOptions,
|
|
40
37
|
filterProfiles,
|
|
41
38
|
}) => {
|
|
42
|
-
logHomePageLoadPhase('wix_onready_handler_entered');
|
|
43
39
|
const {
|
|
44
40
|
getParamsMapping,
|
|
45
41
|
handlePagination,
|
|
@@ -62,9 +58,7 @@ const homePageOnReady = async ({
|
|
|
62
58
|
detectMobile();
|
|
63
59
|
initPageUI();
|
|
64
60
|
attachEventListeners();
|
|
65
|
-
logHomePageLoadPhase('before_handleUrlParams');
|
|
66
61
|
await handleUrlParams();
|
|
67
|
-
logHomePageLoadPhase('after_handleUrlParams');
|
|
68
62
|
|
|
69
63
|
async function detectMobile() {
|
|
70
64
|
try {
|
|
@@ -294,9 +288,7 @@ const homePageOnReady = async ({
|
|
|
294
288
|
}
|
|
295
289
|
|
|
296
290
|
async function applyFilterToUI(isDefaultStateParams) {
|
|
297
|
-
logHomePageLoadPhase('applyFilterToUI_start', { isDefaultStateParams });
|
|
298
291
|
const renderingEnv = await rendering.env();
|
|
299
|
-
logHomePageLoadPhase('applyFilterToUI_rendering_env', { env: renderingEnv });
|
|
300
292
|
const setFilterFromParams = async (isInitializeValue = true) => {
|
|
301
293
|
const params = await wixLocation.query();
|
|
302
294
|
console.log('params inside setFilterFromParams ', params);
|
|
@@ -330,9 +322,7 @@ const homePageOnReady = async ({
|
|
|
330
322
|
await setFilterFromParams(true);
|
|
331
323
|
if (isDefaultStateParams) {
|
|
332
324
|
console.log('default state set for nearby');
|
|
333
|
-
logHomePageLoadPhase('applyFilterToUI_default_path_fetch_and_nearby_start');
|
|
334
325
|
await Promise.all([fetchFilterData(), nearByHandler(true)]);
|
|
335
|
-
logHomePageLoadPhase('applyFilterToUI_default_path_complete');
|
|
336
326
|
return;
|
|
337
327
|
}
|
|
338
328
|
console.log('not default state');
|
|
@@ -351,15 +341,12 @@ const homePageOnReady = async ({
|
|
|
351
341
|
: () => updateResults('filterTimeout', true);
|
|
352
342
|
console.log('filter ..', filter);
|
|
353
343
|
try {
|
|
354
|
-
logHomePageLoadPhase('applyFilterToUI_non_default_path_start');
|
|
355
344
|
await Promise.all([
|
|
356
345
|
fetchFilterData().then(() => setFilterFromParams(false)),
|
|
357
346
|
//TODO: remove this workaround to fix issue with SSR showing invalid results
|
|
358
347
|
renderingEnv === 'backend' ? Promise.resolve() : searchPromise(),
|
|
359
348
|
]);
|
|
360
|
-
logHomePageLoadPhase('applyFilterToUI_non_default_path_complete');
|
|
361
349
|
} catch (error) {
|
|
362
|
-
logHomePageLoadPhase('applyFilterToUI_error', { message: String(error && error.message) });
|
|
363
350
|
console.error('[applyFilterToUI] failed with error:', error);
|
|
364
351
|
multiStateBoxSelector.changeState('errorState');
|
|
365
352
|
}
|
|
@@ -438,7 +425,6 @@ const homePageOnReady = async ({
|
|
|
438
425
|
}
|
|
439
426
|
// NEAR BY FILTER
|
|
440
427
|
async function nearByHandler(preservePagination = false) {
|
|
441
|
-
logHomePageLoadPhase('nearByHandler_start', { preservePagination });
|
|
442
428
|
const isSearchingNearby = _$w('#nearBy').checked;
|
|
443
429
|
const renderingEnv = await rendering.env();
|
|
444
430
|
// 1. Disable nearby input while processing
|
|
@@ -452,7 +438,6 @@ const homePageOnReady = async ({
|
|
|
452
438
|
filter = newFilter;
|
|
453
439
|
console.log('filter inside nearByHandler', filter);
|
|
454
440
|
if (!success) {
|
|
455
|
-
logHomePageLoadPhase('nearByHandler_geolocation_failed');
|
|
456
441
|
if (renderingEnv !== 'backend') {
|
|
457
442
|
//on Backend environment, geolocation API don't work, so makes no sense to change state for near by
|
|
458
443
|
multiStateBoxSelector.changeState('nearByState');
|
|
@@ -467,7 +452,6 @@ const homePageOnReady = async ({
|
|
|
467
452
|
// If location is not selected, change state to "resultsState"
|
|
468
453
|
if (!isSearchingNearby) {
|
|
469
454
|
if (await noSearchCriteria()) {
|
|
470
|
-
logHomePageLoadPhase('nearByHandler_no_search_criteria');
|
|
471
455
|
multiStateBoxSelector.changeState('noSearchCriteria');
|
|
472
456
|
// 4. Re-enable nearby input
|
|
473
457
|
_$w('#nearBy').enable();
|
|
@@ -479,7 +463,6 @@ const homePageOnReady = async ({
|
|
|
479
463
|
|
|
480
464
|
// 4. Re-enable nearby input when done
|
|
481
465
|
_$w('#nearBy').enable();
|
|
482
|
-
logHomePageLoadPhase('nearByHandler_complete', { success: true });
|
|
483
466
|
return true;
|
|
484
467
|
}
|
|
485
468
|
|
|
@@ -487,7 +470,6 @@ const homePageOnReady = async ({
|
|
|
487
470
|
// FETCH STATE/CITY/AREAS OF PRACTICE FROM BACKEND ONCE AND STORE IT
|
|
488
471
|
|
|
489
472
|
async function fetchFilterData() {
|
|
490
|
-
logHomePageLoadPhase('fetchFilterData_start');
|
|
491
473
|
let completeStateList, areasOfPracticesList, stateCityMapList;
|
|
492
474
|
try {
|
|
493
475
|
const { COMPILED_STATE_LIST, COMPILED_AREAS_OF_PRACTICES, COMPILED_STATE_CITY_MAP } =
|
|
@@ -528,7 +510,6 @@ const homePageOnReady = async ({
|
|
|
528
510
|
|
|
529
511
|
// Update filter states after data is loaded
|
|
530
512
|
updateFiltersState();
|
|
531
|
-
logHomePageLoadPhase('fetchFilterData_complete');
|
|
532
513
|
}
|
|
533
514
|
|
|
534
515
|
// CONSTRUCT DROPDOWN OPTIONS FOR STATE, CITY, AREA OF PRACTICES
|
package/public/Utils/homePage.js
CHANGED
|
@@ -3,7 +3,6 @@ const { window: wixWindow, rendering } = require('@wix/site-window');
|
|
|
3
3
|
|
|
4
4
|
const { DEFAULT_FILTER } = require('../consts.js');
|
|
5
5
|
|
|
6
|
-
const { logHomePageLoadPhase } = require('./homePageLoadTrace.js');
|
|
7
6
|
const { debouncedFunction } = require('./sharedUtils.js');
|
|
8
7
|
|
|
9
8
|
function isValidGeolocation(lat, lng) {
|
|
@@ -400,8 +399,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
400
399
|
});
|
|
401
400
|
}
|
|
402
401
|
async function getAndSetUserLocation(isSearchingNearby, filter) {
|
|
403
|
-
logHomePageLoadPhase('getAndSetUserLocation_start', { isSearchingNearby });
|
|
404
|
-
|
|
405
402
|
const { latitude: existingLat, longitude: existingLng } = filter;
|
|
406
403
|
if (isValidGeolocation(existingLat, existingLng)) {
|
|
407
404
|
return {
|
|
@@ -411,23 +408,16 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
411
408
|
}
|
|
412
409
|
|
|
413
410
|
try {
|
|
414
|
-
logHomePageLoadPhase('getAndSetUserLocation_before_getCurrentGeolocation');
|
|
415
411
|
const location = await getCurrentGeolocationWithTimeout();
|
|
412
|
+
|
|
416
413
|
console.log('location inside getAndSetUserLocation', location);
|
|
417
414
|
const userLat = location.coords?.latitude ?? 0;
|
|
418
415
|
const userLong = location.coords?.longitude ?? 0;
|
|
419
|
-
logHomePageLoadPhase('getAndSetUserLocation_success', {
|
|
420
|
-
lat: userLat,
|
|
421
|
-
lng: userLong,
|
|
422
|
-
});
|
|
423
416
|
return {
|
|
424
417
|
success: true,
|
|
425
418
|
filter: applyGeolocationToFilter(filter, userLat, userLong, isSearchingNearby),
|
|
426
419
|
};
|
|
427
420
|
} catch (error) {
|
|
428
|
-
logHomePageLoadPhase('getAndSetUserLocation_error', {
|
|
429
|
-
message: String(error && error.message),
|
|
430
|
-
});
|
|
431
421
|
console.warn('Failed to get user location in getAndSetUserLocation', error);
|
|
432
422
|
return { success: false, filter };
|
|
433
423
|
}
|
|
@@ -546,7 +536,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
546
536
|
);
|
|
547
537
|
}
|
|
548
538
|
async function parseAndValidateQueryParams(filter, pagination) {
|
|
549
|
-
logHomePageLoadPhase('parseAndValidateQueryParams_start');
|
|
550
539
|
const params = await wixLocation.query();
|
|
551
540
|
const paramsMapping = getParamsMapping(filter, pagination);
|
|
552
541
|
const {
|
|
@@ -558,11 +547,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
558
547
|
const isSearchingNearby = params.nearby === 'true';
|
|
559
548
|
const isNoParams = !withoutPreviewParams || Object.keys(withoutPreviewParams).length === 0;
|
|
560
549
|
const { success, filter: newFilter } = await getAndSetUserLocation(isSearchingNearby, filter);
|
|
561
|
-
logHomePageLoadPhase('parseAndValidateQueryParams_after_geolocation', {
|
|
562
|
-
isNoParams,
|
|
563
|
-
isSearchingNearby,
|
|
564
|
-
success,
|
|
565
|
-
});
|
|
566
550
|
|
|
567
551
|
// Auto-enable nearby if GPS permission granted on fresh page load
|
|
568
552
|
if (
|
|
@@ -573,7 +557,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
573
557
|
!isSearchingNearby
|
|
574
558
|
) {
|
|
575
559
|
await wixQueryParams.add({ nearby: 'true', page: '1' });
|
|
576
|
-
logHomePageLoadPhase('parseAndValidateQueryParams_return', { branch: 'auto_nearby_url' });
|
|
577
560
|
return { isDefaultStateParams: true, filter: newFilter };
|
|
578
561
|
}
|
|
579
562
|
|
|
@@ -587,7 +570,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
587
570
|
// });
|
|
588
571
|
// Don't search yet - let the caller decide what to do
|
|
589
572
|
// The search will be handled in applyFilterToUI
|
|
590
|
-
logHomePageLoadPhase('parseAndValidateQueryParams_return', { branch: 'default_no_params' });
|
|
591
573
|
return { isDefaultStateParams: true, filter: newFilter };
|
|
592
574
|
}
|
|
593
575
|
let autoAdjustFilters = false;
|
|
@@ -627,10 +609,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
627
609
|
withoutPreviewParams.page) ||
|
|
628
610
|
(Object.keys(withoutPreviewParams).length === 1 && withoutPreviewParams.nearby);
|
|
629
611
|
const isDefaultStateParams = isNoParams || isNearbyFilter;
|
|
630
|
-
logHomePageLoadPhase('parseAndValidateQueryParams_return', {
|
|
631
|
-
branch: 'with_query_params',
|
|
632
|
-
isDefaultStateParams,
|
|
633
|
-
});
|
|
634
612
|
return { isDefaultStateParams, filter: newFilter };
|
|
635
613
|
}
|
|
636
614
|
|
|
@@ -714,7 +692,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
714
692
|
isSearchingNearby,
|
|
715
693
|
preservePagination = false,
|
|
716
694
|
}) {
|
|
717
|
-
logHomePageLoadPhase('search_start', { timeoutType, isSearchingNearby });
|
|
718
695
|
const multiStateBoxSelector = _$w('#resultsStateBox');
|
|
719
696
|
const renderingEnv = await rendering.env();
|
|
720
697
|
const initSearchResultsUI = () => {
|
|
@@ -736,7 +713,6 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
736
713
|
longitude: 0,
|
|
737
714
|
}) === JSON.stringify(DEFAULT_FILTER)
|
|
738
715
|
) {
|
|
739
|
-
logHomePageLoadPhase('search_short_circuit_no_criteria');
|
|
740
716
|
multiStateBoxSelector.changeState('noSearchCriteria');
|
|
741
717
|
return [];
|
|
742
718
|
}
|
|
@@ -760,19 +736,14 @@ const createHomepageUtils = (_$w, filterProfiles) => {
|
|
|
760
736
|
timeoutType,
|
|
761
737
|
args: { filter, isSearchingNearby },
|
|
762
738
|
});
|
|
763
|
-
logHomePageLoadPhase('search_before_filterProfiles', { renderingEnv });
|
|
764
739
|
const { success, response, error } = await funcPromise();
|
|
765
740
|
if (!success) {
|
|
766
741
|
_$w('#numberOfResults').text = '';
|
|
767
742
|
console.error('[search] failed with error:', error);
|
|
768
|
-
logHomePageLoadPhase('search_filterProfiles_failed', {
|
|
769
|
-
message: String(error && error.message),
|
|
770
|
-
});
|
|
771
743
|
multiStateBoxSelector.changeState('errorState');
|
|
772
744
|
return [];
|
|
773
745
|
}
|
|
774
746
|
const totalCount = response.items.length;
|
|
775
|
-
logHomePageLoadPhase('search_filterProfiles_success', { totalCount });
|
|
776
747
|
if (!totalCount) {
|
|
777
748
|
_$w('#numberOfResults').text = 'Showing 0 results';
|
|
778
749
|
_$w('#noResultsMessage').text = `${
|
|
@@ -195,6 +195,28 @@ function normalizeExternalUrl(url) {
|
|
|
195
195
|
return `https://${trimmed}`;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Normalizes an email for comparison (lowercased + trimmed).
|
|
200
|
+
* Wix CRM treats emails as case-insensitive, so comparisons must too.
|
|
201
|
+
* @param {string} email
|
|
202
|
+
* @returns {string}
|
|
203
|
+
*/
|
|
204
|
+
function normalizeEmail(email) {
|
|
205
|
+
return typeof email === 'string' ? email.trim().toLowerCase() : '';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Case-insensitive email equality. Two empty/missing emails are not considered a match.
|
|
210
|
+
* @param {string} a
|
|
211
|
+
* @param {string} b
|
|
212
|
+
* @returns {boolean}
|
|
213
|
+
*/
|
|
214
|
+
function emailsMatch(a, b) {
|
|
215
|
+
const normalizedA = normalizeEmail(a);
|
|
216
|
+
const normalizedB = normalizeEmail(b);
|
|
217
|
+
return Boolean(normalizedA) && normalizedA === normalizedB;
|
|
218
|
+
}
|
|
219
|
+
|
|
198
220
|
module.exports = {
|
|
199
221
|
checkAddressIsVisible,
|
|
200
222
|
formatPracticeAreasForDisplay,
|
|
@@ -208,4 +230,6 @@ module.exports = {
|
|
|
208
230
|
formatAddress,
|
|
209
231
|
isWixHostedImage,
|
|
210
232
|
normalizeExternalUrl,
|
|
233
|
+
normalizeEmail,
|
|
234
|
+
emailsMatch,
|
|
211
235
|
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
const { loginQAMember } = require('./qa-login-methods');
|
|
2
|
-
const { authenticateSSOToken } = require('./sso-methods');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Creates login methods with injected generateSessionToken dependency
|
|
6
|
-
* @param {Function} generateSessionToken - The Velo generateSessionToken function to inject
|
|
7
|
-
* @returns {Object} Object containing loginQAMember and authenticateSSOToken methods
|
|
8
|
-
*/
|
|
9
|
-
const createLoginMethods = generateSessionToken => {
|
|
10
|
-
//There is no generateSessionToken SDK version, and the signOn of @wix/identity returns 403 error regardless that the permissions are valid
|
|
11
|
-
//Therefore, as a workaround we need to inject the Velo version of generateSessionToken to the login methods.
|
|
12
|
-
const injectGenerateSessionTokenToMethod =
|
|
13
|
-
method =>
|
|
14
|
-
async (...args) =>
|
|
15
|
-
await method(...args, generateSessionToken);
|
|
16
|
-
return {
|
|
17
|
-
loginQAMember: injectGenerateSessionTokenToMethod(loginQAMember),
|
|
18
|
-
authenticateSSOToken: injectGenerateSessionTokenToMethod(authenticateSSOToken),
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
module.exports = {
|
|
23
|
-
createLoginMethods,
|
|
24
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One session per full page load. First log call creates `loadId` (send this to support for GCL search).
|
|
3
|
-
* Logs a plain object so DevTools shows an expandable tree; `loadId` is still easy to copy for GCL.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
function randomSegment() {
|
|
7
|
-
return Math.random().toString(36).slice(2, 10);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function nowMs() {
|
|
11
|
-
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
12
|
-
return performance.now();
|
|
13
|
-
}
|
|
14
|
-
return Date.now();
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
let session = null;
|
|
18
|
-
|
|
19
|
-
function ensureSession() {
|
|
20
|
-
if (session) {
|
|
21
|
-
return session;
|
|
22
|
-
}
|
|
23
|
-
const t0 = nowMs();
|
|
24
|
-
session = {
|
|
25
|
-
loadId: `hpl_${Date.now()}_${randomSegment()}_${randomSegment()}`,
|
|
26
|
-
t0,
|
|
27
|
-
};
|
|
28
|
-
return session;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {string} phase
|
|
33
|
-
* @param {Record<string, unknown>} [detail]
|
|
34
|
-
*/
|
|
35
|
-
function logHomePageLoadPhase(phase, detail) {
|
|
36
|
-
const s = ensureSession();
|
|
37
|
-
const elapsed = Math.round(nowMs() - s.t0);
|
|
38
|
-
const payload = {
|
|
39
|
-
type: 'HomePageLoad',
|
|
40
|
-
loadId: s.loadId,
|
|
41
|
-
phase,
|
|
42
|
-
elapsedSinceStartMs: elapsed,
|
|
43
|
-
wallTimeIso: new Date().toISOString(),
|
|
44
|
-
};
|
|
45
|
-
if (detail && typeof detail === 'object') {
|
|
46
|
-
payload.detail = detail;
|
|
47
|
-
}
|
|
48
|
-
console.log('[HomePageLoad]', payload);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function getHomePageLoadId() {
|
|
52
|
-
return ensureSession().loadId;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
module.exports = {
|
|
56
|
-
logHomePageLoadPhase,
|
|
57
|
-
getHomePageLoadId,
|
|
58
|
-
};
|