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