abmp-npm 2.0.61 → 2.0.63

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.61",
3
+ "version": "2.0.63",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
package/pages/Home.js CHANGED
@@ -10,6 +10,7 @@ const {
10
10
  formatPracticeAreasForDisplay,
11
11
  checkAddressIsVisible,
12
12
  isWixHostedImage,
13
+ normalizeExternalUrl,
13
14
  } = require('../public/Utils/sharedUtils.js');
14
15
 
15
16
  let filter = JSON.parse(JSON.stringify(DEFAULT_FILTER));
@@ -198,7 +199,7 @@ const homePageOnReady = async ({
198
199
  $item('#websiteContainer').collapse();
199
200
  } else {
200
201
  if (itemData.showWebsite) {
201
- $item('#website').link = itemData.website;
202
+ $item('#website').link = normalizeExternalUrl(itemData.website);
202
203
  } else {
203
204
  $item('#website').link = `${baseUrl}/profile/${itemData.url}`;
204
205
  }
@@ -256,7 +257,7 @@ const homePageOnReady = async ({
256
257
  // 9) "Book now" button
257
258
  if (itemData.bookingUrl) {
258
259
  $item('#bookNowButton').show();
259
- $item('#bookNowButton').link = itemData.bookingUrl;
260
+ $item('#bookNowButton').link = normalizeExternalUrl(itemData.bookingUrl);
260
261
  $item('#bookNowButton').target = '_blank';
261
262
  } else {
262
263
  $item('#bookNowButton').hide();
@@ -9,7 +9,11 @@ const {
9
9
  LIGHTBOX_NAMES,
10
10
  } = require('../public/consts');
11
11
  const { handleOnCustomValidation, isNotValidUrl } = require('../public/Utils/personalDetailsUtils');
12
- const { generateId, isWixHostedImage } = require('../public/Utils/sharedUtils');
12
+ const {
13
+ generateId,
14
+ isWixHostedImage,
15
+ normalizeExternalUrl,
16
+ } = require('../public/Utils/sharedUtils');
13
17
 
14
18
  const MAX_PHONES_COUNT = 10;
15
19
  const MAX_ADDRESSES_COUNT = 10;
@@ -2039,9 +2043,14 @@ async function personalDetailsOnReady({
2039
2043
  return;
2040
2044
  }
2041
2045
 
2046
+ const $item = _$w.at(event.context);
2042
2047
  const isChecked = event.target.checked;
2043
2048
  let updated;
2044
2049
  if (isChecked) {
2050
+ // Same pattern as #mainAddressCheckbox: clear every row, then select one (repeater
2051
+ // often does not refresh sibling items' controls when only `data` changes).
2052
+ _$w('#showPhoneCheckbox').checked = false;
2053
+ $item('#showPhoneCheckbox').checked = true;
2045
2054
  updated = data.map(item =>
2046
2055
  item._id === clickedItemData._id
2047
2056
  ? { ...item, showPhone: true }
@@ -2094,6 +2103,34 @@ async function personalDetailsOnReady({
2094
2103
  $item('#phoneNumberLabel').text = `Phone ${itemData.phoneIndex}`;
2095
2104
  }
2096
2105
 
2106
+ /** Only one "Show your phone" row at a time; aligns UI with itemMemberObj.toShowPhone. */
2107
+ function normalizePhoneShowFlags(phoneData) {
2108
+ if (!phoneData?.length) return phoneData;
2109
+
2110
+ const cms = (itemMemberObj.toShowPhone || '').trim();
2111
+ if (cms) {
2112
+ let matched = false;
2113
+ return phoneData.map(p => {
2114
+ const t = (p.phoneNumber || '').trim();
2115
+ const isMatch = Boolean(t && t === cms);
2116
+ if (isMatch && !matched) {
2117
+ matched = true;
2118
+ return { ...p, showPhone: true };
2119
+ }
2120
+ return { ...p, showPhone: false };
2121
+ });
2122
+ }
2123
+
2124
+ const trueIdxs = phoneData.map((p, i) => (p.showPhone ? i : -1)).filter(i => i >= 0);
2125
+ if (trueIdxs.length <= 1) return phoneData;
2126
+
2127
+ const keep = trueIdxs[0];
2128
+ return phoneData.map((p, i) => ({
2129
+ ...p,
2130
+ showPhone: i === keep,
2131
+ }));
2132
+ }
2133
+
2097
2134
  function renderPhonesList(updatedPhones) {
2098
2135
  let phoneData = updatedPhones || [];
2099
2136
 
@@ -2109,12 +2146,28 @@ async function personalDetailsOnReady({
2109
2146
  }));
2110
2147
  }
2111
2148
 
2149
+ phoneData = normalizePhoneShowFlags(phoneData);
2150
+
2112
2151
  const repeater = _$w('#phoneNumbersList');
2113
2152
 
2114
2153
  repeater.data = phoneData;
2154
+ refreshPhoneShowCheckboxState(phoneData);
2115
2155
  updatePhoneAddButtonState();
2116
2156
  }
2117
2157
 
2158
+ /**
2159
+ * Repeater may not re-run onItemReady for sibling items when only showPhone flags change;
2160
+ * keep each #showPhoneCheckbox in sync with data (same idea as refreshAddressListIsMainState).
2161
+ */
2162
+ function refreshPhoneShowCheckboxState(phoneData) {
2163
+ const repeater = _$w('#phoneNumbersList');
2164
+ const data = phoneData || repeater.data || [];
2165
+ repeater.forEachItem(($item, _itemData, index) => {
2166
+ const show = index < data.length ? data[index].showPhone : false;
2167
+ $item('#showPhoneCheckbox').checked = Boolean(show);
2168
+ });
2169
+ }
2170
+
2118
2171
  function updatePhoneAddButtonState() {
2119
2172
  const currentData = _$w('#phoneNumbersList').data || [];
2120
2173
  const addPhoneButton = _$w('#addPhoneButton');
@@ -2131,7 +2184,22 @@ async function personalDetailsOnReady({
2131
2184
  const itemIndex = currentData.findIndex(item => item._id === phoneId);
2132
2185
 
2133
2186
  if (itemIndex !== -1) {
2134
- currentData[itemIndex].phoneNumber = newPhoneNumber;
2187
+ const item = currentData[itemIndex];
2188
+ const prevTrimmed = (item.phoneNumber || '').trim();
2189
+ const newTrimmed = newPhoneNumber.trim();
2190
+
2191
+ item.phoneNumber = newPhoneNumber;
2192
+
2193
+ if (item.showPhone) {
2194
+ itemMemberObj.toShowPhone = newTrimmed || null;
2195
+ } else if (
2196
+ itemMemberObj.toShowPhone &&
2197
+ prevTrimmed &&
2198
+ itemMemberObj.toShowPhone === prevTrimmed
2199
+ ) {
2200
+ itemMemberObj.toShowPhone = newTrimmed || null;
2201
+ }
2202
+
2135
2203
  renderPhonesList(currentData);
2136
2204
  syncPhonesFromRepeater();
2137
2205
  checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
@@ -2187,14 +2255,14 @@ async function personalDetailsOnReady({
2187
2255
  return;
2188
2256
  }
2189
2257
 
2258
+ const trimmed = (selectedItem.phoneNumber || '').trim();
2259
+
2190
2260
  if (isVisible) {
2191
- itemMemberObj.toShowPhone = selectedItem.phoneNumber?.trim()
2192
- ? selectedItem.phoneNumber
2193
- : null;
2261
+ itemMemberObj.toShowPhone = trimmed || null;
2194
2262
  return;
2195
2263
  }
2196
2264
 
2197
- if (selectedItem.phoneNumber) {
2265
+ if (itemMemberObj.toShowPhone === trimmed) {
2198
2266
  itemMemberObj.toShowPhone = null;
2199
2267
  }
2200
2268
  }
@@ -2216,12 +2284,15 @@ async function personalDetailsOnReady({
2216
2284
  const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
2217
2285
  const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
2218
2286
 
2287
+ const rawWebsite = (_$w('#UrlInput').value || '').trim();
2288
+ const rawBooking = (_$w('#schedulingLinkInput').value || '').trim();
2289
+
2219
2290
  return {
2220
2291
  showContactForm: _$w('#showCotactFormCheckbox').checked,
2221
2292
  contactFormEmail: _$w('#contactFormEmailInput').value,
2222
2293
  toShowPhone: getToShowPhone(),
2223
- bookingUrl: _$w('#schedulingLinkInput').value,
2224
- website: _$w('#UrlInput').value,
2294
+ bookingUrl: rawBooking ? normalizeExternalUrl(rawBooking) : '',
2295
+ website: rawWebsite ? normalizeExternalUrl(rawWebsite) : '',
2225
2296
  showWebsite: showExistingUrl,
2226
2297
  showWixUrl,
2227
2298
  addresses,
@@ -5,37 +5,7 @@ const { DEFAULT_FILTER } = require('../consts.js');
5
5
 
6
6
  const { debouncedFunction } = require('./sharedUtils.js');
7
7
 
8
- /** Returned when an in-flight search was superseded; do not apply results to UI */
9
- const STALE_SEARCH_RESPONSE = Symbol('STALE_SEARCH_RESPONSE');
10
-
11
- function generateSearchId() {
12
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
13
- const r = (Math.random() * 16) | 0;
14
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
15
- return v.toString(16);
16
- });
17
- }
18
-
19
8
  const createHomepageUtils = (_$w, filterProfiles) => {
20
- let currentSearchId = null;
21
-
22
- async function fetchProfilesWithSearchId(args) {
23
- const thisSearchId = generateSearchId();
24
- currentSearchId = thisSearchId;
25
- try {
26
- const response = await filterProfiles(args);
27
- if (thisSearchId !== currentSearchId) {
28
- return STALE_SEARCH_RESPONSE;
29
- }
30
- return response;
31
- } catch (error) {
32
- if (thisSearchId !== currentSearchId) {
33
- return STALE_SEARCH_RESPONSE;
34
- }
35
- throw error;
36
- }
37
- }
38
-
39
9
  const getFiltersSelectors = filterName => ({
40
10
  checkBoxContainerSelector: _$w(`#${filterName}CheckBoxContainer`),
41
11
  searchTextInputSelector: _$w(`#${filterName}TextInput`),
@@ -717,10 +687,7 @@ const createHomepageUtils = (_$w, filterProfiles) => {
717
687
  }
718
688
  const nonDebouncedFilterProfiles = async () => {
719
689
  try {
720
- const result = await fetchProfilesWithSearchId({ filter, isSearchingNearby });
721
- if (result === STALE_SEARCH_RESPONSE) {
722
- return { success: true, response: STALE_SEARCH_RESPONSE };
723
- }
690
+ const result = await filterProfiles({ filter, isSearchingNearby });
724
691
  return { success: true, response: result };
725
692
  } catch (error) {
726
693
  return { success: false, error };
@@ -732,15 +699,12 @@ const createHomepageUtils = (_$w, filterProfiles) => {
732
699
  ? () => nonDebouncedFilterProfiles()
733
700
  : () =>
734
701
  debouncedFunction({
735
- func: fetchProfilesWithSearchId,
702
+ func: filterProfiles,
736
703
  debounceTimeout,
737
704
  timeoutType,
738
705
  args: { filter, isSearchingNearby },
739
706
  });
740
707
  const { success, response, error } = await funcPromise();
741
- if (response === STALE_SEARCH_RESPONSE) {
742
- return [];
743
- }
744
708
  if (!success) {
745
709
  _$w('#numberOfResults').text = '';
746
710
  console.error('[search] failed with error:', error);
@@ -186,6 +186,15 @@ function isWixHostedImage(imageUrl) {
186
186
  );
187
187
  }
188
188
 
189
+ /** Web URLs only: bare hostnames get https:// so they are not treated as site-relative paths. */
190
+ function normalizeExternalUrl(url) {
191
+ if (!url || typeof url !== 'string') return url;
192
+ const trimmed = url.trim();
193
+ if (!trimmed) return trimmed;
194
+ if (/^https?:\/\//i.test(trimmed)) return trimmed;
195
+ return `https://${trimmed}`;
196
+ }
197
+
189
198
  module.exports = {
190
199
  checkAddressIsVisible,
191
200
  formatPracticeAreasForDisplay,
@@ -198,4 +207,5 @@ module.exports = {
198
207
  generateId,
199
208
  formatAddress,
200
209
  isWixHostedImage,
210
+ normalizeExternalUrl,
201
211
  };