abmp-npm 2.0.44 → 2.0.46

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
@@ -68,6 +68,20 @@ async function scheduleCreateContactsFromMembersTask() {
68
68
  }
69
69
  }
70
70
 
71
+ async function scheduleFixPrimaryAddressForMembersTask() {
72
+ try {
73
+ console.log('scheduleFixPrimaryAddressForMembers started!');
74
+ return await taskManager().schedule({
75
+ name: TASKS_NAMES.scheduleFixPrimaryAddressForMembers,
76
+ data: {},
77
+ type: 'scheduled',
78
+ });
79
+ } catch (error) {
80
+ console.error(`Failed to scheduleFixPrimaryAddressForMembers: ${error.message}`);
81
+ throw new Error(`Failed to scheduleFixPrimaryAddressForMembers: ${error.message}`);
82
+ }
83
+ }
84
+
71
85
  async function updateSiteMapS3() {
72
86
  try {
73
87
  return await taskManager().schedule({
@@ -85,4 +99,5 @@ module.exports = {
85
99
  scheduleDailyPullTask,
86
100
  updateSiteMapS3,
87
101
  scheduleCreateContactsFromMembersTask,
102
+ scheduleFixPrimaryAddressForMembersTask,
88
103
  };
@@ -410,6 +410,7 @@ async function getMembersWithWixUrl() {
410
410
  .eq('showWixUrl', true)
411
411
  .ne('action', MEMBER_ACTIONS.DROP)
412
412
  .ne('memberships.membertype', MEMBERSHIPS_TYPES.PAC_STAFF)
413
+ .ne('memberships.membertype', MEMBERSHIPS_TYPES.STUDENT)
413
414
  .isNotEmpty('url')
414
415
  .limit(1000);
415
416
  let currentResults = await membersQuery.find();
@@ -47,10 +47,12 @@ const createRoutersHandlers = wixRouterMethods => {
47
47
  }
48
48
  const profileUrl = `${request.baseUrl}/${PAGES_PATHS.PROFILE}/${profileData.url}`;
49
49
  const isPrivateMember = profileData.isPrivateMember;
50
+ const isStudent = profileData.shouldHaveStudentBadge;
51
+ const shouldNoIndex = isPrivateMember || isStudent;
50
52
  const seoData = {
51
53
  title: seoTitle,
52
54
  description: description,
53
- noIndex: isPrivateMember,
55
+ noIndex: shouldNoIndex,
54
56
  metaTags: [
55
57
  {
56
58
  name: 'description',
@@ -69,7 +71,7 @@ const createRoutersHandlers = wixRouterMethods => {
69
71
  },
70
72
  {
71
73
  name: 'robots',
72
- content: isPrivateMember ? 'noindex, nofollow' : 'index, follow',
74
+ content: shouldNoIndex ? 'noindex, nofollow' : 'index, follow',
73
75
  },
74
76
  // Open Graph tags
75
77
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.44",
3
+ "version": "2.0.46",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
package/pages/Home.js CHANGED
@@ -366,7 +366,7 @@ const homePageOnReady = async ({
366
366
  isSearchingNearby: _$w('#nearBy').checked,
367
367
  preservePagination,
368
368
  });
369
- !preservePagination && (await updateUrlParams(filter, pagination));
369
+ // URL is updated inside search() with the filter actually used (avoids rapid select/deselect overwriting with live filter)
370
370
  return searchResults;
371
371
  }
372
372
 
@@ -1,6 +1,8 @@
1
+ const { location: wixLocationFrontend } = require('@wix/site-location');
2
+ const { local } = require('@wix/site-storage');
1
3
  const { window: wixWindow, rendering } = require('@wix/site-window');
2
4
 
3
- const { LIGHTBOX_NAMES } = require('../public/consts');
5
+ const { LIGHTBOX_NAMES, PAGES_PATHS } = require('../public/consts');
4
6
  const { checkAndLogin } = require('../public/sso-auth-methods');
5
7
 
6
8
  async function loadingPageOnReady(authenticateSSOToken) {
@@ -8,9 +10,16 @@ async function loadingPageOnReady(authenticateSSOToken) {
8
10
  //This calls needs to triggered on client side, otherwise PAC API will return 401 error
9
11
  if (renderingEnv === 'browser') {
10
12
  //Need to pass authenticateSSOToken to checkAndLogin so it will run as a web method not a public one.
11
- await checkAndLogin(authenticateSSOToken).catch(error => {
12
- wixWindow.openLightbox(LIGHTBOX_NAMES.LOGIN_ERROR_ALERT);
13
+ await checkAndLogin(authenticateSSOToken).catch(async error => {
13
14
  console.error(`Something went wrong while logging in: ${error}`);
15
+ // If we already have a session (memberId), redirect to form instead of showing error.
16
+ const storedMemberId = await local.getItem('memberId');
17
+ if (storedMemberId) {
18
+ const redirectTo = `${PAGES_PATHS.MEMBERS_FORM}?token=${encodeURIComponent(storedMemberId)}`;
19
+ await wixLocationFrontend.to(`/${redirectTo}`);
20
+ return;
21
+ }
22
+ wixWindow.openLightbox(LIGHTBOX_NAMES.LOGIN_ERROR_ALERT);
14
23
  });
15
24
  }
16
25
  }
@@ -1556,9 +1556,23 @@ async function personalDetailsOnReady({
1556
1556
  _$w('#mainAddressCheckbox').checked = false;
1557
1557
  checkbox.checked = true;
1558
1558
 
1559
+ // Primary address cannot be hidden: update model and repeater data so UI and save stay in sync
1559
1560
  if (clickedItemData.address.addressStatus === ADDRESS_STATUS_TYPES.DONT_SHOW) {
1560
1561
  updateAddressStatus(clickedItemData._id, ADDRESS_STATUS_TYPES.STATE_CITY_ZIP);
1561
1562
  $item('#addressStatusOptions').value = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
1563
+ const currentData = _$w('#addressesList').data || [];
1564
+ const dataWithStatusFixed = currentData.map(item =>
1565
+ item._id === clickedItemData._id
1566
+ ? {
1567
+ ...item,
1568
+ address: {
1569
+ ...item.address,
1570
+ addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
1571
+ },
1572
+ }
1573
+ : item
1574
+ );
1575
+ _$w('#addressesList').data = dataWithStatusFixed;
1562
1576
  }
1563
1577
 
1564
1578
  updateMainAddressSelection(clickedItemData._id);
@@ -1567,7 +1581,7 @@ async function personalDetailsOnReady({
1567
1581
  });
1568
1582
 
1569
1583
  _$w('#addressStatusOptions').onChange(event => {
1570
- const data = _$w('#addressesList').data;
1584
+ const data = _$w('#addressesList').data || [];
1571
1585
  const clickedItemData = data.find(item => item._id === event.context.itemId);
1572
1586
  const newStatus = event.target.value;
1573
1587
  const $item = _$w.at(event.context);
@@ -1579,6 +1593,15 @@ async function personalDetailsOnReady({
1579
1593
  }
1580
1594
 
1581
1595
  updateAddressStatus(clickedItemData._id, newStatus);
1596
+
1597
+ // Keep repeater data in sync (required for new addresses not yet in itemMemberObj.addresses)
1598
+ const updatedData = data.map(item =>
1599
+ item._id === clickedItemData._id
1600
+ ? { ...item, address: { ...item.address, addressStatus: newStatus } }
1601
+ : item
1602
+ );
1603
+ _$w('#addressesList').data = updatedData;
1604
+
1582
1605
  checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1583
1606
  });
1584
1607
 
@@ -1,11 +1,24 @@
1
1
  const { location: wixLocation, queryParams: wixQueryParams } = require('@wix/site-location');
2
2
  const { window: wixWindow, rendering } = require('@wix/site-window');
3
3
 
4
- const { DEFAULT_FILTER } = require('../consts.js');
4
+ const { DEFAULT_FILTER, DEBOUNCE_DELAY } = require('../consts.js');
5
5
 
6
- const { debouncedFunction } = require('./sharedUtils.js');
6
+ function generateSearchId() {
7
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
8
+ const r = (Math.random() * 16) | 0;
9
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
10
+ return v.toString(16);
11
+ });
12
+ }
7
13
 
8
14
  const createHomepageUtils = (_$w, filterProfiles) => {
15
+ let currentSearchId = null;
16
+ let searchDebounceTimer = null;
17
+ let lastSearchFilter = null;
18
+ let lastSearchIsSearchingNearby = false;
19
+ let lastSearchPreservePagination = false;
20
+ let pendingSearchResolve = null;
21
+
9
22
  const getFiltersSelectors = filterName => ({
10
23
  checkBoxContainerSelector: _$w(`#${filterName}CheckBoxContainer`),
11
24
  searchTextInputSelector: _$w(`#${filterName}TextInput`),
@@ -655,13 +668,14 @@ const createHomepageUtils = (_$w, filterProfiles) => {
655
668
  async function search({
656
669
  filter,
657
670
  pagination,
658
- debounceTimeout,
671
+ debounceTimeout: _debounceTimeout,
659
672
  timeoutType,
660
673
  isSearchingNearby,
661
674
  preservePagination = false,
662
675
  }) {
663
676
  const multiStateBoxSelector = _$w('#resultsStateBox');
664
677
  const renderingEnv = await rendering.env();
678
+
665
679
  const initSearchResultsUI = () => {
666
680
  JSON.stringify(filter) === JSON.stringify(DEFAULT_FILTER)
667
681
  ? _$w('#resetFilter').hide()
@@ -672,70 +686,115 @@ const createHomepageUtils = (_$w, filterProfiles) => {
672
686
  _$w('#profileRepeater').data = [];
673
687
  console.log({ filter });
674
688
  };
675
- const runSearchAndUpdateUI = async (filter, isSearchingNearby) => {
676
- if (!isSearchingNearby) {
689
+
690
+ const runSearchAndUpdateUI = async (
691
+ filterToUse,
692
+ isSearchingNearbyToUse,
693
+ preservePaginationToUse
694
+ ) => {
695
+ if (!isSearchingNearbyToUse) {
677
696
  if (
678
697
  JSON.stringify({
679
- ...filter,
698
+ ...filterToUse,
680
699
  latitude: 0,
681
700
  longitude: 0,
682
701
  }) === JSON.stringify(DEFAULT_FILTER)
683
702
  ) {
684
703
  multiStateBoxSelector.changeState('noSearchCriteria');
704
+ await updateUrlParams(filterToUse, pagination);
685
705
  return [];
686
706
  }
687
707
  }
688
- const nonDebouncedFilterProfiles = async () => {
689
- try {
690
- const result = await filterProfiles({ filter, isSearchingNearby });
691
- return { success: true, response: result };
692
- } catch (error) {
693
- return { success: false, error };
694
- }
695
- };
696
- //Don't run setTimeout on SSR
697
- const funcPromise =
698
- renderingEnv === 'backend'
699
- ? () => nonDebouncedFilterProfiles()
700
- : () =>
701
- debouncedFunction({
702
- func: filterProfiles,
703
- debounceTimeout,
704
- timeoutType,
705
- args: { filter, isSearchingNearby },
706
- });
707
- const { success, response, error } = await funcPromise();
708
- if (!success) {
708
+ const thisSearchId = generateSearchId();
709
+ currentSearchId = thisSearchId;
710
+
711
+ let result;
712
+ try {
713
+ result = await filterProfiles({
714
+ filter: filterToUse,
715
+ isSearchingNearby: isSearchingNearbyToUse,
716
+ });
717
+ } catch (error) {
718
+ if (thisSearchId !== currentSearchId) return [];
709
719
  _$w('#numberOfResults').text = '';
710
720
  console.error('[search] failed with error:', error);
711
721
  multiStateBoxSelector.changeState('errorState');
722
+ await updateUrlParams(filterToUse, pagination);
712
723
  return [];
713
724
  }
725
+
726
+ if (thisSearchId !== currentSearchId) return [];
727
+
728
+ const response = result;
714
729
  const totalCount = response.items.length;
715
730
  if (!totalCount) {
716
731
  _$w('#numberOfResults').text = 'Showing 0 results';
717
732
  _$w('#noResultsMessage').text = `${
718
- filter.searchText && filter.searchText.length > 0
719
- ? `'${filter.searchText}' did not match any search. Please try again.`
733
+ filterToUse.searchText && filterToUse.searchText.length > 0
734
+ ? `'${filterToUse.searchText}' did not match any search. Please try again.`
720
735
  : 'No results found for the selected filters. Please adjust your filters and try again'
721
736
  }`;
722
737
  multiStateBoxSelector.changeState('noResultsState');
738
+ await updateUrlParams(filterToUse, pagination);
723
739
  return [];
724
740
  }
725
741
  console.log({ response });
726
742
  handleNumberOfResults(pagination, totalCount);
727
743
  _$w('#showingResult').show();
728
744
 
729
- if (!preservePagination || pagination.currentPage >= pagination.totalPages) {
745
+ if (!preservePaginationToUse || pagination.currentPage >= pagination.totalPages) {
730
746
  pagination.currentPage = 0;
731
747
  }
732
748
  pagination.totalPages = Math.ceil(totalCount / pagination.pageSize);
733
749
  paginateSearchResults(response.items, pagination);
734
750
  multiStateBoxSelector.changeState('resultsState');
751
+ await updateUrlParams(filterToUse, pagination);
735
752
  return response.items;
736
753
  };
754
+
755
+ // Always show loading as soon as user changes input
737
756
  initSearchResultsUI();
738
- return await runSearchAndUpdateUI(filter, isSearchingNearby);
757
+
758
+ // SSR: run immediately, no debounce
759
+ if (renderingEnv === 'backend') {
760
+ return await runSearchAndUpdateUI(filter, isSearchingNearby, preservePagination);
761
+ }
762
+
763
+ // Client: debounce the API call; loading is already shown above.
764
+ // Snapshot the filter so rapid clicks / URL sync can't mutate it before the debounced run.
765
+ lastSearchFilter = JSON.parse(JSON.stringify(filter));
766
+ lastSearchIsSearchingNearby = isSearchingNearby;
767
+ lastSearchPreservePagination = preservePagination;
768
+
769
+ if (pendingSearchResolve) {
770
+ pendingSearchResolve([]);
771
+ pendingSearchResolve = null;
772
+ }
773
+
774
+ if (searchDebounceTimer) {
775
+ clearTimeout(searchDebounceTimer);
776
+ searchDebounceTimer = null;
777
+ }
778
+
779
+ const delay = DEBOUNCE_DELAY[timeoutType] ?? 300;
780
+ return new Promise(resolve => {
781
+ pendingSearchResolve = resolve;
782
+ searchDebounceTimer = setTimeout(async () => {
783
+ searchDebounceTimer = null;
784
+ const filterToUse = lastSearchFilter;
785
+ const isSearchingNearbyToUse = lastSearchIsSearchingNearby;
786
+ const preservePaginationToUse = lastSearchPreservePagination;
787
+ const items = await runSearchAndUpdateUI(
788
+ filterToUse,
789
+ isSearchingNearbyToUse,
790
+ preservePaginationToUse
791
+ );
792
+ if (pendingSearchResolve) {
793
+ pendingSearchResolve(items);
794
+ pendingSearchResolve = null;
795
+ }
796
+ }, delay);
797
+ });
739
798
  }
740
799
 
741
800
  return {
@@ -87,9 +87,11 @@ function findMainAddress(addressDisplayOption = [], addresses = []) {
87
87
  return '';
88
88
  }
89
89
  function formatAddress(item) {
90
+ if (!item) return '';
90
91
  let addressParts = [];
91
- const limitedPostalCode = item.postalcode.slice(0, 5); //show only 5 digits to not show full user address
92
- switch (item.addressStatus) {
92
+ const limitedPostalCode = (item.postalcode && String(item.postalcode).slice(0, 5)) || ''; //show only 5 digits to not show full user address
93
+ const status = item.addressStatus;
94
+ switch (status) {
93
95
  case ADDRESS_STATUS_TYPES.FULL_ADDRESS:
94
96
  addressParts = [item.line1, item.line2, item.city, item.state, limitedPostalCode];
95
97
  break;
@@ -97,7 +99,10 @@ function formatAddress(item) {
97
99
  addressParts = [item.city, item.state, limitedPostalCode];
98
100
  break;
99
101
  default:
100
- return '';
102
+ if (status === ADDRESS_STATUS_TYPES.DONT_SHOW) return '';
103
+ // Legacy addresses may have no addressStatus; show city/state/zip by default
104
+ addressParts = [item.city, item.state, limitedPostalCode];
105
+ break;
101
106
  }
102
107
  return addressParts.filter(Boolean).join(', ');
103
108
  }