abmp-npm 1.1.51 → 1.6.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,1989 @@
1
+ const { location: wixLocation } = require('@wix/site-location');
2
+ const { storage: wixStorage } = require('@wix/site-storage');
3
+ const { window: wixWindow } = require('@wix/site-window');
4
+
5
+ const { generateId } = require('../public/Utils/sharedUtils');
6
+
7
+ const MAX_PHONES_COUNT = 10;
8
+ const MAX_ADDRESSES_COUNT = 10;
9
+
10
+ const ADDRESS_STATES = {
11
+ VIEW: 'addressViewState',
12
+ EDIT: 'addressEditState',
13
+ };
14
+
15
+ const TESTIMONIAL_STATES = {
16
+ VIEW: 'testimonialState',
17
+ ADD: 'addTestimonialState',
18
+ };
19
+
20
+ const GALLERY_STATES = {
21
+ VIEW: 'imageState',
22
+ ADD: 'addImageState',
23
+ };
24
+
25
+ const MAIN_STATE_BOX_STATES = {
26
+ FORM_STATE: 'formState',
27
+ UNAUTHORIZED_STATE: 'unauthorizedState',
28
+ ERROR_STATE: 'errorState',
29
+ };
30
+
31
+ const FORM_SECTION_HANDLER_MAP = {
32
+ PERSONAL: { section: 'personal', handler: null }, // will be set in init
33
+ BUSINESS_SERVICES: { section: 'businessServices', handler: null },
34
+ CONTACT_BOOKING: { section: 'contactBooking', handler: null },
35
+ DIRECTORY_OPT_OUT: { section: 'directoryOptOut', handler: null },
36
+ WEBSITE_OPT_OUT: { section: 'websiteOptOut', handler: null },
37
+ };
38
+
39
+ const SLUG_FLAGS = {
40
+ VALID: '#validSlugFlag',
41
+ INVALID: '#invalidSlugFlag',
42
+ };
43
+
44
+ const SLUG_MESSAGES = {
45
+ INVALID_FORMAT: 'Enter a valid URL. You can use letters, numbers or dashes.',
46
+ TAKEN: 'Enter a new URL slug. This one is already taken.',
47
+ ERROR: 'There was an error. Please try again.',
48
+ };
49
+
50
+ async function personalDetailsFormOnReady({
51
+ $w,
52
+ _,
53
+ getInterestAll,
54
+ saveRegistrationData,
55
+ validateMemberToken,
56
+ checkUrlUniqueness,
57
+ ADDRESS_STATUS_TYPES,
58
+ DEFAULT_BUSINESS_NAME_TEXT,
59
+ FREE_WEBSITE_TEXT_STATES,
60
+ LIGHTBOX_NAMES,
61
+ ABMP_MEMBERS_HOME_URL,
62
+ handleOnCustomValidation,
63
+ isNotValidUrl,
64
+ }) {
65
+ let itemMemberObj = {};
66
+ let originalMemberData = {};
67
+ let selectedServices = [];
68
+ let uploadedImages = {
69
+ profileImage: '',
70
+ logoImage: '',
71
+ bannerImage: '',
72
+ };
73
+
74
+ const FALLBACK_ADDRESS_STATUS = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
75
+
76
+ let formHasUnsavedChanges = {
77
+ [FORM_SECTION_HANDLER_MAP.PERSONAL.section]: false,
78
+ [FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: false,
79
+ [FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: false,
80
+ [FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.section]: false,
81
+ [FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.section]: false,
82
+ };
83
+
84
+ let slugValidationTimeout = {};
85
+ let isSlugValid = true;
86
+ let currentSlugValidationId = 0;
87
+ let memberData, isValid, isStudent;
88
+
89
+ // Set up handler references after functions are defined
90
+ const setupHandlerReferences = () => {
91
+ FORM_SECTION_HANDLER_MAP.PERSONAL.handler = () => checkPersonalDataChanged();
92
+ FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.handler = () => checkBusinessDataChanged();
93
+ FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.handler = () => checkContactDataChanged();
94
+ FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.handler = () => checkDirectoryOptOutDataChanged();
95
+ FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.handler = () => checkWebsiteOptOutDataChanged();
96
+ };
97
+
98
+ const showUnauthorizedState = () => {
99
+ console.log('❌ Unauthorized');
100
+ $w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.UNAUTHORIZED_STATE);
101
+ };
102
+
103
+ // Main initialization
104
+ const memberTokenId = wixLocation.query.token;
105
+ console.log('memberTokenId', memberTokenId);
106
+ if (!memberTokenId) {
107
+ showUnauthorizedState();
108
+ return;
109
+ }
110
+ try {
111
+ const {
112
+ memberData: { isStudent: _isStudent, ...memberDataResponse }, //excluding student from member data so it won't be saved on form save, as this is a runtime calculation flag
113
+ isValid: isValidResponse,
114
+ } = await validateMemberToken(memberTokenId);
115
+ memberData = memberDataResponse;
116
+ isValid = isValidResponse;
117
+ isStudent = _isStudent;
118
+ } catch (error) {
119
+ console.error(`Error in validateMemberToken memberTokenId : ${memberTokenId}`, error);
120
+ $w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.ERROR_STATE);
121
+ return;
122
+ }
123
+ console.log('memberData frontend', memberData);
124
+ if (!isValid) {
125
+ showUnauthorizedState();
126
+ return;
127
+ }
128
+
129
+ console.log('✅ Authorized 2', { memberTokenId });
130
+ $w('#loginButton2').hide();
131
+ $w('#goBackButton').show();
132
+
133
+ $w('#goBackButton').onClick(async () => {
134
+ try {
135
+ const isFormHasUnsavedChanges = Object.values(formHasUnsavedChanges).some(Boolean);
136
+ if (isFormHasUnsavedChanges) {
137
+ wixWindow.openLightbox(LIGHTBOX_NAMES.SAVE_ALERT);
138
+ } else {
139
+ wixLocation.to(ABMP_MEMBERS_HOME_URL);
140
+ }
141
+ } catch (error) {
142
+ console.error('Logout failed:', error);
143
+ }
144
+ });
145
+
146
+ itemMemberObj = memberData;
147
+ originalMemberData = JSON.parse(JSON.stringify(memberData));
148
+ // Initialize selectedServices based on memberData
149
+ selectedServices = Array.isArray(itemMemberObj.areasOfPractices)
150
+ ? itemMemberObj.areasOfPractices.map((label) => ({ _id: generateId(), label: String(label) }))
151
+ : [];
152
+
153
+ $w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.FORM_STATE);
154
+
155
+ setupHandlerReferences();
156
+ init();
157
+ setupStepTrackingWrapper();
158
+ //initially disable save buttons
159
+ $w('#savePersonalButton').disable();
160
+ $w('#saveBusinessButton').disable();
161
+ $w('#saveContactBookingButton').disable();
162
+ onFormDataChanged();
163
+
164
+ function setupStepTrackingWrapper() {
165
+ const stepPairs = [
166
+ {
167
+ mark: '#personalDetailsMark',
168
+ sing: '#personalDetailsSign',
169
+ step: '#personalDetailsStep',
170
+ text: '#personalDetailsText',
171
+ css: 'personal',
172
+ vector: '#personalDetailsVector',
173
+ },
174
+ {
175
+ mark: '#businessServicesMark',
176
+ sing: '#businessServicesSign',
177
+ step: '#businessServicesStep',
178
+ text: '#businessServicesText',
179
+ css: 'business',
180
+ vector: '#businessServicesVector',
181
+ },
182
+ {
183
+ mark: '#contactMark',
184
+ sing: '#contactSign',
185
+ step: '#contactStep',
186
+ text: '#contactText',
187
+ css: 'contact',
188
+ vector: '#contactVector',
189
+ },
190
+ {
191
+ mark: '#galleryMark',
192
+ sing: '#gallerySign',
193
+ step: '#galleryStep',
194
+ text: '#galleryText',
195
+ css: 'gallery',
196
+ vector: '#galleryVector',
197
+ },
198
+ ];
199
+
200
+ setupStepTracking(stepPairs);
201
+ }
202
+
203
+ function setupStepTracking(stepPairs) {
204
+ stepPairs.forEach(({ mark, sing, step, text, css, vector }) => {
205
+ $w(mark).onViewportEnter(() => {
206
+ $w(sing).customClassList.add('current-step');
207
+ $w(step).customClassList.add('highlighted-text');
208
+ $w(text).customClassList.add('highlighted-text');
209
+ $w(vector).customClassList.add('disabeld-step');
210
+ $w('#accordion').customClassList.add(css);
211
+ $w(mark).scrollTo();
212
+ });
213
+
214
+ $w(mark).onViewportLeave(() => {
215
+ $w(sing).customClassList.remove('current-step');
216
+ $w(step).customClassList.remove('highlighted-text');
217
+ $w(text).customClassList.remove('highlighted-text');
218
+ $w(vector).customClassList.remove('disabeld-step');
219
+ $w('#accordion').customClassList.remove(css);
220
+ });
221
+ });
222
+ }
223
+
224
+ function init() {
225
+ const fullProfilePageLink = `${wixLocation.baseUrl}/profile/${itemMemberObj.url}`;
226
+ setPersonalDetails(fullProfilePageLink);
227
+ setBusinessServices();
228
+ setContactBooking(fullProfilePageLink);
229
+ initGallery();
230
+ }
231
+ function initGallery() {
232
+ $w('#galleryRepeater').onItemReady(handleGalleryItem);
233
+ $w('#uploadGalleryImageButton').onChange(async (event) => {
234
+ const $item = $w.at(event.context);
235
+ const uploadButton = $item('#uploadGalleryImageButton');
236
+ if (uploadButton.value.length === 0) return;
237
+ try {
238
+ const uploadedFiles = await uploadButton.uploadFiles();
239
+ // Initialize gallery array if it doesn't exist
240
+ if (!itemMemberObj.gallery) {
241
+ itemMemberObj.gallery = [];
242
+ }
243
+
244
+ uploadedFiles.forEach((file) => {
245
+ itemMemberObj.gallery.unshift({ src: file.fileUrl });
246
+ });
247
+
248
+ await saveGalleryToCMS();
249
+ setGallery();
250
+ } catch (error) {
251
+ $w('#uploadFailedText').expand();
252
+ setTimeout(() => {
253
+ $w('#uploadFailedText').collapse();
254
+ }, 5000);
255
+ console.error('Upload failed:', error);
256
+ }
257
+ });
258
+ $w('#deleteImageButton').onClick(async (event) => {
259
+ const itemId = event.context.itemId;
260
+ const itemData = $w('#galleryRepeater').data.find((item) => item._id === itemId);
261
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
262
+ if (result && result.toDelete) {
263
+ itemMemberObj.gallery = itemMemberObj.gallery.filter((img) => img.src !== itemData.image.src);
264
+ await saveGalleryToCMS();
265
+ setGallery(); // Re-render
266
+ }
267
+ });
268
+ //
269
+ setGallery();
270
+ }
271
+
272
+ function setPersonalDetails(fullProfilePageLink) {
273
+ $w('#firstNameInput').value = itemMemberObj.firstName || '';
274
+ $w('#lastNameInput').value = itemMemberObj.lastName || '';
275
+
276
+ $w('#slugInput').value = itemMemberObj.url || '';
277
+
278
+ isSlugValid = true;
279
+
280
+ $w(SLUG_FLAGS.VALID).collapse();
281
+ $w(SLUG_FLAGS.INVALID).collapse();
282
+
283
+ $w('#profileLink').text = fullProfilePageLink;
284
+ $w('#profileLink').link = fullProfilePageLink;
285
+ $w('#profileLink').target = '_blank';
286
+ $w('#licenceNoText').text = (itemMemberObj.licenses || [])
287
+ .map((val) => val.license)
288
+ .filter(Boolean)
289
+ .join(', ');
290
+ const handleIsStudent = () => {
291
+ if (isStudent) {
292
+ $w('#optCheckbox').disable();
293
+ $w('#optCheckbox').checked = false;
294
+ $w('#optCheckbox').customClassList.add('disabled-text');
295
+ $w('#optCheckbox').customClassList.add('disabled-checkbox');
296
+ } else {
297
+ $w('#optCheckbox').enable();
298
+ $w('#optCheckbox').checked = !itemMemberObj.optOut;
299
+ }
300
+ };
301
+ handleIsStudent();
302
+ $w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
303
+ toggleFreeWebsiteText(itemMemberObj.showWixUrl);
304
+ setupOptOutCheckbox(
305
+ '#optCheckbox',
306
+ '#optConfirmationBox',
307
+ '#yesOptButton',
308
+ '#cancelOptButton',
309
+ (confirmed) => handleOptConfirmation(confirmed, '#optCheckbox', '#optConfirmationBox', 'optOut')
310
+ );
311
+
312
+ setupOptOutCheckbox(
313
+ '#optWebsiteCheckbox',
314
+ '#optWebsiteConfirmationBox',
315
+ '#yesOptWebsiteButton',
316
+ '#cancelOptwebsiteButton',
317
+ (confirmed) => handleOptConfirmation(confirmed, '#optWebsiteCheckbox', '#optWebsiteConfirmationBox', 'showWixUrl')
318
+ );
319
+ }
320
+
321
+ function setupOptOutCheckbox(checkboxId, confirmationBoxId, confirmBtnId, cancelBtnId, confirmCallback) {
322
+ const checkbox = $w(checkboxId);
323
+ const box = $w(confirmationBoxId);
324
+
325
+ checkbox.onChange((e) => {
326
+ if (!e.target.checked) {
327
+ box.expand();
328
+ checkbox.disable();
329
+ checkbox.customClassList.add('disabled-text');
330
+ } else {
331
+ confirmCallback(true);
332
+ }
333
+ let sectionHandlerType;
334
+ if (checkboxId === '#optCheckbox') {
335
+ sectionHandlerType = FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT;
336
+ } else if (checkboxId === '#optWebsiteCheckbox') {
337
+ sectionHandlerType = FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT;
338
+ }
339
+ checkFormChanges(sectionHandlerType);
340
+ });
341
+
342
+ $w(confirmBtnId).onClick(() => confirmCallback(true));
343
+ $w(cancelBtnId).onClick(() => confirmCallback(false));
344
+ }
345
+
346
+ function toggleFreeWebsiteText(isFreeWebsiteEnabled) {
347
+ if (isFreeWebsiteEnabled) {
348
+ $w('#freeWebsiteText').text = FREE_WEBSITE_TEXT_STATES.ENABLED;
349
+ } else {
350
+ $w('#freeWebsiteText').text = FREE_WEBSITE_TEXT_STATES.DISABLED;
351
+ }
352
+ }
353
+ async function handleOptConfirmation(confirmed, optCheckbox, optConfirmationBox, field) {
354
+ const checkbox = $w(optCheckbox);
355
+ const box = $w(optConfirmationBox);
356
+ if (confirmed) {
357
+ const toSaveOptValue = optCheckbox === '#optCheckbox' ? !checkbox.checked : checkbox.checked;
358
+ const formData = {
359
+ ...itemMemberObj,
360
+ [field]: toSaveOptValue,
361
+ };
362
+
363
+ await saveData(formData);
364
+ if (field === 'showWixUrl') {
365
+ const showExistingUrl = $w('#showExsistingUrlCheckbox').checked;
366
+ if (toSaveOptValue) {
367
+ if (!showExistingUrl) {
368
+ $w('#showUrlWixCheckbox').checked = true;
369
+ }
370
+ $w('#showUrlWixCheckbox').enable();
371
+ } else {
372
+ $w('#showUrlWixCheckbox').checked = false;
373
+ $w('#showUrlWixCheckbox').disable();
374
+ }
375
+ toggleFreeWebsiteText(toSaveOptValue);
376
+ }
377
+ }
378
+
379
+ box.collapse();
380
+ checkbox.enable();
381
+ checkbox.customClassList.remove('disabled-text');
382
+
383
+ if (!confirmed) {
384
+ checkbox.checked = !checkbox.checked;
385
+ }
386
+ const section =
387
+ field === 'showWixUrl'
388
+ ? FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.section
389
+ : FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.section;
390
+ formHasUnsavedChanges[section] = false;
391
+ }
392
+
393
+ function onFormDataChanged() {
394
+ const CHANGE_EVENTS = {
395
+ ON_CHANGE: 'onChange',
396
+ ON_INPUT: 'onInput',
397
+ };
398
+ const elements = {
399
+ $firstNameInput: $w('#firstNameInput'),
400
+ $lastNameInput: $w('#lastNameInput'),
401
+ $businessNameCheckbox: $w('#businessNameCheckbox'),
402
+ $yearJoinedcheckbox: $w('#yearJoinedcheckbox'),
403
+ $aboutInput: $w('#aboutInput'),
404
+ $businessNameInput: $w('#businessNameInput'),
405
+ $uploadProfileButton: $w('#uploadProfileButton'),
406
+ $uploadLogoButton: $w('#uploadLogoButton'),
407
+ $uploadBannerButton: $w('#uploadBannerButton'),
408
+ $showContactFormCheckbox: $w('#showCotactFormCheckbox'),
409
+ $contactFormEmailInput: $w('#contactFormEmailInput'),
410
+ $schedulingLinkInput: $w('#schedulingLinkInput'),
411
+ $UrlInput: $w('#UrlInput'),
412
+ $slugInput: $w('#slugInput'),
413
+ };
414
+ const formChangeEventBindings = {
415
+ [FORM_SECTION_HANDLER_MAP.PERSONAL.section]: [
416
+ { $elem: elements.$firstNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
417
+ { $elem: elements.$lastNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
418
+ { $elem: elements.$slugInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
419
+ ],
420
+ [FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: [
421
+ { $elem: elements.$businessNameCheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
422
+ { $elem: elements.$yearJoinedcheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
423
+ { $elem: elements.$aboutInput, changeEvent: CHANGE_EVENTS.ON_CHANGE },
424
+ { $elem: elements.$businessNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
425
+ { $elem: elements.$uploadProfileButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
426
+ { $elem: elements.$uploadLogoButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
427
+ { $elem: elements.$uploadBannerButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
428
+ ],
429
+ [FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: [
430
+ { $elem: elements.$showContactFormCheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
431
+ { $elem: elements.$contactFormEmailInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
432
+ { $elem: elements.$schedulingLinkInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
433
+ { $elem: elements.$UrlInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
434
+ ],
435
+ };
436
+ Object.keys(formChangeEventBindings).forEach((section) => {
437
+ formChangeEventBindings[section].forEach(({ $elem, changeEvent }) => {
438
+ $elem[changeEvent](() => {
439
+ const handlerMap = Object.values(FORM_SECTION_HANDLER_MAP).find(
440
+ (handlerMap) => handlerMap.section === section
441
+ );
442
+ checkFormChanges(handlerMap);
443
+ });
444
+ });
445
+ });
446
+
447
+ $w('#slugInput').onInput((event) => {
448
+ $w('#savePersonalButton').disable();
449
+ const slug = event.target.value;
450
+
451
+ isSlugValid = false;
452
+
453
+ if (slugValidationTimeout.slugValidation) {
454
+ clearTimeout(slugValidationTimeout.slugValidation);
455
+ }
456
+
457
+ const validationId = ++currentSlugValidationId;
458
+
459
+ slugValidationTimeout.slugValidation = setTimeout(async () => {
460
+ try {
461
+ if (validationId !== currentSlugValidationId) {
462
+ return;
463
+ }
464
+
465
+ const result = await validateSlugRealTime(slug);
466
+
467
+ if (validationId === currentSlugValidationId) {
468
+ isSlugValid = result.isValid;
469
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.PERSONAL);
470
+ }
471
+ } catch (error) {
472
+ console.error('Slug validation error:', error);
473
+ if (validationId === currentSlugValidationId) {
474
+ isSlugValid = false;
475
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.PERSONAL);
476
+ }
477
+ }
478
+ }, 800);
479
+ });
480
+ }
481
+
482
+ function checkFormChanges(formSectionHandler) {
483
+ let isFormDataChanged = false;
484
+ const toggleSaveDataButton = (formDataType, isFormDataChanged) => {
485
+ const saveButtonMap = {
486
+ [FORM_SECTION_HANDLER_MAP.PERSONAL.section]: '#savePersonalButton',
487
+ [FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: '#saveBusinessButton',
488
+ [FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: '#saveContactBookingButton',
489
+ };
490
+
491
+ const buttonSelector = saveButtonMap[formDataType];
492
+ if (!buttonSelector) {
493
+ throw new Error(`No save button defined for form section: ${formDataType}`);
494
+ }
495
+ const $saveDataButton = $w(buttonSelector);
496
+
497
+ let isUrlValid = true,
498
+ isEmailValid = true,
499
+ isNameValid = true,
500
+ isSlugValid = true;
501
+ if (formDataType === FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section) {
502
+ isEmailValid = $w('#contactFormEmailInput').valid;
503
+ isUrlValid = !isNotValidUrl($w('#UrlInput').value) && !isNotValidUrl($w('#schedulingLinkInput').value);
504
+ }
505
+ if (formDataType === FORM_SECTION_HANDLER_MAP.PERSONAL.section) {
506
+ isNameValid = $w('#firstNameInput').valid && $w('#lastNameInput').valid;
507
+ }
508
+
509
+ if (isFormDataChanged && isUrlValid && isEmailValid && isNameValid && isSlugValid) {
510
+ $saveDataButton.enable();
511
+ } else {
512
+ $saveDataButton.disable();
513
+ }
514
+ };
515
+ if (formSectionHandler) {
516
+ const { section, handler } = formSectionHandler;
517
+ isFormDataChanged = handler();
518
+ formHasUnsavedChanges[section] = isFormDataChanged;
519
+ if (
520
+ [
521
+ FORM_SECTION_HANDLER_MAP.PERSONAL.section,
522
+ FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section,
523
+ FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section,
524
+ ].includes(section)
525
+ ) {
526
+ toggleSaveDataButton(section, isFormDataChanged);
527
+ }
528
+ } else {
529
+ Object.values(FORM_SECTION_HANDLER_MAP).forEach(({ section, handler }) => {
530
+ formHasUnsavedChanges[section] = handler();
531
+ });
532
+ }
533
+ }
534
+ function checkWebsiteOptOutDataChanged() {
535
+ const currentWebsiteOptInData = $w('#optWebsiteCheckbox').checked;
536
+ const originalWebsiteOptInData = originalMemberData.showWixUrl;
537
+ return !_.isEqual(currentWebsiteOptInData, originalWebsiteOptInData);
538
+ }
539
+ function checkDirectoryOptOutDataChanged() {
540
+ const currentDirectoryOptOutData = $w('#optCheckbox').checked;
541
+ const originalDirectoryOptOutData = !originalMemberData.optOut;
542
+ return !_.isEqual(currentDirectoryOptOutData, originalDirectoryOptOutData);
543
+ }
544
+ function checkBusinessDataChanged() {
545
+ const currentBusinessData = getBusinessAndServicesData();
546
+ const originalBusinessData = {
547
+ showBusinessName: originalMemberData.showBusinessName,
548
+ businessName: originalMemberData.businessName,
549
+ showABMP: originalMemberData.showABMP,
550
+ aboutService: originalMemberData.aboutService,
551
+ profileImage: originalMemberData.profileImage,
552
+ logoImage: originalMemberData.logoImage,
553
+ bannerImages: originalMemberData.bannerImages || [],
554
+ areasOfPractices: originalMemberData.areasOfPractices || [],
555
+ testimonial: originalMemberData.testimonial || [],
556
+ };
557
+ return !_.isEqual(currentBusinessData, originalBusinessData);
558
+ }
559
+
560
+ function checkContactDataChanged() {
561
+ const currentContactData = getContactAndBookingData();
562
+ const originalContactData = {
563
+ showContactForm: originalMemberData.showContactForm,
564
+ contactFormEmail: originalMemberData.contactFormEmail,
565
+ toShowPhone: originalMemberData.toShowPhone,
566
+ bookingUrl: originalMemberData.bookingUrl,
567
+ website: originalMemberData.website,
568
+ showWebsite: originalMemberData.showWebsite,
569
+ showWixUrl: originalMemberData.showWixUrl,
570
+ addressDisplayOption: originalMemberData.addressDisplayOption,
571
+ addresses: originalMemberData.addresses,
572
+ };
573
+ return !_.isEqual(currentContactData, originalContactData);
574
+ }
575
+
576
+ function setBusinessServices() {
577
+ $w('#businessNameText').text = itemMemberObj.businessName || DEFAULT_BUSINESS_NAME_TEXT;
578
+ $w('#businessNameCheckbox').checked = itemMemberObj.showBusinessName;
579
+ $w('#yearJoinedcheckbox').checked = itemMemberObj.showABMP;
580
+ $w('#aboutInput').value = itemMemberObj.aboutService;
581
+ $w('#businessNameInput').value = itemMemberObj.businessName;
582
+ $w('#clearBusinessNameBtn').onClick(() => {
583
+ $w('#businessNameInput').value = '';
584
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
585
+ });
586
+
587
+ // Get memberships array
588
+ const memberships = Array.isArray(itemMemberObj.memberships) ? itemMemberObj.memberships : [];
589
+ // Find ABMP object
590
+ const abmp = memberships.find((m) => m.association === 'ABMP');
591
+ // Set yearJoinedText
592
+ if (abmp && abmp.membersince) {
593
+ $w('#yearJoinedText').text = abmp.membersince;
594
+ } else {
595
+ $w('#yearJoinedText').text = 'Year joined not provided';
596
+ }
597
+
598
+ uploadImageFromLightbox();
599
+ setupImageUploadAndDeleteHandlers();
600
+ setupServiceSelection();
601
+ setInterestData();
602
+ setupTestimonials();
603
+
604
+ // Initialize areasOfPractices array if it doesn't exist
605
+ if (!itemMemberObj.areasOfPractices) {
606
+ itemMemberObj.areasOfPractices = [];
607
+ }
608
+
609
+ if (Array.isArray(itemMemberObj.areasOfPractices)) {
610
+ selectedServices = itemMemberObj.areasOfPractices.map((label) => ({
611
+ _id: generateId(),
612
+ label: String(label),
613
+ }));
614
+ renderServices();
615
+ }
616
+
617
+ displayExistingImagesFromCMS();
618
+
619
+ $w('#savePersonalButton').onClick(savePersonalDetails);
620
+ $w('#saveBusinessButton').onClick(saveBusinessServices);
621
+ $w('#servicesRepeater').onItemReady(($item, itemData) => {
622
+ $item('#serviceNameText').text = itemData.label;
623
+ });
624
+ }
625
+
626
+ function uploadImage(uploadButton, imageKey, updateUI) {
627
+ $w(uploadButton).onChange(async () => {
628
+ if ($w(uploadButton).value?.length > 0) {
629
+ try {
630
+ const uploadedFiles = await $w(uploadButton).uploadFiles();
631
+ uploadedFiles.forEach((file) => {
632
+ uploadedImages[imageKey] = file.fileUrl;
633
+ updateUI(file);
634
+ });
635
+ } catch (error) {
636
+ console.error(`File upload error: ${error.errorCode}`, error.errorDescription);
637
+ }
638
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
639
+ }
640
+ });
641
+ }
642
+
643
+ function uploadImageFromLightbox() {
644
+ $w('#bannerLightboxButton').onClick(async () => {
645
+ const returnedImage = await wixWindow.openLightbox(LIGHTBOX_NAMES.SELECT_BANNER_IMAGES);
646
+ console.log('uploadedImages', returnedImage);
647
+
648
+ if (returnedImage && returnedImage.image) {
649
+ console.log('Image returned from lightbox:', returnedImage);
650
+
651
+ // Update stored image
652
+ uploadedImages.bannerImage = returnedImage.image;
653
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
654
+ $w('#bannerImage').src = returnedImage.image;
655
+ $w('#bannerImageName').text = extractFileName(returnedImage.image);
656
+ $w('#bannerImageContainer').expand();
657
+ }
658
+ });
659
+ }
660
+
661
+ function extractFileName(fileUrl) {
662
+ try {
663
+ const url = new URL(fileUrl);
664
+ const pathParts = url.pathname.split('/');
665
+ return pathParts[pathParts.length - 1] || '';
666
+ } catch {
667
+ return '';
668
+ }
669
+ }
670
+
671
+ function setupImageUploadAndDeleteHandlers() {
672
+ uploadImage('#uploadProfileButton', 'profileImage', (file) => {
673
+ $w('#profileImage').src = file.fileUrl;
674
+ $w('#profileImageName').text = formatFileName(file.fileName);
675
+ $w('#profileImageContainer').expand();
676
+ });
677
+
678
+ uploadImage('#uploadLogoButton', 'logoImage', (file) => {
679
+ $w('#logoImage').src = file.fileUrl;
680
+ $w('#logoImageName').text = file.fileName;
681
+ $w('#logoImageContainer').expand();
682
+ });
683
+
684
+ uploadImage('#uploadBannerButton', 'bannerImage', (file) => {
685
+ $w('#bannerImage').src = file.fileUrl;
686
+ $w('#bannerImageName').text = file.fileName;
687
+ $w('#bannerImageContainer').expand();
688
+ });
689
+
690
+ setupDeleteHandler(
691
+ '#deleteProfileImage',
692
+ '#profileImage',
693
+ '#profileImageName',
694
+ '#profileImageContainer',
695
+ 'profileImage',
696
+ '#uploadProfileButton'
697
+ );
698
+ setupDeleteHandler(
699
+ '#deleteLogoImage',
700
+ '#logoImage',
701
+ '#logoImageName',
702
+ '#logoImageContainer',
703
+ 'logoImage',
704
+ '#uploadLogoButton'
705
+ );
706
+ setupDeleteHandler(
707
+ '#deleteBannerImage',
708
+ '#bannerImage',
709
+ '#bannerImageName',
710
+ '#bannerImageContainer',
711
+ 'bannerImage',
712
+ '#uploadBannerButton'
713
+ );
714
+ }
715
+
716
+ function setupDeleteHandler(deleteBtn, imgId, nameId, containerId, imageKey, uploadBtnId) {
717
+ $w(deleteBtn).onClick(async () => {
718
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
719
+
720
+ if (result && result.toDelete) {
721
+ $w(imgId).src = '';
722
+ $w(nameId).text = '';
723
+ $w(containerId).collapse();
724
+ uploadedImages[imageKey] = '';
725
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
726
+ $w(uploadBtnId).reset();
727
+ }
728
+ });
729
+ }
730
+
731
+ function displayExistingImagesFromCMS() {
732
+ const imageMap = [
733
+ {
734
+ key: 'profileImage',
735
+ imageSelector: '#profileImage',
736
+ nameSelector: '#profileImageName',
737
+ containerSelector: '#profileImageContainer',
738
+ },
739
+ {
740
+ key: 'logoImage',
741
+ imageSelector: '#logoImage',
742
+ nameSelector: '#logoImageName',
743
+ containerSelector: '#logoImageContainer',
744
+ },
745
+ {
746
+ key: 'bannerImages',
747
+ imageSelector: '#bannerImage',
748
+ nameSelector: '#bannerImageName',
749
+ containerSelector: '#bannerImageContainer',
750
+ isArray: true,
751
+ },
752
+ ];
753
+
754
+ imageMap.forEach(({ key, imageSelector, nameSelector, containerSelector, isArray }) => {
755
+ const imageValue = isArray
756
+ ? Array.isArray(itemMemberObj[key]) && itemMemberObj[key].length > 0
757
+ ? itemMemberObj[key][0]
758
+ : null
759
+ : itemMemberObj[key];
760
+
761
+ if (imageValue) {
762
+ $w(imageSelector).src = imageValue;
763
+ $w(nameSelector).text = formatFileName(extractFileName(imageValue));
764
+ $w(containerSelector).expand();
765
+ uploadedImages[key === 'bannerImages' ? 'bannerImage' : key] = imageValue;
766
+ }
767
+ });
768
+ }
769
+
770
+ async function handleItemDelete(event, getTextSelector, arrayRef, matchField, renderFn) {
771
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
772
+
773
+ if (result && result.toDelete) {
774
+ const $clickedItem = $w.at(event.context);
775
+ const textToRemove = $clickedItem(getTextSelector).text;
776
+
777
+ arrayRef.splice(
778
+ 0,
779
+ arrayRef.length,
780
+ ...arrayRef.filter((item) =>
781
+ typeof item === 'string' ? item !== textToRemove : item[matchField] !== textToRemove
782
+ )
783
+ );
784
+
785
+ renderFn();
786
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
787
+ }
788
+ }
789
+
790
+ async function setInterestData() {
791
+ const interestsData = await getInterestAll();
792
+
793
+ $w('#removeServiceButton').onClick((event) => {
794
+ handleItemDelete(event, '#serviceNameText', selectedServices, 'label', renderServices);
795
+ });
796
+
797
+ if (Array.isArray(interestsData) && interestsData.length > 0) {
798
+ const formattedData = interestsData.map((val, index) => ({
799
+ _id: String(index),
800
+ value: val,
801
+ }));
802
+
803
+ $w('#repeaterInterest').data = formattedData;
804
+ $w('#repeaterInterest').onItemReady(($item, itemData, index) => {
805
+ $item('#interestText').text = itemData.value;
806
+ });
807
+ }
808
+ }
809
+
810
+ async function setupServiceSelection() {
811
+ const intrestInput = $w('#intrestInput');
812
+ intrestInput.onClick(async () => {
813
+ if (intrestInput.value) {
814
+ intrestInput.onClick(async () => {
815
+ await filterInterests(intrestInput.value);
816
+ });
817
+ } else {
818
+ setInterestData();
819
+ }
820
+ $w('#containerRepeaterInterest').expand();
821
+ });
822
+
823
+ // intrestInput.onBlur(() => {
824
+ // // Give time to allow click on repeater item before collapsing
825
+ // setTimeout(() => {
826
+ // $w('#containerRepeaterInterest').collapse();
827
+ // }, 150);
828
+ // });
829
+
830
+ intrestInput.onKeyPress((event) => {
831
+ debounce_fun();
832
+
833
+ if (event.key === 'Enter') {
834
+ const typedValue = intrestInput.value.trim();
835
+
836
+ if (
837
+ typedValue &&
838
+ !selectedServices.some((service) => service.label.toLowerCase() === typedValue.toLowerCase())
839
+ ) {
840
+ selectedServices.unshift({
841
+ _id: generateId(),
842
+ label: typedValue,
843
+ });
844
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
845
+ renderServices();
846
+ intrestInput.value = '';
847
+ $w('#containerRepeaterInterest').collapse();
848
+ }
849
+ }
850
+ });
851
+
852
+ $w('#repeaterInterest').onItemReady(($item, itemData) => {
853
+ $item('#interestText').text = itemData.value;
854
+
855
+ $item('#intrestItem').onClick(() => {
856
+ if (!selectedServices.some((service) => service.label === itemData.value)) {
857
+ selectedServices.unshift({
858
+ _id: generateId(),
859
+ label: itemData.value,
860
+ });
861
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
862
+ renderServices();
863
+ $w('#intrestInput').value = '';
864
+ $w('#containerRepeaterInterest').collapse();
865
+ }
866
+ });
867
+ });
868
+ }
869
+
870
+ function renderServices() {
871
+ setupRepeater('#servicesRepeater', selectedServices);
872
+ }
873
+
874
+ function setupRepeater(repeaterId, data) {
875
+ const repeater = $w(repeaterId);
876
+ repeater.data = data;
877
+ }
878
+
879
+ /**
880
+ * Logs user data changes for debugging and investigation purposes
881
+ * @param {string} saveType - Type of save operation (personal, business, contact)
882
+ * @param {Object} beforeData - Data before save
883
+ * @param {Object} afterData - Data after save
884
+ * @param {boolean} success - Whether the save was successful
885
+ */
886
+ function logUserDataChanges(saveType, beforeData, afterData, success) {
887
+ const memberId = wixStorage.local.getItem('memberId');
888
+ const timestamp = new Date().toISOString();
889
+
890
+ console.group(`User Data Change Log - ${saveType.toUpperCase()} - ${success ? 'SUCCESS' : 'FAILED'}`);
891
+ console.log('Change Details:', {
892
+ memberId,
893
+ timestamp,
894
+ saveType,
895
+ success,
896
+ });
897
+
898
+ console.log('BEFORE Save:', beforeData);
899
+ console.log('AFTER Save:', afterData);
900
+
901
+ // Calculate and log specific changes
902
+ const changes = {};
903
+ Object.keys(afterData).forEach((key) => {
904
+ if (JSON.stringify(beforeData[key]) !== JSON.stringify(afterData[key])) {
905
+ changes[key] = {
906
+ before: beforeData[key],
907
+ after: afterData[key],
908
+ };
909
+ }
910
+ });
911
+
912
+ if (Object.keys(changes).length > 0) {
913
+ console.log('Specific Changes:', changes);
914
+ } else {
915
+ console.log('No changes detected in data comparison');
916
+ }
917
+
918
+ console.groupEnd();
919
+ }
920
+
921
+ async function saveData(formData) {
922
+ const memberId = wixStorage.local.getItem('memberId');
923
+
924
+ // Capture data before save for logging
925
+ const beforeSaveData = JSON.parse(JSON.stringify(itemMemberObj));
926
+
927
+ const { type, saveData: saved } = await saveRegistrationData(formData, memberId);
928
+
929
+ if (type === 'success') {
930
+ // Log the successful change
931
+ logUserDataChanges('general', beforeSaveData, saved, true);
932
+
933
+ itemMemberObj = { ...saved };
934
+ originalMemberData = JSON.parse(JSON.stringify(saved));
935
+ return {
936
+ success: true,
937
+ message: 'The information was saved successfully.',
938
+ };
939
+ } else {
940
+ // Log the failed attempt
941
+ logUserDataChanges('general', beforeSaveData, formData, false);
942
+
943
+ return {
944
+ success: false,
945
+ message: "It looks like something went wrong — the information wasn't saved. Please try again later.",
946
+ };
947
+ }
948
+ }
949
+
950
+ function isValidSlugFormat(slug) {
951
+ if (!slug || slug.length === 0) return false;
952
+ if (slug.length < 3 || slug.length > 50) return false;
953
+
954
+ const slugRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/;
955
+ return slugRegex.test(slug);
956
+ }
957
+
958
+ async function validateSlugRealTime(slug) {
959
+ $w(SLUG_FLAGS.VALID).collapse();
960
+ $w(SLUG_FLAGS.INVALID).collapse();
961
+
962
+ const trimmedSlug = slug.trim();
963
+
964
+ if (!isValidSlugFormat(trimmedSlug)) {
965
+ $w('#invalidSlugMessage').text = SLUG_MESSAGES.INVALID_FORMAT;
966
+ $w(SLUG_FLAGS.INVALID).expand();
967
+ return { isValid: false };
968
+ }
969
+
970
+ if (trimmedSlug === (originalMemberData.url || '')) {
971
+ $w(SLUG_FLAGS.VALID).collapse();
972
+ $w(SLUG_FLAGS.INVALID).collapse();
973
+ return { isValid: true };
974
+ }
975
+
976
+ try {
977
+ const result = await checkUrlUniqueness(trimmedSlug, itemMemberObj.memberId);
978
+ const isUnique = result.isUnique;
979
+
980
+ if (isUnique) {
981
+ $w(SLUG_FLAGS.VALID).expand();
982
+ $w(SLUG_FLAGS.INVALID).collapse();
983
+ return { isValid: true };
984
+ } else {
985
+ $w('#invalidSlugMessage').text = SLUG_MESSAGES.TAKEN;
986
+ $w(SLUG_FLAGS.INVALID).expand();
987
+ $w(SLUG_FLAGS.VALID).collapse();
988
+ return { isValid: false };
989
+ }
990
+ } catch (error) {
991
+ console.error('Error checking slug uniqueness:', error);
992
+ $w('#invalidSlugMessage').text = SLUG_MESSAGES.ERROR;
993
+ $w(SLUG_FLAGS.INVALID).expand();
994
+ $w(SLUG_FLAGS.VALID).collapse();
995
+ return { isValid: false };
996
+ }
997
+ }
998
+
999
+ function getPersonalData() {
1000
+ const firstName = $w('#firstNameInput').value.trim();
1001
+ const lastName = $w('#lastNameInput').value.trim();
1002
+ const fullName = `${firstName} ${lastName}`.trim();
1003
+ const url = $w('#slugInput').value.trim();
1004
+
1005
+ return {
1006
+ firstName,
1007
+ lastName,
1008
+ fullName,
1009
+ url,
1010
+ };
1011
+ }
1012
+
1013
+ function checkPersonalDataChanged() {
1014
+ const currentPersonalData = getPersonalData();
1015
+ const originalPersonalData = {
1016
+ firstName: originalMemberData.firstName || '',
1017
+ lastName: originalMemberData.lastName || '',
1018
+ fullName: originalMemberData.fullName || '',
1019
+ url: originalMemberData.url || '',
1020
+ };
1021
+ return !_.isEqual(currentPersonalData, originalPersonalData);
1022
+ }
1023
+
1024
+ function getBusinessAndServicesData() {
1025
+ const getCurrentTestimonials = () =>
1026
+ $w('#testimonialRepeater')
1027
+ .data.filter((item) => item.isAdd === false)
1028
+ .map((item) => item.text)
1029
+ .filter(Boolean) || itemMemberObj.testimonial;
1030
+
1031
+ return {
1032
+ showBusinessName: $w('#businessNameCheckbox').checked,
1033
+ businessName: $w('#businessNameInput').value,
1034
+ showABMP: $w('#yearJoinedcheckbox').checked,
1035
+ profileImage: uploadedImages.profileImage,
1036
+ logoImage: uploadedImages.logoImage,
1037
+ bannerImages: uploadedImages.bannerImage ? [uploadedImages.bannerImage] : [],
1038
+ areasOfPractices: selectedServices.map((service) => service.label),
1039
+ aboutService: $w('#aboutInput').value,
1040
+ testimonial: getCurrentTestimonials(),
1041
+ };
1042
+ }
1043
+ async function savePersonalDetails() {
1044
+ const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
1045
+ const personalChanges = getPersonalData();
1046
+ const originalUrl = beforeData.url;
1047
+
1048
+ const formData = {
1049
+ ...itemMemberObj,
1050
+ ...personalChanges,
1051
+ };
1052
+
1053
+ // Log the specific personal data changes
1054
+ console.group('Personal Details Save Attempt');
1055
+ console.log('Current Data:', beforeData);
1056
+ console.log('Changes Being Applied:', personalChanges);
1057
+ console.log('Final Form Data:', formData);
1058
+ console.groupEnd();
1059
+
1060
+ const result = await saveData(formData);
1061
+ formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.PERSONAL.section] = false;
1062
+
1063
+ if (result.success) {
1064
+ if (personalChanges.url && personalChanges.url !== originalUrl) {
1065
+ const newProfileLink = `${wixLocation.baseUrl}/profile/${personalChanges.url}`;
1066
+ console.log('🔗 Updating profile link:', { originalUrl, newUrl: personalChanges.url, newProfileLink });
1067
+ $w('#profileLink').text = newProfileLink;
1068
+ $w('#profileLink').link = newProfileLink;
1069
+
1070
+ $w(SLUG_FLAGS.VALID).collapse();
1071
+ $w(SLUG_FLAGS.INVALID).collapse();
1072
+ }
1073
+
1074
+ $w('#savePersonalButton').disable();
1075
+ }
1076
+
1077
+ handleSaveDataFeedback($w('#personalMessage'), result.message);
1078
+ }
1079
+
1080
+ async function saveBusinessServices() {
1081
+ const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
1082
+ const businessChanges = getBusinessAndServicesData();
1083
+
1084
+ const formData = {
1085
+ ...itemMemberObj,
1086
+ ...businessChanges,
1087
+ };
1088
+
1089
+ // Log the specific business data changes
1090
+ console.group('Business Services Save Attempt');
1091
+ console.log('Current Data:', beforeData);
1092
+ console.log('Changes Being Applied:', businessChanges);
1093
+ console.log('Final Form Data:', formData);
1094
+ console.log('Image Changes:', {
1095
+ profileImage: uploadedImages.profileImage,
1096
+ logoImage: uploadedImages.logoImage,
1097
+ bannerImage: uploadedImages.bannerImage,
1098
+ });
1099
+ console.log('Services Selected:', selectedServices);
1100
+ console.groupEnd();
1101
+
1102
+ const result = await saveData(formData);
1103
+ formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section] = false;
1104
+ handleSaveDataFeedback($w('#businessMessage'), result.message);
1105
+ $w('#businessNameText').text = formData.businessName || DEFAULT_BUSINESS_NAME_TEXT;
1106
+ }
1107
+
1108
+ function setupTestimonials() {
1109
+ const addTestimonialButton = $w('#addTestimonialButton');
1110
+
1111
+ addTestimonialButton.onClick(handleAddTestimonial);
1112
+ $w('#deleteTestimonialButton').onClick((event) => {
1113
+ handleItemDelete(event, '#testimonialText', itemMemberObj.testimonial, null, renderTestimonials);
1114
+ });
1115
+
1116
+ renderTestimonials();
1117
+ $w('#testimonialRepeater').onItemReady(($item, itemData) => {
1118
+ const msb = $item('#testimonialMSB');
1119
+ if (itemData.isAdd) {
1120
+ msb.changeState(TESTIMONIAL_STATES.ADD);
1121
+ } else {
1122
+ msb.changeState(TESTIMONIAL_STATES.VIEW);
1123
+ $item('#testimonialText').text = itemData.text;
1124
+ }
1125
+ });
1126
+ }
1127
+
1128
+ function renderTestimonials() {
1129
+ const testimonials =
1130
+ itemMemberObj.testimonial === null
1131
+ ? []
1132
+ : Array.isArray(itemMemberObj.testimonial)
1133
+ ? itemMemberObj.testimonial
1134
+ : [];
1135
+ const addItem = { _id: 'add-item', text: '', isAdd: true };
1136
+ const testimonialData = [
1137
+ addItem,
1138
+ ...testimonials.map((text) => ({ _id: generateId(), text, isAdd: false })),
1139
+ ];
1140
+
1141
+ setupRepeater('#testimonialRepeater', testimonialData);
1142
+ }
1143
+
1144
+ function handleAddTestimonial(event) {
1145
+ const $clickedItem = $w.at(event.context);
1146
+ const input = $clickedItem('#testimonialsInput');
1147
+ const newText = input.value;
1148
+
1149
+ if (newText?.trim()) {
1150
+ // Initialize testimonial array if it doesn't exist
1151
+ if (!itemMemberObj.testimonial) {
1152
+ itemMemberObj.testimonial = [];
1153
+ }
1154
+ itemMemberObj.testimonial.push(newText.trim());
1155
+ input.value = '';
1156
+ renderTestimonials();
1157
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
1158
+ }
1159
+ }
1160
+
1161
+ function setContactBooking(fullProfilePageLink) {
1162
+ // derive booleans only once
1163
+ const showWixUrlCheckbox = !itemMemberObj.showWebsite && itemMemberObj.showWixUrl;
1164
+ const showExistingUrlCheckbox = itemMemberObj.showWebsite;
1165
+
1166
+ // basic fields
1167
+ $w('#showCotactFormCheckbox').checked = itemMemberObj.showContactForm;
1168
+ $w('#contactFormEmailInput').value = itemMemberObj.contactFormEmail;
1169
+ $w('#schedulingLinkInput').value = itemMemberObj.bookingUrl;
1170
+
1171
+ // URL part
1172
+ $w('#UrlInput').value = itemMemberObj.website || '';
1173
+ $w('#showUrlWixCheckbox').checked = showWixUrlCheckbox;
1174
+ $w('#showExsistingUrlCheckbox').checked = showExistingUrlCheckbox;
1175
+ $w('#urlWebsiteText').text = fullProfilePageLink;
1176
+
1177
+ // custom validation for url inputs and email
1178
+ handleOnCustomValidation($w('#UrlInput'));
1179
+ handleOnCustomValidation($w('#schedulingLinkInput'));
1180
+
1181
+ // enable/disable & styling in one pass
1182
+ if (showWixUrlCheckbox) {
1183
+ $w('#UrlInput').disable();
1184
+ $w('#urlWebsiteText').customClassList.add('highlighted-text');
1185
+ } else if (showExistingUrlCheckbox) {
1186
+ $w('#UrlInput').enable();
1187
+ $w('#urlWebsiteText').customClassList.remove('highlighted-text');
1188
+ } else {
1189
+ // neither checked: disable input & remove highlight
1190
+ $w('#UrlInput').disable();
1191
+ $w('#urlWebsiteText').customClassList.remove('highlighted-text');
1192
+ }
1193
+
1194
+ // clear buttons
1195
+ $w('#clearSchedulingLinkInput').onClick(() => {
1196
+ $w('#schedulingLinkInput').value = '';
1197
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1198
+ });
1199
+ $w('#clearExistingUrlLinkInput').onClick(() => {
1200
+ $w('#UrlInput').value = '';
1201
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1202
+ });
1203
+
1204
+ // toggle handlers
1205
+ $w('#showUrlWixCheckbox').onChange((e) => {
1206
+ if (e.target.checked) {
1207
+ $w('#showExsistingUrlCheckbox').checked = false;
1208
+ $w('#UrlInput').disable();
1209
+ $w('#urlWebsiteText').customClassList.add('highlighted-text');
1210
+ }
1211
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1212
+ });
1213
+ $w('#showExsistingUrlCheckbox').onChange((e) => {
1214
+ if (e.target.checked) {
1215
+ $w('#showUrlWixCheckbox').checked = false;
1216
+ $w('#UrlInput').enable();
1217
+ $w('#urlWebsiteText').customClassList.remove('highlighted-text');
1218
+ }
1219
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1220
+ });
1221
+
1222
+ setupAddressRepeater();
1223
+ setupPhoneRepeater();
1224
+ $w('#saveContactBookingButton').onClick(saveContactBooking);
1225
+ }
1226
+
1227
+ /**
1228
+ * Converts our internal address format to AddressInput component format
1229
+ * @param {Object} address - Our internal address object with line1, city, state, postalcode, latitude, longitude
1230
+ * @returns {Object} AddressInput compatible object
1231
+ */
1232
+ function convertToAddressInputFormat(address) {
1233
+ if (!address) return null;
1234
+
1235
+ if (!address.line1 && !address.city && !address.state && !address.postalcode) {
1236
+ return null;
1237
+ }
1238
+
1239
+ const formatted = formatFullAddress(address);
1240
+
1241
+ const hasValidCoordinates = Boolean(address.latitude && address.longitude);
1242
+ const location = hasValidCoordinates ? { latitude: address.latitude, longitude: address.longitude } : null;
1243
+
1244
+ return {
1245
+ formatted,
1246
+ location,
1247
+ streetAddress: {
1248
+ name: extractStreetName(address.line1),
1249
+ number: extractStreetNumber(address.line1),
1250
+ },
1251
+ city: address.city || '',
1252
+ subdivision: address.state || '',
1253
+ country: address.country || 'US',
1254
+ postalCode: address.postalcode || '',
1255
+ };
1256
+ }
1257
+
1258
+ /**
1259
+ * Converts AddressInput format back to our internal address format
1260
+ * @param {Object} addressInputValue - AddressInput value object
1261
+ * @param {Object} existingAddress - Existing address data to preserve key and other fields
1262
+ * @returns {Object} Our internal address format
1263
+ */
1264
+ function parseAddressInput(addressInputValue, existingAddress = null) {
1265
+ if (!addressInputValue) return null;
1266
+
1267
+ let line1 = '';
1268
+ if (addressInputValue.streetAddress) {
1269
+ const number = addressInputValue.streetAddress.number || '';
1270
+ const name = addressInputValue.streetAddress.name || '';
1271
+ line1 = `${number} ${name}`.trim();
1272
+ }
1273
+
1274
+ if (!line1 && addressInputValue.formatted) {
1275
+ line1 = addressInputValue.formatted.split(',')[0]?.trim() || '';
1276
+ }
1277
+
1278
+ return {
1279
+ key: existingAddress?.key || generateId(),
1280
+ line1,
1281
+ line2: existingAddress?.line2 || '',
1282
+ city: addressInputValue.city || '',
1283
+ state: addressInputValue.subdivision || '',
1284
+ postalcode: addressInputValue.postalCode || '',
1285
+ country: addressInputValue.country || 'US',
1286
+ latitude: addressInputValue.location?.latitude || (existingAddress?.latitude || 0),
1287
+ longitude: addressInputValue.location?.longitude || (existingAddress?.longitude || 0),
1288
+ addressStatus: existingAddress?.addressStatus || FALLBACK_ADDRESS_STATUS,
1289
+ };
1290
+ }
1291
+
1292
+ function setupAddressRepeater() {
1293
+ $w('#addressesList').onItemReady(($item, itemData, index) => handleAddressItem($item, itemData, index));
1294
+ renderAddressesList();
1295
+
1296
+ $w('#newAddressButton').onClick(addNewAddress);
1297
+
1298
+ setupAddressRepeaterEventListeners();
1299
+ }
1300
+
1301
+ function setupAddressRepeaterEventListeners() {
1302
+ $w('#mainAddressCheckbox').onChange((event) => {
1303
+ const data = $w('#addressesList').data;
1304
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1305
+ const $item = $w.at(event.context);
1306
+
1307
+ $w('#mainAddressCheckbox').checked = false;
1308
+ $item('#mainAddressCheckbox').checked = true;
1309
+
1310
+ if (clickedItemData.address.addressStatus === ADDRESS_STATUS_TYPES.DONT_SHOW) {
1311
+ updateAddressStatus(clickedItemData._id, ADDRESS_STATUS_TYPES.STATE_CITY_ZIP);
1312
+ $item('#addressStatusOptions').value = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
1313
+ }
1314
+
1315
+ updateMainAddressSelection(clickedItemData._id);
1316
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1317
+ });
1318
+
1319
+ $w('#addressStatusOptions').onChange((event) => {
1320
+ const data = $w('#addressesList').data;
1321
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1322
+ const newStatus = event.target.value;
1323
+ const $item = $w.at(event.context);
1324
+ const isMain = $item('#mainAddressCheckbox').checked;
1325
+
1326
+ if (isMain && newStatus === ADDRESS_STATUS_TYPES.DONT_SHOW) {
1327
+ $item('#addressStatusOptions').value = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
1328
+ return;
1329
+ }
1330
+
1331
+ updateAddressStatus(clickedItemData._id, newStatus);
1332
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1333
+ });
1334
+
1335
+ $w('#addressItemEditBtn').onClick((event) => {
1336
+ const data = $w('#addressesList').data;
1337
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1338
+ const $item = $w.at(event.context);
1339
+ $item('#addressItemStates').changeState(ADDRESS_STATES.EDIT);
1340
+ const addressInputValue = convertToAddressInputFormat(clickedItemData.address);
1341
+ $item('#addressEditInput').value = addressInputValue;
1342
+ });
1343
+
1344
+ $w('#addressItemRemoveBtn').onClick(async (event) => {
1345
+ const data = $w('#addressesList').data;
1346
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1347
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
1348
+ if (result && result.toDelete) {
1349
+ removeAddress(clickedItemData._id);
1350
+ }
1351
+ });
1352
+
1353
+ $w('#addressEditInput').onChange((event) => {
1354
+ const $item = $w.at(event.context);
1355
+ validateAddressCompleteness($item);
1356
+ });
1357
+
1358
+ $w('#addressEditCancelBtn').onClick((event) => {
1359
+ const data = $w('#addressesList').data;
1360
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1361
+ const $item = $w.at(event.context);
1362
+ if (clickedItemData.isNewAddress) {
1363
+ removeNewAddressFromRepeater(clickedItemData._id);
1364
+ } else {
1365
+ $item('#addressItemStates').changeState(ADDRESS_STATES.VIEW);
1366
+ $item('#addressValidationMessage').hide();
1367
+ }
1368
+ });
1369
+
1370
+ $w('#addressEditSaveBtn').onClick((event) => {
1371
+ const data = $w('#addressesList').data;
1372
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1373
+ const $item = $w.at(event.context);
1374
+ saveAddressFromSingleInput($item, clickedItemData);
1375
+ });
1376
+ }
1377
+
1378
+ function addNewAddress() {
1379
+ const currentData = $w('#addressesList').data || [];
1380
+
1381
+ if (currentData.length >= MAX_ADDRESSES_COUNT) {
1382
+ return;
1383
+ }
1384
+
1385
+ const newAddressId = generateId();
1386
+
1387
+ const newAddress = {
1388
+ key: newAddressId,
1389
+ line1: '',
1390
+ line2: '',
1391
+ city: '',
1392
+ state: '',
1393
+ postalcode: '',
1394
+ country: 'US',
1395
+ latitude: 0,
1396
+ longitude: 0,
1397
+ addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
1398
+ };
1399
+
1400
+ const newAddressItem = {
1401
+ _id: newAddressId,
1402
+ address: newAddress,
1403
+ isMain: false,
1404
+ addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
1405
+ isNewAddress: true,
1406
+ };
1407
+
1408
+ renderAddressesList([...currentData, newAddressItem]);
1409
+ }
1410
+
1411
+ function handleAddressItem($item, itemData, index) {
1412
+ const multiStateBox = $item('#addressItemStates');
1413
+
1414
+ setupAddressViewState($item, itemData, index);
1415
+ setupAddressEditState($item, itemData, index);
1416
+
1417
+ if (itemData.isNewAddress) {
1418
+ multiStateBox.changeState(ADDRESS_STATES.EDIT);
1419
+ } else {
1420
+ multiStateBox.changeState(ADDRESS_STATES.VIEW);
1421
+ }
1422
+ }
1423
+
1424
+ function setupAddressViewState($item, itemData, index) {
1425
+ const formattedAddress = formatFullAddress(itemData.address);
1426
+
1427
+ $item('#addressItemtext').text = formattedAddress;
1428
+ $item('#addressItemNumber').text = `Location ${index + 1}`;
1429
+ $item('#mainAddressCheckbox').checked = itemData.isMain || false;
1430
+
1431
+ // TO DO: Ask client what should be the default address status
1432
+ const addressStatus = itemData.address.addressStatus || ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
1433
+ $item('#addressStatusOptions').value = addressStatus;
1434
+ }
1435
+
1436
+ function setupAddressEditState($item, itemData, index) {
1437
+ $item('#addressEditInput').enable();
1438
+
1439
+ const addressInputValue = convertToAddressInputFormat(itemData.address);
1440
+ $item('#addressEditInput').value = addressInputValue;
1441
+
1442
+ $item('#addressValidationMessage').hide();
1443
+ }
1444
+
1445
+ function validateAddressCompleteness($item) {
1446
+ const addressInput = $item('#addressEditInput');
1447
+ const saveBtn = $item('#addressEditSaveBtn');
1448
+ const validationMessage = $item('#addressValidationMessage');
1449
+
1450
+ const addressValue = addressInput.value;
1451
+
1452
+ if (!addressValue) {
1453
+ showAddressValidationError(validationMessage, saveBtn, 'Please provide a complete address');
1454
+ return false;
1455
+ }
1456
+
1457
+ const missingFields = [];
1458
+
1459
+ if (!addressValue.streetAddress?.name) {
1460
+ missingFields.push('street name');
1461
+ }
1462
+
1463
+ if (!addressValue.streetAddress?.number) {
1464
+ missingFields.push('street number');
1465
+ }
1466
+
1467
+ if (!addressValue.city) {
1468
+ missingFields.push('city');
1469
+ }
1470
+
1471
+ if (!addressValue.subdivision) {
1472
+ missingFields.push('state');
1473
+ }
1474
+
1475
+ if (!addressValue.postalCode) {
1476
+ missingFields.push('postal code');
1477
+ }
1478
+
1479
+ if (!addressValue.location?.latitude || !addressValue.location?.longitude) {
1480
+ missingFields.push('valid location details');
1481
+ }
1482
+
1483
+ if (missingFields.length > 0) {
1484
+ const message = `Please provide: ${missingFields.join(', ')}`;
1485
+ showAddressValidationError(validationMessage, saveBtn, message);
1486
+ return false;
1487
+ }
1488
+
1489
+ hideAddressValidationError(validationMessage, saveBtn);
1490
+ return true;
1491
+ }
1492
+
1493
+ function showAddressValidationError(validationMessage, saveBtn, message) {
1494
+ validationMessage.text = message;
1495
+ validationMessage.show();
1496
+ saveBtn.disable();
1497
+ }
1498
+
1499
+ function hideAddressValidationError(validationMessage, saveBtn) {
1500
+ validationMessage.hide();
1501
+ saveBtn.enable();
1502
+ }
1503
+
1504
+ function saveAddressFromSingleInput($item, itemData) {
1505
+ if (!validateAddressCompleteness($item)) {
1506
+ return;
1507
+ }
1508
+
1509
+ const addressInput = $item('#addressEditInput');
1510
+ const addressValue = addressInput.value;
1511
+
1512
+ const convertedAddress = parseAddressInput(addressValue, itemData.address);
1513
+
1514
+ const formattedAddress = formatFullAddress(convertedAddress);
1515
+ $item('#addressItemtext').text = formattedAddress;
1516
+
1517
+ if (itemData.isNewAddress) {
1518
+ setNewAddress(itemData._id, convertedAddress);
1519
+ } else {
1520
+ updateAddress(itemData._id, convertedAddress);
1521
+ }
1522
+
1523
+ $item('#addressItemStates').changeState(ADDRESS_STATES.VIEW);
1524
+ $item('#addressValidationMessage').hide();
1525
+ }
1526
+
1527
+ function removeNewAddressFromRepeater(addressId) {
1528
+ const currentData = $w('#addressesList').data || [];
1529
+ const updatedData = currentData.filter((item) => item._id !== addressId);
1530
+ $w('#addressesList').data = updatedData;
1531
+ }
1532
+
1533
+ function setNewAddress(addressId, addressData) {
1534
+ if (!itemMemberObj.addresses) {
1535
+ itemMemberObj.addresses = [];
1536
+ }
1537
+ itemMemberObj.addresses.push(addressData);
1538
+
1539
+ if (!itemMemberObj.addressDisplayOption) {
1540
+ itemMemberObj.addressDisplayOption = [];
1541
+ }
1542
+ itemMemberObj.addressDisplayOption.push({
1543
+ key: addressData.key,
1544
+ isMain: false,
1545
+ });
1546
+
1547
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1548
+ }
1549
+
1550
+ function extractStreetNumber(line1) {
1551
+ if (!line1) return '';
1552
+ const match = line1.match(/^\d+/);
1553
+ return match ? match[0] : '';
1554
+ }
1555
+
1556
+ function extractStreetName(line1) {
1557
+ if (!line1) return '';
1558
+ return line1.replace(/^\d+\s*/, '').trim();
1559
+ }
1560
+
1561
+ function renderAddressesList(updatedAddresses) {
1562
+ let addressData = updatedAddresses || [];
1563
+
1564
+ if (!addressData || addressData?.length === 0) {
1565
+ const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
1566
+ const displayOptions = Array.isArray(itemMemberObj.addressDisplayOption)
1567
+ ? itemMemberObj.addressDisplayOption
1568
+ : [];
1569
+
1570
+ addressData = addresses.map((address, index) => {
1571
+ const displayOption = displayOptions.find((opt) => opt.key === address.key);
1572
+ return {
1573
+ _id: address.key || `address_${index}`,
1574
+ address,
1575
+ isMain: displayOption?.isMain || false,
1576
+ addressStatus: address.addressStatus || FALLBACK_ADDRESS_STATUS,
1577
+ isNewAddress: false,
1578
+ };
1579
+ });
1580
+ }
1581
+
1582
+ const repeater = $w('#addressesList');
1583
+ repeater.data = addressData;
1584
+
1585
+ updateAddressAddButtonState();
1586
+ }
1587
+
1588
+ function updateAddressAddButtonState() {
1589
+ const currentData = $w('#addressesList').data || [];
1590
+ const newAddressButton = $w('#newAddressButton');
1591
+
1592
+ if (currentData.length >= MAX_ADDRESSES_COUNT) {
1593
+ newAddressButton.disable();
1594
+ } else {
1595
+ newAddressButton.enable();
1596
+ }
1597
+ }
1598
+
1599
+ function updateMainAddressSelection(selectedId) {
1600
+ if (!itemMemberObj.addressDisplayOption) {
1601
+ itemMemberObj.addressDisplayOption = [];
1602
+ }
1603
+
1604
+ itemMemberObj.addressDisplayOption.forEach((option) => {
1605
+ option.isMain = false;
1606
+ });
1607
+
1608
+ const selectedOption = itemMemberObj.addressDisplayOption.find((opt) => opt.key === selectedId);
1609
+
1610
+ selectedOption.isMain = true;
1611
+ }
1612
+
1613
+ function updateAddressStatus(addressId, newStatus) {
1614
+ const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
1615
+ const addressIndex = addresses.findIndex(
1616
+ (addr) => (addr.key || `address_${addresses.indexOf(addr)}`) === addressId
1617
+ );
1618
+
1619
+ if (addressIndex !== -1) {
1620
+ itemMemberObj.addresses[addressIndex].addressStatus = newStatus;
1621
+ }
1622
+ }
1623
+
1624
+ function updateAddress(addressId, newAddressValue) {
1625
+ const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
1626
+ const addressIndex = addresses.findIndex(
1627
+ (addr) => (addr.key || `address_${addresses.indexOf(addr)}`) === addressId
1628
+ );
1629
+
1630
+ if (addressIndex !== -1) {
1631
+ itemMemberObj.addresses[addressIndex] = {
1632
+ ...itemMemberObj.addresses[addressIndex],
1633
+ ...newAddressValue,
1634
+ };
1635
+
1636
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1637
+ }
1638
+ }
1639
+
1640
+ function removeAddress(addressId) {
1641
+ if (itemMemberObj.addresses) {
1642
+ itemMemberObj.addresses = itemMemberObj.addresses.filter(
1643
+ (addr) => (addr.key || `address_${itemMemberObj.addresses.indexOf(addr)}`) !== addressId
1644
+ );
1645
+ }
1646
+
1647
+ if (itemMemberObj.addressDisplayOption) {
1648
+ itemMemberObj.addressDisplayOption = itemMemberObj.addressDisplayOption.filter((opt) => opt.key !== addressId);
1649
+ }
1650
+
1651
+ renderAddressesList();
1652
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1653
+ }
1654
+
1655
+ function formatFullAddress(addr) {
1656
+ if (!addr) return '';
1657
+
1658
+ const parts = [];
1659
+
1660
+ if (addr.line1) parts.push(addr.line1);
1661
+ if (addr.city) parts.push(addr.city);
1662
+ if (addr.state && addr.postalcode) {
1663
+ parts.push(`${addr.state} ${addr.postalcode}`);
1664
+ } else if (addr.state) {
1665
+ parts.push(addr.state);
1666
+ } else if (addr.postalcode) {
1667
+ parts.push(addr.postalcode);
1668
+ }
1669
+
1670
+ return parts.join(', ') || 'No address entered';
1671
+ }
1672
+
1673
+ function setupPhoneRepeater() {
1674
+ $w('#phoneNumbersList').onItemReady(handlePhoneItem);
1675
+ renderPhonesList();
1676
+
1677
+ $w('#addPhoneButton').onClick(addNewPhone);
1678
+
1679
+ setupPhoneRepeaterEventListeners();
1680
+ }
1681
+
1682
+ function setupPhoneRepeaterEventListeners() {
1683
+ $w('#phoneInput').onInput((event) => {
1684
+ const data = $w('#phoneNumbersList').data;
1685
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1686
+ const phoneValue = event.target.value;
1687
+
1688
+ updatePhoneNumber(clickedItemData._id, phoneValue);
1689
+
1690
+ if (clickedItemData.isNewPhone && phoneValue.trim()) {
1691
+ addNewPhoneToData(clickedItemData._id, phoneValue.trim());
1692
+ clickedItemData.isNewPhone = false;
1693
+ }
1694
+
1695
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1696
+ });
1697
+
1698
+ $w('#showPhoneCheckbox').onChange((event) => {
1699
+ const data = $w('#phoneNumbersList').data;
1700
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1701
+ const $item = $w.at(event.context);
1702
+
1703
+ $w('#showPhoneCheckbox').checked = false;
1704
+ $item('#showPhoneCheckbox').checked = true;
1705
+
1706
+ updateShowPhoneSelection(clickedItemData._id, event.target.checked);
1707
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1708
+ });
1709
+
1710
+ $w('#removePhoneBtn').onClick(async (event) => {
1711
+ const data = $w('#phoneNumbersList').data;
1712
+ const clickedItemData = data.find((item) => item._id === event.context.itemId);
1713
+ const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
1714
+ if (result && result.toDelete) {
1715
+ removePhone(clickedItemData._id);
1716
+ }
1717
+ });
1718
+ }
1719
+
1720
+ function addNewPhone() {
1721
+ const currentData = $w('#phoneNumbersList').data || [];
1722
+
1723
+ if (currentData.length >= MAX_PHONES_COUNT) {
1724
+ return;
1725
+ }
1726
+
1727
+ const newPhoneId = generateId();
1728
+ const newPhoneItem = {
1729
+ _id: newPhoneId,
1730
+ phoneNumber: '',
1731
+ showPhone: false,
1732
+ isNewPhone: true,
1733
+ phoneIndex: currentData.length + 1,
1734
+ };
1735
+
1736
+ renderPhonesList([...currentData, newPhoneItem]);
1737
+ }
1738
+
1739
+ function handlePhoneItem($item, itemData) {
1740
+ $item('#phoneInput').value = itemData.phoneNumber || '';
1741
+ $item('#showPhoneCheckbox').checked = itemData.showPhone || false;
1742
+ $item('#phoneNumberLabel').text = `Phone ${itemData.phoneIndex}`;
1743
+ }
1744
+
1745
+ function renderPhonesList(updatedPhones) {
1746
+ let phoneData = updatedPhones || [];
1747
+
1748
+ if (!phoneData || phoneData?.length === 0) {
1749
+ const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
1750
+
1751
+ phoneData = phones.map((phone, index) => ({
1752
+ _id: `phone_${index}`,
1753
+ phoneNumber: phone,
1754
+ showPhone: phone === itemMemberObj.toShowPhone,
1755
+ isNewPhone: false,
1756
+ phoneIndex: index + 1,
1757
+ }));
1758
+ }
1759
+
1760
+ const repeater = $w('#phoneNumbersList');
1761
+
1762
+ repeater.data = phoneData;
1763
+ updatePhoneAddButtonState();
1764
+ }
1765
+
1766
+ function updatePhoneAddButtonState() {
1767
+ const currentData = $w('#phoneNumbersList').data || [];
1768
+ const addPhoneButton = $w('#addPhoneButton');
1769
+
1770
+ if (currentData.length >= MAX_PHONES_COUNT) {
1771
+ addPhoneButton.disable();
1772
+ } else {
1773
+ addPhoneButton.enable();
1774
+ }
1775
+ }
1776
+
1777
+ function updatePhoneNumber(phoneId, newPhoneNumber) {
1778
+ const currentData = $w('#phoneNumbersList').data || [];
1779
+ const itemIndex = currentData.findIndex((item) => item._id === phoneId);
1780
+
1781
+ if (itemIndex !== -1) {
1782
+ currentData[itemIndex].phoneNumber = newPhoneNumber;
1783
+ renderPhonesList(currentData);
1784
+ syncPhonesFromRepeater();
1785
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1786
+ }
1787
+ }
1788
+
1789
+ function syncPhonesFromRepeater() {
1790
+ const phoneData = $w('#phoneNumbersList').data || [];
1791
+ itemMemberObj.phones = phoneData
1792
+ .filter((item) => !item.isNewPhone && item.phoneNumber.trim())
1793
+ .map((item) => item.phoneNumber);
1794
+ }
1795
+
1796
+ function addNewPhoneToData(phoneId, phoneNumber) {
1797
+ if (!itemMemberObj.phones) {
1798
+ itemMemberObj.phones = [];
1799
+ }
1800
+
1801
+ itemMemberObj.phones.push(phoneNumber);
1802
+ renderPhonesList();
1803
+ checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
1804
+ }
1805
+
1806
+ function removePhone(phoneId) {
1807
+ const currentData = $w('#phoneNumbersList').data || [];
1808
+ const phoneToRemove = currentData.find((item) => item._id === phoneId);
1809
+
1810
+ if (phoneToRemove) {
1811
+ if (itemMemberObj.toShowPhone === phoneToRemove.phoneNumber) {
1812
+ itemMemberObj.toShowPhone = null;
1813
+ }
1814
+
1815
+ const updatedData = currentData.filter((item) => item._id !== phoneId);
1816
+ renderPhonesList(updatedData);
1817
+ }
1818
+ }
1819
+
1820
+ function updateShowPhoneSelection(phoneId, isVisible) {
1821
+ const currentData = $w('#phoneNumbersList').data || [];
1822
+ const selectedItem = currentData.find((item) => item._id === phoneId);
1823
+
1824
+ if (selectedItem && selectedItem.phoneNumber) {
1825
+ if (isVisible) {
1826
+ itemMemberObj.toShowPhone = selectedItem.phoneNumber;
1827
+ } else {
1828
+ itemMemberObj.toShowPhone = null;
1829
+ }
1830
+ }
1831
+ }
1832
+
1833
+ function getToShowPhone() {
1834
+ return itemMemberObj.toShowPhone || null;
1835
+ }
1836
+
1837
+ function getContactAndBookingData() {
1838
+ const showWixUrl = $w('#showUrlWixCheckbox').checked;
1839
+ const showExistingUrl = $w('#showExsistingUrlCheckbox').checked;
1840
+
1841
+ const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
1842
+ const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
1843
+
1844
+ return {
1845
+ showContactForm: $w('#showCotactFormCheckbox').checked,
1846
+ contactFormEmail: $w('#contactFormEmailInput').value,
1847
+ toShowPhone: getToShowPhone(),
1848
+ bookingUrl: $w('#schedulingLinkInput').value,
1849
+ website: $w('#UrlInput').value,
1850
+ showWebsite: showExistingUrl,
1851
+ showWixUrl,
1852
+ addresses,
1853
+ addressDisplayOption: itemMemberObj.addressDisplayOption || [],
1854
+ phones,
1855
+ };
1856
+ }
1857
+ async function saveContactBooking() {
1858
+ // if showWixUrl value changes then update optWebsiteCheckbox value
1859
+ $w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
1860
+
1861
+ const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
1862
+ const contactChanges = getContactAndBookingData();
1863
+
1864
+ const formData = {
1865
+ ...itemMemberObj,
1866
+ ...contactChanges,
1867
+ };
1868
+
1869
+ // Log the specific contact & booking data changes
1870
+ console.group('Contact & Booking Save Attempt');
1871
+ console.log('Current Data:', beforeData);
1872
+ console.log('Changes Being Applied:', contactChanges);
1873
+ console.log('Final Form Data:', formData);
1874
+ console.log('Address Changes:', {
1875
+ addressCount: contactChanges.addresses?.length || 0,
1876
+ addressDisplayOptions: contactChanges.addressDisplayOption,
1877
+ });
1878
+ console.log('Phone Selection:', contactChanges.toShowPhone);
1879
+ console.groupEnd();
1880
+
1881
+ const result = await saveData(formData);
1882
+ formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section] = false;
1883
+ handleSaveDataFeedback($w('#contactMessage'), result.message);
1884
+ }
1885
+
1886
+ function handleSaveDataFeedback($messageElement, message) {
1887
+ $messageElement.text = message;
1888
+ $messageElement.expand();
1889
+ setTimeout(() => {
1890
+ $messageElement.collapse();
1891
+ }, 5000);
1892
+ }
1893
+
1894
+ function setGallery() {
1895
+ // Initialize gallery array if it doesn't exist
1896
+ if (!itemMemberObj.gallery) {
1897
+ itemMemberObj.gallery = [];
1898
+ }
1899
+ const gallery = itemMemberObj.gallery;
1900
+
1901
+ const galleryData = buildGalleryData(gallery);
1902
+
1903
+ $w('#galleryRepeater').data = galleryData;
1904
+ }
1905
+
1906
+ function buildGalleryData(gallery) {
1907
+ return [
1908
+ { _id: 'add-item', isAdd: true },
1909
+ ...gallery.map((image) => ({
1910
+ _id: generateId(),
1911
+ image,
1912
+ isAdd: false,
1913
+ })),
1914
+ ];
1915
+ }
1916
+
1917
+ function handleGalleryItem($item, itemData) {
1918
+ const multiStateBox = $item('#galleryMSB');
1919
+
1920
+ if (itemData.isAdd) {
1921
+ setupAddImageState($item, multiStateBox);
1922
+ } else {
1923
+ setupImageState($item, itemData, multiStateBox);
1924
+ }
1925
+ }
1926
+
1927
+ function setupAddImageState($item, multiStateBox) {
1928
+ multiStateBox.changeState(GALLERY_STATES.ADD);
1929
+ }
1930
+
1931
+ function setupImageState($item, itemData, multiStateBox) {
1932
+ multiStateBox.changeState(GALLERY_STATES.VIEW);
1933
+ $item('#galleryImage').src = itemData.image.src;
1934
+ }
1935
+
1936
+ async function saveGalleryToCMS() {
1937
+ const formData = {
1938
+ ...itemMemberObj,
1939
+ gallery: itemMemberObj.gallery,
1940
+ };
1941
+
1942
+ await saveData(formData);
1943
+ }
1944
+
1945
+ function formatFileName(fullName, maxBaseLength = 23) {
1946
+ const dotIndex = fullName.lastIndexOf('.');
1947
+ if (dotIndex === -1 || fullName.length <= maxBaseLength + 4) return fullName;
1948
+
1949
+ const name = fullName.slice(0, dotIndex);
1950
+ const ext = fullName.slice(dotIndex);
1951
+ return `${name.slice(0, maxBaseLength)}...${ext}`;
1952
+ }
1953
+
1954
+ async function filterInterests(searchValue) {
1955
+ const container = $w('#containerRepeaterInterest');
1956
+ const repeater = $w('#repeaterInterest');
1957
+
1958
+ // if (!searchValue) {
1959
+ // container.collapse();
1960
+ // repeater.data = [];
1961
+ // return [];
1962
+ // }
1963
+
1964
+ const allInterests = await getInterestAll();
1965
+ const filtered = allInterests
1966
+ .filter((val) => val.toLowerCase().includes(searchValue))
1967
+ .map((val) => ({ _id: generateId(), value: val }));
1968
+
1969
+ if (filtered.length > 0) {
1970
+ repeater.data = filtered;
1971
+ container.expand();
1972
+ } else {
1973
+ repeater.data = [];
1974
+ container.collapse();
1975
+ }
1976
+
1977
+ return filtered;
1978
+ }
1979
+
1980
+ const debounce_fun = _.debounce(async function () {
1981
+ const searchValue = $w('#intrestInput').value.trim().toLowerCase();
1982
+ await filterInterests(searchValue);
1983
+ }, 250);
1984
+ }
1985
+
1986
+ module.exports = {
1987
+ personalDetailsFormOnReady,
1988
+ };
1989
+