abmp-npm 1.7.3 → 1.8.0

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.
@@ -0,0 +1,129 @@
1
+ const { lightbox } = require('@wix/site-window');
2
+ const { phone } = require('phone');
3
+
4
+ const { VALIDATION_MESSAGES, REGEX } = require('../public');
5
+
6
+ async function contactUsOnReady({ $w: _$w, contactSubmission }) {
7
+ _$w('#submitButton').disable();
8
+ const receivedData = await lightbox.getContext();
9
+ const formFieldsSelectors = ['#firstName', '#lastName', '#email', '#phone', '#message'];
10
+ const inputOnCustomValidation = ({ value, reject, message, pattern }) => {
11
+ const isValid = typeof value === 'string' && pattern.test(value.trim());
12
+ if (!isValid) reject(message);
13
+ };
14
+ async function validateAllFields() {
15
+ let allValid = true;
16
+ for (const selector of formFieldsSelectors) {
17
+ const isValid = await _$w(selector).valid;
18
+ _$w(selector).updateValidityIndication();
19
+ if (!isValid) {
20
+ allValid = false;
21
+ }
22
+ }
23
+ return allValid;
24
+ }
25
+
26
+ function resetForm() {
27
+ formFieldsSelectors.forEach(selector => {
28
+ const field = _$w(selector);
29
+ if (field && field.reset) {
30
+ field.reset();
31
+ } else {
32
+ field.value = '';
33
+ }
34
+ if (field && field.resetValidityIndication) {
35
+ field.resetValidityIndication();
36
+ }
37
+ });
38
+ }
39
+
40
+ function setAlertMessage(message) {
41
+ const $message = _$w('#successMessage');
42
+ $message.text = message;
43
+ $message.expand();
44
+
45
+ setTimeout(() => {
46
+ $message.collapse();
47
+ }, 8000);
48
+ }
49
+ // First Name
50
+ _$w('#firstName').onCustomValidation((value, reject) => {
51
+ inputOnCustomValidation({
52
+ value,
53
+ reject,
54
+ message: VALIDATION_MESSAGES.CONTACT_US.FIRST_NAME,
55
+ pattern: REGEX.NAME,
56
+ });
57
+ });
58
+ // Last Name
59
+ _$w('#lastName').onCustomValidation((value, reject) => {
60
+ inputOnCustomValidation({
61
+ value,
62
+ reject,
63
+ message: VALIDATION_MESSAGES.CONTACT_US.LAST_NAME,
64
+ pattern: REGEX.NAME,
65
+ });
66
+ });
67
+ // Message
68
+ _$w('#message').onCustomValidation((value, reject) => {
69
+ inputOnCustomValidation({
70
+ value,
71
+ reject,
72
+ message: VALIDATION_MESSAGES.CONTACT_US.MESSAGE,
73
+ pattern: REGEX.MESSAGE,
74
+ });
75
+ });
76
+
77
+ // Email validation uses native input validation
78
+ // No custom validation needed as email input has built-in validation
79
+
80
+ // Phone (US format)
81
+ _$w('#phone').onCustomValidation((value, reject) => {
82
+ const { isValid } = phone(value, { country: 'US' });
83
+ if (!isValid) {
84
+ reject(VALIDATION_MESSAGES.CONTACT_US.PHONE);
85
+ }
86
+ });
87
+ _$w('#captchaInput').onVerified(async () => {
88
+ const allValid = await validateAllFields();
89
+ if (allValid) {
90
+ _$w('#submitButton').enable();
91
+ return;
92
+ }
93
+ _$w('#submitButton').disable();
94
+ setAlertMessage(VALIDATION_MESSAGES.CONTACT_US.CAPTCHA);
95
+ _$w('#captchaInput').reset();
96
+ });
97
+
98
+ _$w('#captchaInput').onTimeout(() => {
99
+ _$w('#submitButton').disable();
100
+ });
101
+ _$w('#submitButton').onClick(async () => {
102
+ const allValid = await validateAllFields();
103
+ if (!allValid) {
104
+ setAlertMessage(VALIDATION_MESSAGES.CONTACT_US.INVALID_FIELDS);
105
+ return;
106
+ }
107
+
108
+ try {
109
+ const formData = {
110
+ firstName: _$w('#firstName').value,
111
+ lastName: _$w('#lastName').value,
112
+ email: _$w('#email').value,
113
+ phone: _$w('#phone').value,
114
+ message: _$w('#message').value,
115
+ };
116
+ await contactSubmission(formData, receivedData._id);
117
+ await resetForm();
118
+
119
+ setAlertMessage(VALIDATION_MESSAGES.CONTACT_US.SUBMISSION_SUCCESS);
120
+ } catch (error) {
121
+ console.error('Submission failed:', error);
122
+ setAlertMessage(VALIDATION_MESSAGES.CONTACT_US.SUBMISSION_FAILED);
123
+ }
124
+ });
125
+ }
126
+
127
+ module.exports = {
128
+ contactUsOnReady,
129
+ };
@@ -0,0 +1,351 @@
1
+ const TESTIMONIALS_PER_PAGE_CONFIG = {
2
+ DESKTOP: 4,
3
+ TABLET: 2,
4
+ MOBILE: 1,
5
+ };
6
+
7
+ const BREAKPOINTS = {
8
+ DESKTOP: 1301,
9
+ TABLET: 750,
10
+ };
11
+
12
+ async function profileOnReady({
13
+ $w: _$w,
14
+ profileData,
15
+ openLightbox,
16
+ getBoundingRect,
17
+ wixLocation,
18
+ generateId,
19
+ prepareText,
20
+ }) {
21
+ let testimonialsPerPage = TESTIMONIALS_PER_PAGE_CONFIG.TABLET;
22
+ let currentTestimonialPage = 0;
23
+
24
+ console.log('profileData', profileData);
25
+
26
+ if (!profileData) {
27
+ wixLocation.to(`${wixLocation.baseUrl}/404`);
28
+ return;
29
+ }
30
+
31
+ initializePage();
32
+
33
+ function initializePage() {
34
+ bindProfileData();
35
+ setupAddressToggle();
36
+ setupResponsiveTestimonials();
37
+ }
38
+
39
+ // Profile data binding
40
+ function bindProfileData() {
41
+ bindAddressData();
42
+ bindMemberInfo();
43
+ bindContactInfo();
44
+ bindBusinessInfo();
45
+ bindGalleryData();
46
+ bindTestimonialsData();
47
+ }
48
+
49
+ function bindAddressData() {
50
+ if (profileData.mainAddress) {
51
+ setTextForElements(
52
+ ['#LocationText', '#LocationText2', '#LocationText3'],
53
+ profileData.mainAddress
54
+ );
55
+ } else {
56
+ collapseElements(['#locationContainer', '#location1Container', '#locationContainer2']);
57
+ }
58
+
59
+ setupAdditionalAddresses();
60
+ }
61
+
62
+ function setupAdditionalAddresses() {
63
+ _$w('#moreAdressesRepeater').data = profileData.processedAddresses;
64
+
65
+ if (profileData.processedAddresses.length > 0) {
66
+ _$w('#moreLocationButton').expand();
67
+ _$w('#addressTitle').collapse();
68
+ }
69
+
70
+ _$w('#moreAdressesRepeater').onItemReady(($item, itemData) => {
71
+ console.log('Item Data:', itemData);
72
+ $item('#adressText').text = itemData.address || '';
73
+ });
74
+ }
75
+
76
+ function setupAddressToggle() {
77
+ toggleContainer('#moreLocationButton', '#addressContainer');
78
+ }
79
+
80
+ function toggleContainer(buttonId, containerId) {
81
+ const $button = _$w(buttonId);
82
+ const $container = _$w(containerId);
83
+
84
+ $button.onClick(() => {
85
+ const isCollapsed = $container.collapsed;
86
+ $container[isCollapsed ? 'expand' : 'collapse']();
87
+ $button.label = isCollapsed ? 'Less Locations -' : 'More Locations +';
88
+ });
89
+ }
90
+
91
+ function bindMemberInfo() {
92
+ bindMemberSince();
93
+ bindStudentBadge();
94
+ bindProfileImages();
95
+ bindFullName();
96
+ }
97
+
98
+ function bindMemberSince() {
99
+ if (profileData.memberSince) {
100
+ _$w('#sinceYearText').text = profileData.memberSince;
101
+ } else {
102
+ _$w('#memberSinceBox').collapse();
103
+ }
104
+ }
105
+
106
+ function bindStudentBadge() {
107
+ if (profileData.shouldHaveStudentBadge) {
108
+ _$w('#studentContainer, #studentContainerMobile').expand();
109
+ } else {
110
+ _$w('#studentContainer, #studentContainerMobile').collapse();
111
+ }
112
+ }
113
+
114
+ function bindProfileImages() {
115
+ if (profileData.logoImage) {
116
+ _$w('#logoImage').src = profileData.logoImage;
117
+ } else {
118
+ _$w('#logoImage').collapse();
119
+ }
120
+
121
+ if (profileData.profileImage) {
122
+ _$w('#profileImage').src = profileData.profileImage;
123
+ } else {
124
+ _$w('#profileImage').src =
125
+ 'https://static.wixstatic.com/media/1d7134_e052e9b1d0a543d0980650e16dd6d374~mv2.jpg';
126
+ }
127
+ }
128
+
129
+ function bindFullName() {
130
+ if (profileData.fullName) {
131
+ setTextForElements(
132
+ ['#fullNameText', '#fullNameText2', '#fullNameTextFoter'],
133
+ profileData.fullName
134
+ );
135
+ } else {
136
+ collapseElements(['#fullNameText', '#fullNameText2', '#fullNameTextFoter']);
137
+ }
138
+ }
139
+
140
+ // Contact information binding
141
+ function bindContactInfo() {
142
+ bindContactForm();
143
+ bindBookingUrl();
144
+ bindPhoneNumber();
145
+ bindLicenseNumber();
146
+ }
147
+
148
+ function bindContactForm() {
149
+ if (profileData.showContactForm) {
150
+ _$w('#contactButton').onClick(() => openLightbox('Contact Us', profileData));
151
+ } else {
152
+ _$w('#contactButton').collapse();
153
+ }
154
+ }
155
+
156
+ function bindBookingUrl() {
157
+ if (profileData.bookingUrl) {
158
+ _$w('#bookNowButton').link = profileData.bookingUrl;
159
+ } else {
160
+ _$w('#bookNowButton').collapse();
161
+ }
162
+ }
163
+
164
+ function bindPhoneNumber() {
165
+ if (profileData.phone) {
166
+ const formattedPhoneNumber = profileData.phone.replace(/[^\d+]/g, '');
167
+ const getPhoneHTML = ($phoneSelector) =>
168
+ $phoneSelector.html.replace(
169
+ $phoneSelector.text,
170
+ `<a href="${`tel:${formattedPhoneNumber}`}">${profileData.phone}</a>`
171
+ );
172
+ _$w('#phoneText').html = getPhoneHTML(_$w('#phoneText'));
173
+ _$w('#phoneText2').html = getPhoneHTML(_$w('#phoneText2'));
174
+ } else {
175
+ collapseElements(['#phoneContainer', '#phoneContainer2']);
176
+ }
177
+ }
178
+
179
+ function bindLicenseNumber() {
180
+ if (profileData.licenceNo) {
181
+ _$w('#licenceNoText').text = profileData.licenceNo;
182
+ } else {
183
+ _$w('#licensesContainer').collapse();
184
+ }
185
+ }
186
+
187
+ function bindBusinessInfo() {
188
+ bindAboutService();
189
+ bindBusinessName();
190
+ bindAreasOfPractice();
191
+ }
192
+
193
+ function bindAboutService() {
194
+ if (profileData.aboutService) {
195
+ _$w('#aboutYouText').html = profileData.aboutService;
196
+ } else {
197
+ _$w('#aboutSection').collapse();
198
+ }
199
+ }
200
+
201
+ function bindBusinessName() {
202
+ if (profileData.businessName) {
203
+ _$w('#businessName').text = profileData.businessName;
204
+ _$w('#businessName').expand();
205
+ } else {
206
+ _$w('#businessName').collapse();
207
+ }
208
+ }
209
+
210
+ function bindAreasOfPractice() {
211
+ const areasText = prepareText(profileData.areasOfPractices);
212
+
213
+ if (areasText) {
214
+ _$w('#areaOfPracticesText').text = areasText;
215
+ } else {
216
+ _$w('#areaOfPracticesText').collapse();
217
+ }
218
+
219
+ if (Array.isArray(profileData.areasOfPractices) && profileData.areasOfPractices.length > 0) {
220
+ populateRepeater(profileData.areasOfPractices, '#areaOfPracticesRepeater', '#practiceText');
221
+ } else {
222
+ _$w('#servicesSection').collapse();
223
+ }
224
+ }
225
+
226
+ function bindGalleryData() {
227
+ if (profileData.bannerImages && profileData.bannerImages.length > 0) {
228
+ _$w('#bannerImage').src = profileData.bannerImages[0];
229
+ }
230
+
231
+ if (!profileData.gallery?.length) {
232
+ _$w('#gallerySection').collapse();
233
+ } else {
234
+ _$w('#gallery').items = profileData.gallery;
235
+ _$w('#gallerySection').expand();
236
+ }
237
+ }
238
+
239
+ function bindTestimonialsData() {
240
+ if (!profileData.testimonials?.length) {
241
+ _$w('#testimonialsSection').collapse();
242
+ }
243
+ }
244
+
245
+ // Responsive testimonials setup
246
+ async function setupResponsiveTestimonials() {
247
+ const { window } = await getBoundingRect();
248
+ testimonialsPerPage = getTestimonialsPerPage(window.width);
249
+
250
+ // Monitor window resize
251
+ setInterval(async () => {
252
+ const { window: win } = await getBoundingRect();
253
+ const newTestimonialsPerPage = getTestimonialsPerPage(win.width);
254
+
255
+ if (newTestimonialsPerPage !== testimonialsPerPage) {
256
+ testimonialsPerPage = newTestimonialsPerPage;
257
+ currentTestimonialPage = 0;
258
+ displayTestimonialsPage(profileData.testimonials);
259
+ }
260
+ }, 500);
261
+
262
+ setupTestimonialsIfAvailable();
263
+ }
264
+
265
+ function setupTestimonialsIfAvailable() {
266
+ if (profileData.testimonials.length > 0) {
267
+ setupTestimonialsPagination(profileData.testimonials);
268
+ _$w('#testimonialsSection').expand();
269
+ } else {
270
+ _$w('#testimonialsSection').collapse();
271
+ }
272
+ }
273
+
274
+ function getTestimonialsPerPage(width) {
275
+ if (width >= BREAKPOINTS.DESKTOP) return TESTIMONIALS_PER_PAGE_CONFIG.DESKTOP;
276
+ if (width >= BREAKPOINTS.TABLET) return TESTIMONIALS_PER_PAGE_CONFIG.TABLET;
277
+ return TESTIMONIALS_PER_PAGE_CONFIG.MOBILE;
278
+ }
279
+
280
+ function setTextForElements(elementIds, text) {
281
+ elementIds.forEach((id) => {
282
+ _$w(id).text = text;
283
+ });
284
+ }
285
+
286
+ function collapseElements(elementIds) {
287
+ elementIds.forEach((id) => {
288
+ _$w(id).collapse();
289
+ });
290
+ }
291
+
292
+ function populateRepeater(data, repeaterId, textElementId) {
293
+ const repeaterData = data.map((item) => ({
294
+ _id: generateId(),
295
+ text: item.trim(),
296
+ }));
297
+ _$w(repeaterId).data = repeaterData;
298
+ _$w(repeaterId).onItemReady(($item, itemData) => {
299
+ $item(textElementId).text = itemData.text;
300
+ });
301
+ }
302
+
303
+ // Testimonials pagination
304
+ function setupTestimonialsPagination(allTestimonials) {
305
+ currentTestimonialPage = 0;
306
+
307
+ _$w('#prevTestimonialBtn').onClick(() => {
308
+ if (currentTestimonialPage > 0) {
309
+ currentTestimonialPage--;
310
+ displayTestimonialsPage(allTestimonials);
311
+ }
312
+ });
313
+
314
+ _$w('#nextTestimonialBtn').onClick(() => {
315
+ const maxPage = Math.floor((allTestimonials.length - 1) / testimonialsPerPage);
316
+ if (currentTestimonialPage < maxPage) {
317
+ currentTestimonialPage++;
318
+ displayTestimonialsPage(allTestimonials);
319
+ }
320
+ });
321
+
322
+ displayTestimonialsPage(allTestimonials);
323
+ }
324
+
325
+ function displayTestimonialsPage(allTestimonials) {
326
+ const start = currentTestimonialPage * testimonialsPerPage;
327
+ const end = start + testimonialsPerPage;
328
+ const currentBatch = allTestimonials.slice(start, end);
329
+
330
+ populateRepeater(currentBatch, '#testimonialsrepeater', '#testimonialText');
331
+ updateTestimonialNavigation(end, allTestimonials.length);
332
+ }
333
+
334
+ function updateTestimonialNavigation(end, totalLength) {
335
+ _$w('#prevTestimonialBtn').hide();
336
+ _$w('#nextTestimonialBtn').hide();
337
+
338
+ if (currentTestimonialPage > 0) {
339
+ _$w('#prevTestimonialBtn').show();
340
+ }
341
+
342
+ if (end < totalLength) {
343
+ _$w('#nextTestimonialBtn').show();
344
+ }
345
+ }
346
+ }
347
+
348
+ module.exports = {
349
+ profileOnReady,
350
+ };
351
+
package/pages/index.js ADDED
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ ...require('./ContactUs.js'),
3
+ ...require('./Profile.js'),
4
+ };
@@ -0,0 +1,15 @@
1
+ const REGEX = {
2
+ NAME: /^[a-zA-Z\s'-]{2,}$/,
3
+ MESSAGE: /^[A-Za-z0-9\s.,!?'"-]{2,}$/,
4
+ };
5
+
6
+ const COLLECTIONS = {
7
+ MEMBERS_DATA: 'MembersDataLatest',
8
+ CONTACT_US_SUBMISSIONS: 'contactUsSubmissions',
9
+ SITE_CONFIGS: 'SiteConfigs',
10
+ };
11
+
12
+ module.exports = {
13
+ REGEX,
14
+ COLLECTIONS,
15
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ ...require('./consts'),
3
+ ...require('./messages'),
4
+ ...require('./utils'),
5
+ };
@@ -0,0 +1,16 @@
1
+ const VALIDATION_MESSAGES = {
2
+ CONTACT_US: {
3
+ FIRST_NAME: 'Please enter a valid first name.',
4
+ LAST_NAME: 'Please enter a valid last name.',
5
+ MESSAGE: 'Please enter a valid message.',
6
+ PHONE: 'Please enter a valid US phone number.',
7
+ CAPTCHA: 'Please fix the highlighted fields before captcha verification.',
8
+ INVALID_FIELDS: 'Please fix the highlighted fields before submitting.',
9
+ SUBMISSION_FAILED: 'An error occurred. Please try again.',
10
+ SUBMISSION_SUCCESS: 'Contact submitted successfully',
11
+ },
12
+ };
13
+
14
+ module.exports = {
15
+ VALIDATION_MESSAGES,
16
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Generate a unique ID
3
+ * @returns {string} Unique identifier
4
+ */
5
+ function generateId() {
6
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
7
+ }
8
+
9
+ /**
10
+ * Formats an array of practice areas, showing as many as fit within 70 characters
11
+ * @param {Array} areaOfPractices - Array of practice area strings
12
+ * @returns {string} Formatted string of practice areas
13
+ */
14
+ function prepareText(areaOfPractices = []) {
15
+ // always return a string
16
+ if (!Array.isArray(areaOfPractices) || areaOfPractices.length === 0) {
17
+ return '';
18
+ }
19
+
20
+ // Filter out null/undefined/empty
21
+ const validAreas = areaOfPractices.filter(
22
+ (area) => area !== null && area !== undefined && area !== ''
23
+ );
24
+
25
+ if (validAreas.length === 0) {
26
+ return '';
27
+ }
28
+
29
+ if (validAreas.length === 1) {
30
+ return validAreas[0].length > 70 ? validAreas[0].substring(0, 67) + '...' : validAreas[0];
31
+ }
32
+
33
+ // build up to 70-char string
34
+ let current = '';
35
+ const visible = [];
36
+ for (const item of validAreas) {
37
+ const sep = visible.length ? ', ' : '';
38
+ const next = current + sep + item;
39
+ if (next.length > 70) break;
40
+ visible.push(item);
41
+ current = next;
42
+ }
43
+
44
+ // if nothing fit, at least show the first (truncated)
45
+ if (visible.length === 0) {
46
+ const first = validAreas[0];
47
+ return first.length > 67 ? first.substring(0, 67) + '...' : first;
48
+ }
49
+
50
+ const remaining = validAreas.length - visible.length;
51
+ return remaining > 0 ? `${visible.join(', ')}, +${remaining} Techniques` : visible.join(', ');
52
+ }
53
+
54
+ module.exports = {
55
+ generateId,
56
+ prepareText,
57
+ };
58
+