abmp-npm 10.0.70 → 10.0.71

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/index.js CHANGED
@@ -16,5 +16,4 @@ module.exports = {
16
16
  ...require('./dev-only-methods'),
17
17
  ...require('./tasks/migration-methods'),
18
18
  ...require('./tasks/url-migration-methods'),
19
- ...require('./public-profile-methods'),
20
19
  };
@@ -125,5 +125,4 @@ module.exports = {
125
125
  generateSEOTitle,
126
126
  stripHtmlTags,
127
127
  getMemberProfileData,
128
- transformMemberToProfileData,
129
128
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "10.0.70",
3
+ "version": "10.0.71",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
@@ -42,7 +42,6 @@
42
42
  "@wix/secrets": "^1.0.62",
43
43
  "@wix/site-location": "^1.31.0",
44
44
  "@wix/site-members": "^1.32.0",
45
- "@wix/site-seo": "^1.22.0",
46
45
  "@wix/site-storage": "^1.22.0",
47
46
  "@wix/site-window": "^1.44.0",
48
47
  "@wix/urls": "^1.0.57",
package/pages/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  module.exports = {
2
2
  ...require('./ContactUs.js'),
3
3
  ...require('./Profile.js'),
4
- ...require('./publicProfile.js'),
5
4
  ...require('./Home.js'),
6
5
  ...require('./personalDetails.js'),
7
6
  ...require('./QAPage.js'),
@@ -18,6 +18,33 @@ const {
18
18
  const MAX_PHONES_COUNT = 10;
19
19
  const MAX_ADDRESSES_COUNT = 10;
20
20
 
21
+ /** US 10-digit NANP: strip non-digits; drop leading 1 from 11-digit input. */
22
+ function digitsFromPhoneInput(value) {
23
+ if (!value || typeof value !== 'string') return '';
24
+ let d = value.replace(/\D/g, '');
25
+ if (d.length === 11 && d[0] === '1') d = d.slice(1);
26
+ return d.slice(0, 10);
27
+ }
28
+
29
+ /** Format as (###) ###-#### while typing. */
30
+ function formatUsPhoneInput(digits) {
31
+ if (!digits) return '';
32
+ const d = digits.slice(0, 10);
33
+ if (d.length <= 3) return `(${d}`;
34
+ if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
35
+ return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
36
+ }
37
+
38
+ function formatPhoneNumberForInput(value) {
39
+ return formatUsPhoneInput(digitsFromPhoneInput(value || ''));
40
+ }
41
+
42
+ function phoneDigitsEqual(a, b) {
43
+ const da = digitsFromPhoneInput(String(a || ''));
44
+ const db = digitsFromPhoneInput(String(b || ''));
45
+ return da.length > 0 && db.length > 0 && da === db;
46
+ }
47
+
21
48
  const ADDRESS_STATES = {
22
49
  VIEW: 'addressViewState',
23
50
  EDIT: 'addressEditState',
@@ -2024,12 +2051,16 @@ async function personalDetailsOnReady({
2024
2051
  _$w('#phoneInput').onInput(event => {
2025
2052
  const data = _$w('#phoneNumbersList').data;
2026
2053
  const clickedItemData = data.find(item => item._id === event.context.itemId);
2027
- const phoneValue = event.target.value;
2054
+ const digits = digitsFromPhoneInput(event.target.value);
2055
+ const formatted = formatUsPhoneInput(digits);
2056
+ if (event.target.value !== formatted) {
2057
+ event.target.value = formatted;
2058
+ }
2028
2059
 
2029
- updatePhoneNumber(clickedItemData._id, phoneValue);
2060
+ updatePhoneNumber(clickedItemData._id, formatted);
2030
2061
 
2031
- if (clickedItemData.isNewPhone && phoneValue.trim()) {
2032
- addNewPhoneToData(clickedItemData._id, phoneValue.trim());
2062
+ if (clickedItemData.isNewPhone && digits.length > 0) {
2063
+ addNewPhoneToData(clickedItemData._id, formatted);
2033
2064
  clickedItemData.isNewPhone = false;
2034
2065
  }
2035
2066
 
@@ -2098,7 +2129,7 @@ async function personalDetailsOnReady({
2098
2129
  }
2099
2130
 
2100
2131
  function handlePhoneItem($item, itemData) {
2101
- $item('#phoneInput').value = itemData.phoneNumber || '';
2132
+ $item('#phoneInput').value = formatPhoneNumberForInput(itemData.phoneNumber || '');
2102
2133
  $item('#showPhoneCheckbox').checked = itemData.showPhone || false;
2103
2134
  $item('#phoneNumberLabel').text = `Phone ${itemData.phoneIndex}`;
2104
2135
  }
@@ -2112,7 +2143,7 @@ async function personalDetailsOnReady({
2112
2143
  let matched = false;
2113
2144
  return phoneData.map(p => {
2114
2145
  const t = (p.phoneNumber || '').trim();
2115
- const isMatch = Boolean(t && t === cms);
2146
+ const isMatch = Boolean(t && phoneDigitsEqual(t, cms));
2116
2147
  if (isMatch && !matched) {
2117
2148
  matched = true;
2118
2149
  return { ...p, showPhone: true };
@@ -2140,7 +2171,7 @@ async function personalDetailsOnReady({
2140
2171
  phoneData = phones.map((phone, index) => ({
2141
2172
  _id: `phone_${index}`,
2142
2173
  phoneNumber: phone,
2143
- showPhone: phone === itemMemberObj.toShowPhone,
2174
+ showPhone: phoneDigitsEqual(phone, itemMemberObj.toShowPhone),
2144
2175
  isNewPhone: false,
2145
2176
  phoneIndex: index + 1,
2146
2177
  }));
@@ -2195,7 +2226,7 @@ async function personalDetailsOnReady({
2195
2226
  } else if (
2196
2227
  itemMemberObj.toShowPhone &&
2197
2228
  prevTrimmed &&
2198
- itemMemberObj.toShowPhone === prevTrimmed
2229
+ phoneDigitsEqual(itemMemberObj.toShowPhone, prevTrimmed)
2199
2230
  ) {
2200
2231
  itemMemberObj.toShowPhone = newTrimmed || null;
2201
2232
  }
@@ -2230,14 +2261,17 @@ async function personalDetailsOnReady({
2230
2261
  if (phoneToRemove) {
2231
2262
  if (itemMemberObj.phones) {
2232
2263
  itemMemberObj.phones = itemMemberObj.phones.filter(
2233
- phone => phone !== phoneToRemove.phoneNumber
2264
+ phone => !phoneDigitsEqual(phone, phoneToRemove.phoneNumber)
2234
2265
  );
2235
2266
  }
2236
2267
 
2237
2268
  // Clear toShowPhone if it was the removed phone or if it's no longer in the list
2238
2269
  // (handles format mismatch e.g. "(406)655-4940" vs "(406) 655-4940")
2239
2270
  const remainingPhones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
2240
- if (itemMemberObj.toShowPhone && !remainingPhones.includes(itemMemberObj.toShowPhone)) {
2271
+ if (
2272
+ itemMemberObj.toShowPhone &&
2273
+ !remainingPhones.some(p => phoneDigitsEqual(p, itemMemberObj.toShowPhone))
2274
+ ) {
2241
2275
  itemMemberObj.toShowPhone = null;
2242
2276
  }
2243
2277
 
@@ -2262,7 +2296,10 @@ async function personalDetailsOnReady({
2262
2296
  return;
2263
2297
  }
2264
2298
 
2265
- if (itemMemberObj.toShowPhone === trimmed) {
2299
+ if (
2300
+ itemMemberObj.toShowPhone === trimmed ||
2301
+ phoneDigitsEqual(itemMemberObj.toShowPhone, trimmed)
2302
+ ) {
2266
2303
  itemMemberObj.toShowPhone = null;
2267
2304
  }
2268
2305
  }
@@ -2273,7 +2310,7 @@ async function personalDetailsOnReady({
2273
2310
  // Never expose toShowPhone when phones is empty, so save payload clears it in CMS
2274
2311
  if (phones.length === 0) return null;
2275
2312
  // If toShowPhone is not in the list (e.g. format mismatch), treat as none selected
2276
- if (toShow && !phones.includes(toShow)) return null;
2313
+ if (toShow && !phones.some(p => phoneDigitsEqual(p, toShow))) return null;
2277
2314
  return toShow;
2278
2315
  }
2279
2316
 
@@ -2302,9 +2339,6 @@ async function personalDetailsOnReady({
2302
2339
  }
2303
2340
 
2304
2341
  async function saveContactBooking() {
2305
- // if showWixUrl value changes then update optWebsiteCheckbox value
2306
- _$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
2307
-
2308
2342
  const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
2309
2343
  const otherWebsiteValue = (_$w('#UrlInput').value || '').trim();
2310
2344
  const isOtherWebsiteInvalid =
@@ -2345,8 +2379,13 @@ async function personalDetailsOnReady({
2345
2379
  console.groupEnd();
2346
2380
 
2347
2381
  const result = await saveData(formData);
2348
- if (beforeData.showWebsite !== contactChanges.showWebsite) {
2349
- handleOptWebsiteCheckboxEnable(showExistingUrl);
2382
+ if (result.success) {
2383
+ if (beforeData.showWebsite !== contactChanges.showWebsite) {
2384
+ handleOptWebsiteCheckboxEnable(showExistingUrl);
2385
+ }
2386
+ // Sync Personal Details opt-in from saved member.
2387
+ _$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
2388
+ toggleFreeWebsiteText(itemMemberObj.showWixUrl);
2350
2389
  }
2351
2390
  formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section] = false;
2352
2391
  handleSaveDataFeedback(_$w('#contactMessage'), result.message);
package/public/consts.js CHANGED
@@ -5,7 +5,6 @@ const REGEX = {
5
5
 
6
6
  const COLLECTIONS = {
7
7
  MEMBERS_DATA: 'MembersDataLatest',
8
- MEMBERS_DATA_PUBLIC: 'MembersDataPublic',
9
8
  CONTACT_US_SUBMISSIONS: 'contactUsSubmissions',
10
9
  SITE_CONFIGS: 'SiteConfigs',
11
10
  COMPILED_STATE_CITY_MAP: 'CompiledStateCityMap',
@@ -1,54 +0,0 @@
1
- const { COLLECTIONS } = require('../public/consts');
2
-
3
- const { CONFIG_KEYS } = require('./consts');
4
- const { MEMBER_ACTIONS } = require('./daily-pull/consts');
5
- const { wixData } = require('./elevated-modules');
6
- const { findMemberByWixDataId } = require('./members-data-methods');
7
- const { transformMemberToProfileData } = require('./routers/utils');
8
- const { getSiteConfigs } = require('./utils');
9
-
10
- const getPublicMemberRecord = memberDataId =>
11
- wixData
12
- .query(COLLECTIONS.MEMBERS_DATA_PUBLIC)
13
- .eq('memberData', memberDataId)
14
- .limit(1)
15
- .find()
16
- .then(res => res.items?.[0] || null);
17
-
18
- async function getPublicMemberProfileData({ memberDataId }) {
19
- if (!memberDataId) {
20
- return null;
21
- }
22
-
23
- const publicMember = await getPublicMemberRecord(memberDataId);
24
- if (!publicMember || publicMember.showWixUrl !== true) {
25
- return null;
26
- }
27
-
28
- const member = await findMemberByWixDataId(memberDataId);
29
- if (!member || member.action === MEMBER_ACTIONS.DROP || member.showWixUrl !== true) {
30
- return null;
31
- }
32
-
33
- const siteConfigs = await getSiteConfigs();
34
- const siteAssociation = siteConfigs[CONFIG_KEYS.SITE_ASSOCIATION];
35
- const defaultProfileImage = siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_IMAGE];
36
-
37
- const profileData = transformMemberToProfileData(member, siteAssociation);
38
- return {
39
- profileData: { ...profileData, defaultProfileImage },
40
- };
41
- }
42
-
43
- module.exports = {
44
- getPublicMemberProfileData,
45
- async getPublicProfileSeoConfig() {
46
- const siteConfigs = await getSiteConfigs();
47
- return {
48
- siteAssociation: siteConfigs[CONFIG_KEYS.SITE_ASSOCIATION],
49
- defaultSEODescription: siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_SEO_DESCRIPTION],
50
- siteLogoUrl: siteConfigs[CONFIG_KEYS.SITE_LOGO_URL],
51
- defaultProfileImage: siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_IMAGE],
52
- };
53
- },
54
- };
@@ -1,523 +0,0 @@
1
- const { location: wixLocation } = require('@wix/site-location');
2
- const { seo } = require('@wix/site-seo');
3
- const { window: wixWindow } = require('@wix/site-window');
4
-
5
- const { LIGHTBOX_NAMES } = require('../public/consts');
6
- const {
7
- generateId,
8
- formatPracticeAreasForDisplay,
9
- isWixHostedImage,
10
- } = require('../public/Utils/sharedUtils');
11
-
12
- const TESTIMONIALS_PER_PAGE_CONFIG = {
13
- DESKTOP: 4,
14
- TABLET: 2,
15
- MOBILE: 1,
16
- };
17
-
18
- const BREAKPOINTS = {
19
- DESKTOP: 1301,
20
- TABLET: 750,
21
- };
22
-
23
- const resolveMemberDataId = memberData => {
24
- if (!memberData) {
25
- return null;
26
- }
27
- if (typeof memberData === 'string') {
28
- return memberData;
29
- }
30
- return memberData?._id || memberData?._ref || null;
31
- };
32
-
33
- async function publicProfileOnReady({
34
- $w: _$w,
35
- getPublicMemberProfileData,
36
- getPublicProfileSeoConfig,
37
- }) {
38
- const dataset = _$w('#dynamicDataset');
39
- let profileData = null;
40
-
41
- let testimonialsPerPage = TESTIMONIALS_PER_PAGE_CONFIG.TABLET;
42
- let currentTestimonialPage = 0;
43
-
44
- if (typeof getPublicMemberProfileData !== 'function') {
45
- console.error('[publicProfileOnReady] getPublicMemberProfileData is required');
46
- wixLocation.to(`${wixLocation.baseUrl}/404`);
47
- return;
48
- }
49
-
50
- await dataset.onReadyAsync();
51
- const item = dataset.getCurrentItem();
52
-
53
- if (!item || item.showWixUrl !== true) {
54
- wixLocation.to(`${wixLocation.baseUrl}/404`);
55
- return;
56
- }
57
-
58
- const memberDataId = resolveMemberDataId(item.memberData);
59
- if (!memberDataId) {
60
- wixLocation.to(`${wixLocation.baseUrl}/404`);
61
- return;
62
- }
63
-
64
- const result = await getPublicMemberProfileData({ memberDataId });
65
- profileData = result?.profileData || null;
66
-
67
- if (!profileData) {
68
- wixLocation.to(`${wixLocation.baseUrl}/404`);
69
- return;
70
- }
71
-
72
- if (typeof getPublicProfileSeoConfig === 'function') {
73
- try {
74
- const seoConfig = await getPublicProfileSeoConfig();
75
- applySeo(profileData, seoConfig);
76
- } catch (error) {
77
- console.error('[publicProfileOnReady] Failed to set SEO', error);
78
- }
79
- }
80
-
81
- initializePage();
82
-
83
- function initializePage() {
84
- bindProfileData();
85
- setupAddressToggle();
86
- setupResponsiveTestimonials();
87
- }
88
-
89
- // Profile data binding
90
- function bindProfileData() {
91
- bindAddressData();
92
- bindMemberInfo();
93
- bindContactInfo();
94
- bindBusinessInfo();
95
- bindGalleryData();
96
- bindTestimonialsData();
97
- }
98
-
99
- function bindAddressData() {
100
- if (profileData.mainAddress) {
101
- setTextForElements(
102
- ['#LocationText', '#LocationText2', '#LocationText3'],
103
- profileData.mainAddress
104
- );
105
- } else {
106
- deleteElements(['#locationContainer', '#location1Container', '#locationContainer2']);
107
- }
108
-
109
- setupAdditionalAddresses();
110
- }
111
-
112
- function setupAdditionalAddresses() {
113
- _$w('#moreAdressesRepeater').data = profileData.moreAddressesToDisplay;
114
-
115
- if (profileData.moreAddressesToDisplay.length > 0) {
116
- _$w('#moreLocationButton').expand();
117
- _$w('#addressTitle').collapse();
118
- }
119
-
120
- _$w('#moreAdressesRepeater').onItemReady(($item, itemData) => {
121
- console.log('Item Data:', itemData);
122
- $item('#adressText').text = itemData.address || '';
123
- });
124
- }
125
-
126
- function setupAddressToggle() {
127
- toggleContainer('#moreLocationButton', '#addressContainer');
128
- }
129
-
130
- function toggleContainer(buttonId, containerId) {
131
- const $button = _$w(buttonId);
132
- const $container = _$w(containerId);
133
-
134
- $button.onClick(() => {
135
- const isCollapsed = $container.collapsed;
136
- $container[isCollapsed ? 'expand' : 'collapse']();
137
- $button.label = isCollapsed ? 'Less Locations -' : 'More Locations +';
138
- });
139
- }
140
-
141
- function bindMemberInfo() {
142
- bindMemberSince();
143
- bindStudentBadge();
144
- bindProfileImages();
145
- bindFullName();
146
- }
147
-
148
- function bindMemberSince() {
149
- if (profileData.memberSince) {
150
- _$w('#sinceYearText').text = profileData.memberSince;
151
- } else {
152
- _$w('#memberSinceBox').delete();
153
- }
154
- }
155
-
156
- function bindStudentBadge() {
157
- if (profileData.shouldHaveStudentBadge) {
158
- _$w('#studentContainer, #studentContainerMobile').expand();
159
- } else {
160
- _$w('#studentContainer, #studentContainerMobile').delete();
161
- }
162
- }
163
-
164
- function bindProfileImages() {
165
- if (profileData.logoImage) {
166
- _$w('#logoImage').src = profileData.logoImage;
167
- } else {
168
- _$w('#logoImage').delete();
169
- }
170
-
171
- if (profileData.profileImage && isWixHostedImage(profileData.profileImage)) {
172
- _$w('#profileImage').src = profileData.profileImage;
173
- } else {
174
- _$w('#profileImage').src = profileData.defaultProfileImage;
175
- }
176
- }
177
-
178
- function bindFullName() {
179
- if (profileData.fullName) {
180
- setTextForElements(
181
- ['#fullNameText', '#fullNameText2', '#fullNameTextFoter'],
182
- profileData.fullName
183
- );
184
- } else {
185
- deleteElements(['#fullNameText', '#fullNameText2', '#fullNameTextFoter']);
186
- }
187
- }
188
-
189
- // Contact information binding
190
- function bindContactInfo() {
191
- bindContactForm();
192
- bindBookingUrl();
193
- bindPhoneNumber();
194
- bindLicenseNumber();
195
- }
196
-
197
- function bindContactForm() {
198
- if (profileData.showContactForm) {
199
- _$w('#contactButton').onClick(() =>
200
- wixWindow.openLightbox(LIGHTBOX_NAMES.CONTACT_US, profileData)
201
- );
202
- } else {
203
- _$w('#contactButton').delete();
204
- }
205
- }
206
-
207
- function bindBookingUrl() {
208
- if (profileData.bookingUrl) {
209
- _$w('#bookNowButton').link = profileData.bookingUrl;
210
- } else {
211
- _$w('#bookNowButton').delete();
212
- }
213
- }
214
-
215
- function bindPhoneNumber() {
216
- if (profileData.phone) {
217
- const formattedPhoneNumber = profileData.phone.replace(/[^\d+]/g, '');
218
- const getPhoneHTML = $phoneSelector =>
219
- $phoneSelector.html.replace(
220
- $phoneSelector.text,
221
- `<a href="${`tel:${formattedPhoneNumber}`}">${profileData.phone}</a>`
222
- );
223
- _$w('#phoneText').html = getPhoneHTML(_$w('#phoneText'));
224
- _$w('#phoneText2').html = getPhoneHTML(_$w('#phoneText2'));
225
- } else {
226
- deleteElements(['#phoneContainer', '#phoneContainer2']);
227
- }
228
- }
229
-
230
- function bindLicenseNumber() {
231
- if (profileData.licenceNo) {
232
- _$w('#licenceNoText').text = profileData.licenceNo;
233
- } else {
234
- _$w('#licensesContainer').delete();
235
- }
236
- }
237
-
238
- function bindBusinessInfo() {
239
- bindAboutService();
240
- bindBusinessName();
241
- bindAreasOfPractice();
242
- }
243
-
244
- function bindAboutService() {
245
- if (profileData.aboutService) {
246
- _$w('#aboutYouText').html = profileData.aboutService;
247
- } else {
248
- _$w('#aboutSection').delete();
249
- }
250
- }
251
-
252
- function bindBusinessName() {
253
- if (profileData.businessName) {
254
- _$w('#businessName').text = profileData.businessName;
255
- _$w('#businessName').expand();
256
- } else {
257
- _$w('#businessName').delete();
258
- }
259
- }
260
-
261
- function bindAreasOfPractice() {
262
- const areasText = formatPracticeAreasForDisplay(profileData.areasOfPractices);
263
-
264
- if (areasText) {
265
- _$w('#areaOfPracticesText').text = areasText;
266
- } else {
267
- _$w('#areaOfPracticesText').delete();
268
- }
269
-
270
- if (Array.isArray(profileData.areasOfPractices) && profileData.areasOfPractices.length > 0) {
271
- populateRepeater(profileData.areasOfPractices, '#areaOfPracticesRepeater', '#practiceText');
272
- } else {
273
- _$w('#servicesSection').delete();
274
- }
275
- }
276
-
277
- function bindGalleryData() {
278
- if (profileData.bannerImages && profileData.bannerImages.length > 0) {
279
- _$w('#bannerImage').src = profileData.bannerImages[0];
280
- }
281
-
282
- if (!profileData.gallery?.length) {
283
- _$w('#gallerySection').delete();
284
- } else {
285
- _$w('#gallery').items = profileData.gallery;
286
- _$w('#gallerySection').restore();
287
- }
288
- }
289
-
290
- function bindTestimonialsData() {
291
- if (!profileData.testimonials?.length) {
292
- _$w('#testimonialsSection').delete();
293
- }
294
- }
295
-
296
- // Responsive testimonials setup
297
- async function setupResponsiveTestimonials() {
298
- const { window } = await wixWindow.getBoundingRect();
299
- testimonialsPerPage = getTestimonialsPerPage(window.width);
300
-
301
- // Monitor window resize
302
- setInterval(async () => {
303
- const { window: win } = await wixWindow.getBoundingRect();
304
- const newTestimonialsPerPage = getTestimonialsPerPage(win.width);
305
-
306
- if (newTestimonialsPerPage !== testimonialsPerPage) {
307
- testimonialsPerPage = newTestimonialsPerPage;
308
- currentTestimonialPage = 0;
309
- displayTestimonialsPage(profileData.testimonials);
310
- }
311
- }, 500);
312
-
313
- setupTestimonialsIfAvailable();
314
- }
315
-
316
- function setupTestimonialsIfAvailable() {
317
- if (profileData.testimonials.length > 0) {
318
- setupTestimonialsPagination(profileData.testimonials);
319
- _$w('#testimonialsSection').expand();
320
- } else {
321
- _$w('#testimonialsSection').delete();
322
- }
323
- }
324
-
325
- function getTestimonialsPerPage(width) {
326
- if (width >= BREAKPOINTS.DESKTOP) return TESTIMONIALS_PER_PAGE_CONFIG.DESKTOP;
327
- if (width >= BREAKPOINTS.TABLET) return TESTIMONIALS_PER_PAGE_CONFIG.TABLET;
328
- return TESTIMONIALS_PER_PAGE_CONFIG.MOBILE;
329
- }
330
-
331
- function setTextForElements(elementIds, text) {
332
- elementIds.forEach(id => {
333
- _$w(id).text = text;
334
- });
335
- }
336
-
337
- function deleteElements(elementIds) {
338
- elementIds.forEach(id => {
339
- _$w(id).delete();
340
- });
341
- }
342
-
343
- function populateRepeater(data, repeaterId, textElementId) {
344
- const repeaterData = data.map(item => ({
345
- _id: generateId(),
346
- text: item.trim(),
347
- }));
348
- _$w(repeaterId).data = repeaterData;
349
- _$w(repeaterId).onItemReady(($item, itemData) => {
350
- $item(textElementId).text = itemData.text;
351
- });
352
- }
353
-
354
- // Testimonials pagination
355
- function setupTestimonialsPagination(allTestimonials) {
356
- currentTestimonialPage = 0;
357
-
358
- _$w('#prevTestimonialBtn').onClick(() => {
359
- if (currentTestimonialPage > 0) {
360
- currentTestimonialPage--;
361
- displayTestimonialsPage(allTestimonials);
362
- }
363
- });
364
-
365
- _$w('#nextTestimonialBtn').onClick(() => {
366
- const maxPage = Math.floor((allTestimonials.length - 1) / testimonialsPerPage);
367
- if (currentTestimonialPage < maxPage) {
368
- currentTestimonialPage++;
369
- displayTestimonialsPage(allTestimonials);
370
- }
371
- });
372
-
373
- displayTestimonialsPage(allTestimonials);
374
- }
375
-
376
- function displayTestimonialsPage(allTestimonials) {
377
- const start = currentTestimonialPage * testimonialsPerPage;
378
- const end = start + testimonialsPerPage;
379
- const currentBatch = allTestimonials.slice(start, end);
380
-
381
- populateRepeater(currentBatch, '#testimonialsrepeater', '#testimonialText');
382
- updateTestimonialNavigation(end, allTestimonials.length);
383
- }
384
-
385
- function updateTestimonialNavigation(end, totalLength) {
386
- _$w('#prevTestimonialBtn').hide();
387
- _$w('#nextTestimonialBtn').hide();
388
-
389
- if (currentTestimonialPage > 0) {
390
- _$w('#prevTestimonialBtn').show();
391
- }
392
-
393
- if (end < totalLength) {
394
- _$w('#nextTestimonialBtn').show();
395
- }
396
- }
397
-
398
- function applySeo(data, config) {
399
- if (!data || !config) {
400
- return;
401
- }
402
- const { siteAssociation, defaultSEODescription, siteLogoUrl, defaultProfileImage } = config;
403
-
404
- const profileImage =
405
- data.profileImage?.trim() && isWixHostedImage(data.profileImage)
406
- ? data.profileImage
407
- : defaultProfileImage;
408
- const ogImage = profileImage || data.logoImage || siteLogoUrl;
409
- const seoTitle = generateSEOTitle({
410
- fullName: data.fullName,
411
- areasOfPractices: data.areasOfPractices,
412
- siteAssociation,
413
- });
414
- let description = stripHtmlTags(data.aboutService) || defaultSEODescription || '';
415
- if (description.length > 160) {
416
- description = description.substring(0, 157) + '...';
417
- }
418
- const profileUrl = data.url
419
- ? `${wixLocation.baseUrl}/profile/${data.url}`
420
- : wixLocation.baseUrl;
421
- const shouldNoIndex = data.isPrivateMember || data.shouldHaveStudentBadge;
422
-
423
- seo.setTitle(seoTitle);
424
- seo.setDescription(description);
425
- seo.setMetaTags(
426
- [
427
- {
428
- name: 'description',
429
- content: description,
430
- },
431
- {
432
- name: 'keywords',
433
- content:
434
- `${data.fullName}, ${data.areasOfPractices ? data.areasOfPractices.slice(0, 3).join(', ') : ''}, ${siteAssociation}, ${data.city || ''}, ${data.state || ''}`
435
- .replace(/,\s*,/g, ',')
436
- .replace(/^,|,$/g, ''),
437
- },
438
- {
439
- name: 'author',
440
- content: data.fullName,
441
- },
442
- {
443
- name: 'robots',
444
- content: shouldNoIndex ? 'noindex, nofollow' : 'index, follow',
445
- },
446
- {
447
- property: 'og:type',
448
- content: 'profile',
449
- },
450
- {
451
- property: 'og:title',
452
- content: seoTitle,
453
- },
454
- {
455
- property: 'og:description',
456
- content: description,
457
- },
458
- {
459
- property: 'og:image',
460
- content: ogImage,
461
- },
462
- {
463
- property: 'og:url',
464
- content: profileUrl,
465
- },
466
- {
467
- property: 'og:site_name',
468
- content: `${siteAssociation} Members`,
469
- },
470
- {
471
- name: 'twitter:card',
472
- content: 'summary_large_image',
473
- },
474
- {
475
- name: 'twitter:title',
476
- content: seoTitle,
477
- },
478
- {
479
- name: 'twitter:description',
480
- content: description,
481
- },
482
- {
483
- name: 'twitter:image',
484
- content: ogImage,
485
- },
486
- {
487
- name: 'geo.region',
488
- content: data.state || '',
489
- },
490
- {
491
- name: 'geo.placename',
492
- content: data.city || '',
493
- },
494
- ].filter(tag => tag.content && tag.content.trim() !== '')
495
- );
496
- }
497
-
498
- function generateSEOTitle({ fullName, areasOfPractices, siteAssociation }) {
499
- return `${fullName}${
500
- areasOfPractices && areasOfPractices.length > 0
501
- ? ` | ${areasOfPractices.slice(0, 3).join(', ')}`
502
- : ''
503
- } | ${siteAssociation} Member`;
504
- }
505
-
506
- function stripHtmlTags(html) {
507
- if (!html) return '';
508
- return html
509
- .replace(/<[^>]*>/g, '')
510
- .replace(/&nbsp;/g, ' ')
511
- .replace(/&amp;/g, '&')
512
- .replace(/&lt;/g, '<')
513
- .replace(/&gt;/g, '>')
514
- .replace(/&quot;/g, '"')
515
- .replace(/&#39;/g, "'")
516
- .replace(/\s+/g, ' ')
517
- .trim();
518
- }
519
- }
520
-
521
- module.exports = {
522
- publicProfileOnReady,
523
- };