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,779 +0,0 @@
1
- //home page code
2
- const { location: wixLocation } = require('@wix/site-location');
3
- const { window: wixWindow, rendering } = require('@wix/site-window');
4
- const { withWarmUpData } = require('psdev-utils/frontend');
5
-
6
- const { ADDRESS_STATUS_TYPES, DEFAULT_FILTER, DROPDOWN_OPTIONS } = require('../public/consts.js');
7
- const { createHomepageUtils } = require('../public/Utils/homePage.js');
8
- const {
9
- getMainAddress,
10
- formatPracticeAreasForDisplay,
11
- checkAddressIsVisible,
12
- isWixHostedImage,
13
- normalizeExternalUrl,
14
- } = require('../public/Utils/sharedUtils.js');
15
-
16
- let filter = JSON.parse(JSON.stringify(DEFAULT_FILTER));
17
- let dropDownOptions = JSON.parse(JSON.stringify(DROPDOWN_OPTIONS));
18
- let stateCityMap;
19
- let retryAttempts = 0;
20
- const sidePanelFilterData = new Map();
21
- const stateNameCodeMap = new Map();
22
- let multiStateBoxSelector;
23
- // Debounce variables
24
- const debounceTimeout = {};
25
- // Pagination variables
26
- const pagination = {
27
- pageSize: 12,
28
- currentPage: 0,
29
- };
30
- let searchResults = [];
31
- let isMobile = false;
32
-
33
- const homePageOnReady = async ({
34
- _$w,
35
- getCompiledFiltersOptions,
36
- getNonCompiledFiltersOptions,
37
- filterProfiles,
38
- }) => {
39
- const {
40
- getParamsMapping,
41
- handlePagination,
42
- getFiltersSelectors,
43
- onChangeMultiCheckbox,
44
- setDefaultCity,
45
- setDefaultState,
46
- setDefaultFilterOption,
47
- prepareOptionsFunction,
48
- getAndSetUserLocation,
49
- setDefaultDropdownState,
50
- toggleDropdownFunctionality,
51
- showFiltersOnDesktop,
52
- filterOptionsFunction,
53
- parseAndValidateQueryParams,
54
- updateUrlParams,
55
- noSearchCriteria,
56
- search,
57
- } = createHomepageUtils(_$w, filterProfiles);
58
- detectMobile();
59
- initPageUI();
60
- attachEventListeners();
61
- await handleUrlParams();
62
-
63
- async function detectMobile() {
64
- try {
65
- const formFactor = await wixWindow.formFactor();
66
- isMobile = formFactor === 'Mobile';
67
- } catch (error) {
68
- isMobile = false;
69
- console.log('Mobile detection error, assuming desktop:', error);
70
- }
71
- }
72
-
73
- function initPageUI() {
74
- multiStateBoxSelector = _$w('#resultsStateBox');
75
- multiStateBoxSelector.changeState('loadingState');
76
- _$w('#searchDesktop').expand();
77
- _$w('#showingResult').expand();
78
- showFiltersOnDesktop();
79
- }
80
-
81
- async function attachEventListeners() {
82
- /**
83
- * PAGINATION CODE
84
- */
85
- _$w('#previousPage').onClick(() => {
86
- handlePagination({ delta: -1, pagination, searchResults, filter });
87
- });
88
-
89
- _$w('#nextPage').onClick(() => {
90
- handlePagination({ delta: 1, pagination, searchResults, filter });
91
- });
92
-
93
- _$w(
94
- '#pageButton1,#pageButton2,#pageButton3,#pageButton4,#pageButton5,#pageButton6,#pageButton7,#pageButton8,#pageButton9,#pageButton10,#pageButton11,#pageButton12,#pageButton13,#pageButton14,#pageButton15'
95
- ).onClick(event => {
96
- const label = event.target.label;
97
- const pageNumber = Number(label) - 1;
98
- if (pageNumber === pagination.currentPage) return;
99
- handlePagination({
100
- delta:
101
- pageNumber > pagination.currentPage
102
- ? pageNumber - pagination.currentPage
103
- : (pagination.currentPage - pageNumber) * -1,
104
- pagination,
105
- searchResults,
106
- filter,
107
- });
108
- });
109
- _$w('#resetFilter').onClick(resetFilter);
110
- _$w('#clearButton').onClick(resetFilter);
111
-
112
- _$w('#searchDesktop').onInput(event => searchProfilesOnSearchText(event));
113
- _$w('#searchTabletMobile').onInput(event => searchProfilesOnSearchText(event));
114
- // Use onChange for the switch and onClick for the retry button
115
- _$w('#nearBy').onChange(() => {
116
- updateFiltersState();
117
- if (isMobile) {
118
- return;
119
- }
120
- nearByHandler();
121
- });
122
-
123
- _$w('#nearByRetryButton').onClick(() => {
124
- nearByHandler();
125
- });
126
-
127
- // ZIP CODE FILTER
128
- _$w('#zipcode').onInput(async event => {
129
- const zipcode = event.target.value.trim();
130
- zipcode === '' ? (filter.postalcode = null) : (filter.postalcode = zipcode);
131
- updateFiltersState();
132
-
133
- if (isMobile) {
134
- return;
135
- }
136
-
137
- await updateResults('filterTimeout');
138
- });
139
-
140
- _$w('#filterButton').onClick(() => {
141
- _$w('#filtersContainer').expand();
142
- _$w('#filtersContainer').scrollTo();
143
- });
144
-
145
- _$w('#closeFiltersButton').onClick(() => _$w('#filtersContainer').collapse());
146
- _$w('#applyButton').onClick(async () => {
147
- _$w('#filtersContainer').collapse();
148
-
149
- // Update URL params with current filter state before searching
150
- updateFiltersState();
151
- await updateUrlParams(filter, pagination);
152
- await updateResults('zeroTimeout');
153
- });
154
-
155
- // Retry
156
-
157
- _$w('#retryButton').onClick(async () => {
158
- if (retryAttempts > 1) {
159
- resetFilter();
160
-
161
- return;
162
- }
163
-
164
- retryAttempts += 1;
165
-
166
- await updateResults('zeroTimeout');
167
- });
168
-
169
- _$w('#clearSearch').onClick(async () => {
170
- if (!filter.searchText || filter.searchText.length === 0) {
171
- await resetFilter();
172
-
173
- return;
174
- }
175
-
176
- filter.searchText = null;
177
-
178
- _$w('#searchDesktop').value = undefined;
179
- _$w('#searchTabletMobile').value = undefined;
180
-
181
- await updateResults('zeroTimeout');
182
- });
183
- const baseUrl = await wixLocation.baseUrl();
184
- _$w('#profileRepeater').onItemReady(($item, itemData) => {
185
- // 1) safely default to arrays
186
- const addresses = Array.isArray(itemData.addresses) ? itemData.addresses : [];
187
- const areasOfPractices = Array.isArray(itemData.areasOfPractices)
188
- ? itemData.areasOfPractices
189
- : [];
190
-
191
- // 2) Profile image
192
- if (itemData.profileImage?.trim() && isWixHostedImage(itemData.profileImage)) {
193
- $item('#profileImage').src = itemData.profileImage;
194
- }
195
-
196
- // 3) Website link
197
- if (!itemData.showWixUrl && !itemData.showWebsite) {
198
- $item('#website').collapse();
199
- $item('#websiteContainer').collapse();
200
- } else {
201
- if (itemData.showWebsite) {
202
- $item('#website').link = normalizeExternalUrl(itemData.website);
203
- } else {
204
- $item('#website').link = `${baseUrl}/profile/${itemData.url}`;
205
- }
206
- $item('#website').target = '_blank';
207
- }
208
-
209
- // 4) Full name
210
- $item('#fullName').text = itemData.fullName || '';
211
-
212
- // 5) Location text
213
- const mainAddress = getMainAddress(itemData.addressDisplayOption, addresses);
214
- $item('#location').text = mainAddress || '';
215
-
216
- // 6) Miles away
217
- const isNearbyEnabled = _$w('#nearBy').checked;
218
- const miles = itemData.distance ?? 0;
219
-
220
- if (isNearbyEnabled && miles) {
221
- $item('#differenceInMiles').text = miles.toFixed(1);
222
- $item('#milesAwayText').text = 'miles away';
223
- } else {
224
- $item('#differenceInMiles').text = '';
225
- $item('#milesAwayText').text = '';
226
- }
227
-
228
- // 7) "Show maps" button enabled only if there's a full address with valid coordinates
229
- const visible = checkAddressIsVisible(addresses);
230
- const fullAddressWithValidCoords = visible.find(
231
- addr =>
232
- addr.addressStatus === ADDRESS_STATUS_TYPES.FULL_ADDRESS &&
233
- addr.latitude &&
234
- addr.longitude
235
- );
236
-
237
- if (fullAddressWithValidCoords) {
238
- $item('#showMaps').enable();
239
- $item('#showMaps').show();
240
- const { latitude, longitude } = fullAddressWithValidCoords;
241
- $item('#showMaps').link = `https://maps.google.com/?q=${latitude},${longitude}`;
242
- $item('#showMaps').target = '_blank';
243
- } else {
244
- $item('#showMaps').hide();
245
- }
246
-
247
- // 8) Phone / contact form
248
- if (itemData.showContactForm) {
249
- $item('#call').expand();
250
- $item('#callContainer').expand();
251
- $item('#call').onClick(() => wixWindow.openLightbox('Contact Us', itemData));
252
- } else {
253
- $item('#call').collapse();
254
- $item('#callContainer').collapse();
255
- }
256
-
257
- // 9) "Book now" button
258
- if (itemData.bookingUrl) {
259
- $item('#bookNowButton').show();
260
- $item('#bookNowButton').link = normalizeExternalUrl(itemData.bookingUrl);
261
- $item('#bookNowButton').target = '_blank';
262
- } else {
263
- $item('#bookNowButton').hide();
264
- }
265
-
266
- // 10) Area of practices text
267
- const text = formatPracticeAreasForDisplay(areasOfPractices);
268
- if (text) {
269
- $item('#areaOfPracticesText').text = text;
270
- } else {
271
- $item('#areaOfPracticesText').collapse();
272
- }
273
-
274
- // 11) Hide separator if neither website nor call is shown
275
- if ($item('#website').collapsed && $item('#call').collapsed) {
276
- $item('#line').hide();
277
- }
278
- });
279
- }
280
-
281
- async function handleUrlParams() {
282
- const { isDefaultStateParams, filter: newFilter } = await parseAndValidateQueryParams(
283
- filter,
284
- pagination
285
- );
286
- filter = newFilter;
287
- await applyFilterToUI(isDefaultStateParams);
288
- }
289
-
290
- async function applyFilterToUI(isDefaultStateParams) {
291
- const renderingEnv = await rendering.env();
292
- const setFilterFromParams = async (isInitializeValue = true) => {
293
- const params = await wixLocation.query();
294
- console.log('params inside setFilterFromParams ', params);
295
- const paramsMapping = getParamsMapping(filter, pagination);
296
- for (const [param, { setValue, setUI }] of Object.entries(paramsMapping)) {
297
- const value = params[param];
298
- if (value !== undefined && value !== null && value !== '') {
299
- try {
300
- if (isInitializeValue) {
301
- console.log('setting value ', value, ' for param ', param);
302
- setValue({
303
- value: String(value),
304
- });
305
- } else {
306
- console.log('setting ui value ', value, ' for param ', param);
307
- setUI &&
308
- (await setUI({
309
- value: String(value),
310
- dropDownOptions,
311
- stateNameCodeMap,
312
- sidePanelFilterData,
313
- stateCityMap,
314
- }));
315
- }
316
- } catch (error) {
317
- console.error(`Error setting parameter ${param}:`, error);
318
- }
319
- }
320
- }
321
- };
322
- await setFilterFromParams(true);
323
- if (isDefaultStateParams) {
324
- console.log('default state set for nearby');
325
- await Promise.all([fetchFilterData(), nearByHandler(true)]);
326
- return;
327
- }
328
- console.log('not default state');
329
- const searchPromise =
330
- filter.searchText && filter.searchText.length > 0
331
- ? () =>
332
- search({
333
- filter,
334
- pagination,
335
- debounceTimeout,
336
- timeoutType: 'searchTimeout',
337
- isSearchingNearby: _$w('#nearBy').checked,
338
- }).then(result => {
339
- searchResults = result;
340
- })
341
- : () => updateResults('filterTimeout', true);
342
- console.log('filter ..', filter);
343
- try {
344
- await Promise.all([
345
- fetchFilterData().then(() => setFilterFromParams(false)),
346
- //TODO: remove this workaround to fix issue with SSR showing invalid results
347
- renderingEnv === 'backend' ? Promise.resolve() : searchPromise(),
348
- ]);
349
- } catch (error) {
350
- console.error('[applyFilterToUI] failed with error:', error);
351
- multiStateBoxSelector.changeState('errorState');
352
- }
353
- }
354
-
355
- /**
356
- * UPDATE PROFILES BASED ON APPLIED/DEFAULT FILTER
357
- */
358
- async function updateResults(timeoutType, preservePagination = false) {
359
- if (debounceTimeout[timeoutType]) {
360
- clearTimeout(debounceTimeout[timeoutType]);
361
- }
362
- searchResults = await search({
363
- filter,
364
- pagination,
365
- debounceTimeout,
366
- timeoutType,
367
- isSearchingNearby: _$w('#nearBy').checked,
368
- preservePagination,
369
- });
370
- !preservePagination && (await updateUrlParams(filter, pagination));
371
- return searchResults;
372
- }
373
-
374
- /**
375
- * LEFT SIDE FILTER PANEL CODE
376
- */
377
-
378
- // RESET FILTER
379
-
380
- async function resetFilter() {
381
- _$w('#resetFilter').hide();
382
- loadDefaultCheckBoxOptions('state');
383
- loadDefaultCheckBoxOptions('practiceAreas');
384
- loadDefaultCheckBoxOptions('city');
385
- _$w('#searchDesktop').enable();
386
- _$w('#searchTabletMobile').enable();
387
- _$w('#zipcode').enable();
388
- _$w('#stateTextInput').enable();
389
- _$w('#searchDesktop').value = '';
390
- _$w('#searchTabletMobile').value = '';
391
- _$w('#zipcode').value = '';
392
- _$w('#stateTextInput').value = '';
393
- _$w('#nearBy').checked = false;
394
-
395
- // Ensure city input is disabled on reset
396
- _$w('#cityTextInput').disable();
397
- filter = JSON.parse(JSON.stringify(DEFAULT_FILTER));
398
- dropDownOptions = JSON.parse(JSON.stringify(DROPDOWN_OPTIONS));
399
- await updateResults('zeroTimeout');
400
- }
401
-
402
- // SEARCH BAR FILTER
403
- async function searchProfilesOnSearchText(event) {
404
- const searchText = event.target.value.trim();
405
- searchText === '' ? (filter.searchText = null) : (filter.searchText = searchText);
406
- if (searchText.length === 0) {
407
- filter[`stateSearch`] = '';
408
- filter[`practiceAreasSearch`] = '';
409
- await updateResults('zeroTimeout');
410
- return;
411
- }
412
-
413
- const timeoutType = 'searchTimeout';
414
- if (debounceTimeout[timeoutType]) {
415
- clearTimeout(debounceTimeout[timeoutType]);
416
- }
417
- searchResults = await search({
418
- filter,
419
- pagination,
420
- debounceTimeout,
421
- timeoutType,
422
- isSearchingNearby: _$w('#nearBy').checked,
423
- });
424
- await updateUrlParams(filter, pagination);
425
- }
426
- // NEAR BY FILTER
427
- async function nearByHandler(preservePagination = false) {
428
- const isSearchingNearby = _$w('#nearBy').checked;
429
- const renderingEnv = await rendering.env();
430
- // 1. Disable nearby input while processing
431
- _$w('#nearBy').disable();
432
-
433
- // 2. Enable/Disable other inputs first
434
- updateFiltersState();
435
-
436
- // 3. Do the query
437
- const { success, filter: newFilter } = await getAndSetUserLocation(isSearchingNearby, filter);
438
- filter = newFilter;
439
- console.log('filter inside nearByHandler', filter);
440
- if (!success) {
441
- if (renderingEnv !== 'backend') {
442
- //on Backend environment, geolocation API don't work, so makes no sense to change state for near by
443
- multiStateBoxSelector.changeState('nearByState');
444
- }
445
- _$w('#nearBy').checked = false;
446
- updateFiltersState();
447
- // 4. Re-enable nearby input
448
- _$w('#nearBy').enable();
449
- return false;
450
- }
451
-
452
- // If location is not selected, change state to "resultsState"
453
- if (!isSearchingNearby) {
454
- if (await noSearchCriteria()) {
455
- multiStateBoxSelector.changeState('noSearchCriteria');
456
- // 4. Re-enable nearby input
457
- _$w('#nearBy').enable();
458
- return;
459
- }
460
- multiStateBoxSelector.changeState('resultsState');
461
- }
462
- await updateResults('zeroTimeout', preservePagination);
463
-
464
- // 4. Re-enable nearby input when done
465
- _$w('#nearBy').enable();
466
- return true;
467
- }
468
-
469
- // STATE, CITY, AREA OF PRACTICES FILTER
470
- // FETCH STATE/CITY/AREAS OF PRACTICE FROM BACKEND ONCE AND STORE IT
471
-
472
- async function fetchFilterData() {
473
- let completeStateList, areasOfPracticesList, stateCityMapList;
474
- try {
475
- const { COMPILED_STATE_LIST, COMPILED_AREAS_OF_PRACTICES, COMPILED_STATE_CITY_MAP } =
476
- await withWarmUpData(
477
- 'getCompiledFiltersOptions',
478
- () => getCompiledFiltersOptions(),
479
- console.log
480
- );
481
- completeStateList = COMPILED_STATE_LIST;
482
- areasOfPracticesList = COMPILED_AREAS_OF_PRACTICES;
483
- stateCityMapList = COMPILED_STATE_CITY_MAP;
484
- } catch (error) {
485
- console.error(
486
- `Failed to get compiled filters list, falling back to non compiled version with error: ${error}`
487
- );
488
- const {
489
- completeStateList: _completeStateList,
490
- areasOfPracticesList: _areasOfPracticesList,
491
- stateCityMapList: _stateCityMapList,
492
- } = await withWarmUpData(
493
- 'getNonCompiledFiltersOptions',
494
- () => getNonCompiledFiltersOptions(),
495
- console.log
496
- );
497
- completeStateList = _completeStateList;
498
- areasOfPracticesList = _areasOfPracticesList;
499
- stateCityMapList = _stateCityMapList;
500
- }
501
- await sidePanelFilterData.set('state', completeStateList);
502
- multiSelectFilter('state');
503
- sidePanelFilterData.set('practiceAreas', areasOfPracticesList);
504
- multiSelectFilter('practiceAreas');
505
- stateCityMap = new Map(Object.entries(stateCityMapList));
506
- multiSelectFilter('city');
507
- sidePanelFilterData
508
- .get('state')
509
- .forEach(state => stateNameCodeMap.set(state.value, state.label));
510
-
511
- // Update filter states after data is loaded
512
- updateFiltersState();
513
- }
514
-
515
- // CONSTRUCT DROPDOWN OPTIONS FOR STATE, CITY, AREA OF PRACTICES
516
-
517
- // LOAD THE CONSTRUCTED OPTIONS TO RESPECTIVE DROPDOWNS
518
-
519
- function loadDefaultCheckBoxOptions(filterName) {
520
- setDefaultDropdownState(filterName, filter);
521
- toggleDropdownFunctionality(filterName, true);
522
- const options = prepareOptionsFunction({
523
- filterName,
524
- sidePanelFilterData,
525
- stateCityMap,
526
- filter,
527
- });
528
- _$w(`#${filterName}CheckBox`).options = options;
529
-
530
- // Update filter states after loading options
531
- updateFiltersState();
532
- return options;
533
- }
534
-
535
- function updateFiltersState() {
536
- const nearByChecked = _$w('#nearBy').checked;
537
- const zipValue = _$w('#zipcode').value?.trim() || null;
538
- const stateCount = _$w('#stateCheckBox').value?.length || 0;
539
-
540
- // if nearBy → disable all and return immediately
541
- if (nearByChecked) {
542
- resetSearch('state');
543
- resetSearch('city');
544
- _$w('#zipcode').value = '';
545
- _$w('#zipcode').disable();
546
- _$w('#stateTextInput').disable();
547
- _$w('#cityTextInput').disable();
548
- return;
549
- }
550
-
551
- // if zip entered → state & city off, zip stays on
552
- if (zipValue) {
553
- resetSearch('state');
554
- resetSearch('city');
555
- _$w('#stateTextInput').disable();
556
- _$w('#cityTextInput').disable();
557
- _$w('#zipcode').enable();
558
- zipValue === '' ? (filter.postalcode = null) : (filter.postalcode = zipValue);
559
- return;
560
- }
561
-
562
- // default: zip + state + searchDesktop on
563
- _$w('#zipcode').enable();
564
- _$w('#stateTextInput').enable();
565
- _$w('#searchDesktop').enable();
566
-
567
- // only enable city if a state is selected
568
- if (stateCount > 0) {
569
- _$w('#cityTextInput').enable();
570
- } else {
571
- _$w('#cityTextInput').disable();
572
- }
573
- }
574
-
575
- // Filters options based on search text
576
-
577
- const filterOptions = (filterName, searchText) => {
578
- const options = prepareOptionsFunction({
579
- filterName,
580
- sidePanelFilterData,
581
- stateCityMap,
582
- filter,
583
- });
584
- if (!searchText || searchText.trim() === '') {
585
- return options;
586
- }
587
- return filterOptionsFunction(filterName, options, searchText.trim());
588
- };
589
- // DYNAMIC FUNCTION FOR INITIALIZING STATE, CITY, AREA OF PRACTICES FILTER
590
- function multiSelectFilter(filterName) {
591
- // Element selectors
592
- const {
593
- checkBoxContainerSelector,
594
- searchTextInputSelector,
595
- clearSearchButtonSelector,
596
- toggleOptionListButtonSelector,
597
- multiCheckBoxSelector,
598
- } = getFiltersSelectors(filterName);
599
-
600
- // Set up event handlers
601
- // Clear search button handler
602
-
603
- clearSearchButtonSelector.onClick(async () => {
604
- searchTextInputSelector.value = undefined;
605
- setDefaultState({
606
- filterName,
607
- withSelectedOptions: false,
608
- filter,
609
- dropDownOptions,
610
- sidePanelFilterData,
611
- stateCityMap,
612
- });
613
- if (filterName === 'state') {
614
- setDefaultCity({
615
- filter,
616
- dropDownOptions,
617
- sidePanelFilterData,
618
- stateCityMap,
619
- });
620
- }
621
- await handleFilterChanged(filterName);
622
- });
623
-
624
- // Toggle dropdown button handler
625
- toggleOptionListButtonSelector.onClick(() => {
626
- // If nearby is checked, don't allow any dropdown to open
627
- if (_$w('#nearBy').checked && filterName !== 'practiceAreas') {
628
- return;
629
- }
630
-
631
- if (filterName === 'city') {
632
- // For city dropdown, ensure we have state selected and city data
633
- if (filter.state.length === 0) {
634
- return; // Don't expand if no state selected
635
- }
636
- // Ensure city data is loaded
637
- const options = prepareOptionsFunction({
638
- filterName,
639
- sidePanelFilterData,
640
- stateCityMap,
641
- filter,
642
- });
643
- if (options.length === 0) {
644
- return; // Don't expand if no city data
645
- }
646
- setDefaultFilterOption({
647
- filterName,
648
- withSelectedOptions: true,
649
- dropDownOptions,
650
- filter,
651
- sidePanelFilterData,
652
- stateCityMap,
653
- });
654
- checkBoxContainerSelector.collapsed
655
- ? checkBoxContainerSelector.expand()
656
- : checkBoxContainerSelector.collapse();
657
- } else if (
658
- filterName === 'practiceAreas' ||
659
- (multiCheckBoxSelector.options.length > 0 &&
660
- !_$w('#nearBy').checked &&
661
- _$w('#zipcode').value?.trim() === '')
662
- ) {
663
- !searchTextInputSelector.value.length &&
664
- setDefaultFilterOption({
665
- filterName,
666
- withSelectedOptions: true,
667
- dropDownOptions,
668
- filter,
669
- sidePanelFilterData,
670
- stateCityMap,
671
- });
672
- checkBoxContainerSelector.collapsed
673
- ? checkBoxContainerSelector.expand()
674
- : checkBoxContainerSelector.collapse();
675
- }
676
- });
677
-
678
- // Search input handler
679
-
680
- searchTextInputSelector.onClick(() => {
681
- handleSearchTextInput(filterName, searchTextInputSelector.value);
682
- });
683
-
684
- searchTextInputSelector.onBlur(() => {
685
- if (dropDownOptions[filterName].displayText) {
686
- clearSearchButtonSelector.expand();
687
- }
688
- });
689
-
690
- searchTextInputSelector.onInput(async event => {
691
- handleSearchTextInput(filterName, event.target.value);
692
- await handleFilterChanged(filterName, true);
693
- });
694
-
695
- // Checkbox selection handler
696
-
697
- multiCheckBoxSelector.onChange(async event => {
698
- await onChangeMultiCheckbox({
699
- filterName,
700
- selectedOptions: event.target.value,
701
- dropDownOptions,
702
- filter,
703
- pagination,
704
- sidePanelFilterData,
705
- stateCityMap,
706
- stateNameCodeMap,
707
- isMobile,
708
- });
709
-
710
- await handleFilterChanged(filterName);
711
- });
712
-
713
- checkBoxContainerSelector.onMouseOut(() => checkBoxContainerSelector.collapse());
714
-
715
- // Initialize with default options
716
- loadDefaultCheckBoxOptions(filterName);
717
- }
718
- function handleSearchTextInput(filterName, input) {
719
- const { checkBoxContainerSelector, clearSearchButtonSelector, multiCheckBoxSelector } =
720
- getFiltersSelectors(filterName);
721
- const tofilterOnValue = !input.includes('selected') ? input : '';
722
- // Toggle clear button visibility
723
- !input || input.length === 0
724
- ? clearSearchButtonSelector.collapse()
725
- : clearSearchButtonSelector.expand();
726
-
727
- // Filter options based on input
728
- const filteredOptions = filterOptions(filterName, tofilterOnValue);
729
- multiCheckBoxSelector.options = filteredOptions;
730
-
731
- // Toggle dropdown list visibility
732
- if (checkBoxContainerSelector.collapsed) {
733
- if (filteredOptions.length > 0) {
734
- checkBoxContainerSelector.expand();
735
- }
736
- }
737
- }
738
- async function handleFilterChanged(filterName, isUserInput = false) {
739
- try {
740
- if (isMobile) {
741
- return;
742
- }
743
-
744
- const { searchTextInputSelector } = getFiltersSelectors(filterName);
745
- // Update results based on selection
746
- filter[`${filterName}Search`] = isUserInput ? searchTextInputSelector.value : '';
747
-
748
- if (filter.searchText && filter.searchText.length > 0) {
749
- searchResults = await search({
750
- filter,
751
- pagination,
752
- debounceTimeout,
753
- timeoutType: 'searchTimeout',
754
- isSearchingNearby: _$w('#nearBy').checked,
755
- });
756
- await updateUrlParams(filter, pagination);
757
- } else {
758
- await updateResults('filterTimeout');
759
- }
760
- } catch (error) {
761
- console.error(`[handleFilterChanged]failed with error: ${error}`);
762
- multiStateBoxSelector.changeState('errorState');
763
- }
764
- }
765
-
766
- function resetSearch(filterName) {
767
- const { searchTextInputSelector, clearSearchButtonSelector, multiCheckBoxSelector } =
768
- getFiltersSelectors(filterName);
769
- clearSearchButtonSelector.collapse();
770
- multiCheckBoxSelector.options = [];
771
- searchTextInputSelector.value = '';
772
-
773
- filter[filterName] = [];
774
- filter[`${filterName}Search`] = '';
775
- }
776
- };
777
- module.exports = {
778
- homePageOnReady,
779
- };