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