abmp-npm 2.0.76 → 2.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -1
- package/pages/Home.js +27 -55
- package/pages/index.js +0 -1
- package/public/Utils/personalDetailsUtils.js +5 -1
- package/public/Utils/sharedUtils.js +32 -0
- package/public/consts.js +0 -1
- package/.claude/worktrees/fix-save-button-disable/.husky/pre-commit +0 -19
- package/.claude/worktrees/fix-save-button-disable/.prettierignore +0 -7
- package/.claude/worktrees/fix-save-button-disable/.prettierrc.json +0 -16
- package/.claude/worktrees/fix-save-button-disable/README.md +0 -1
- package/.claude/worktrees/fix-save-button-disable/backend/README-member-contact-flows.md +0 -136
- package/.claude/worktrees/fix-save-button-disable/backend/__tests__/daily-pull-execution-check.test.js +0 -124
- package/.claude/worktrees/fix-save-button-disable/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
- package/.claude/worktrees/fix-save-button-disable/backend/__tests__/login-email-sync.test.js +0 -49
- package/.claude/worktrees/fix-save-button-disable/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
- package/.claude/worktrees/fix-save-button-disable/backend/__tests__/url-uniqueness.test.js +0 -194
- package/.claude/worktrees/fix-save-button-disable/backend/automations-methods.js +0 -13
- package/.claude/worktrees/fix-save-button-disable/backend/cms-data-methods.js +0 -251
- package/.claude/worktrees/fix-save-button-disable/backend/consts.js +0 -58
- package/.claude/worktrees/fix-save-button-disable/backend/contacts-methods.js +0 -67
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/bulk-process-methods.js +0 -222
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/consts.js +0 -39
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/index.js +0 -5
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/process-member-methods.js +0 -213
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/schedule-methods.js +0 -41
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/sync-to-cms-methods.js +0 -161
- package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/utils.js +0 -151
- package/.claude/worktrees/fix-save-button-disable/backend/data-hooks.js +0 -29
- package/.claude/worktrees/fix-save-button-disable/backend/dev-only-methods.js +0 -58
- package/.claude/worktrees/fix-save-button-disable/backend/elevated-modules.js +0 -18
- package/.claude/worktrees/fix-save-button-disable/backend/forms-methods.js +0 -52
- package/.claude/worktrees/fix-save-button-disable/backend/http-functions/httpFunctions.js +0 -86
- package/.claude/worktrees/fix-save-button-disable/backend/http-functions/index.js +0 -3
- package/.claude/worktrees/fix-save-button-disable/backend/http-functions/interests.js +0 -35
- package/.claude/worktrees/fix-save-button-disable/backend/index.js +0 -19
- package/.claude/worktrees/fix-save-button-disable/backend/jobs.js +0 -158
- package/.claude/worktrees/fix-save-button-disable/backend/login/generate-member-session-token.js +0 -27
- package/.claude/worktrees/fix-save-button-disable/backend/login/index.js +0 -8
- package/.claude/worktrees/fix-save-button-disable/backend/login/qa-login-methods.js +0 -54
- package/.claude/worktrees/fix-save-button-disable/backend/login/sso-methods.js +0 -159
- package/.claude/worktrees/fix-save-button-disable/backend/member-contact-orchestration.js +0 -111
- package/.claude/worktrees/fix-save-button-disable/backend/members-area-methods.js +0 -88
- package/.claude/worktrees/fix-save-button-disable/backend/members-data-methods.js +0 -789
- package/.claude/worktrees/fix-save-button-disable/backend/pac-api-methods.js +0 -54
- package/.claude/worktrees/fix-save-button-disable/backend/routers/index.js +0 -3
- package/.claude/worktrees/fix-save-button-disable/backend/routers/methods.js +0 -191
- package/.claude/worktrees/fix-save-button-disable/backend/routers/utils.js +0 -128
- package/.claude/worktrees/fix-save-button-disable/backend/search-filters-methods.js +0 -124
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-primary-methods.js +0 -256
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-visibility-methods.js +0 -160
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/consts.js +0 -34
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/daily-pull-check-methods.js +0 -78
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/email-normalize-methods.js +0 -134
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/index.js +0 -10
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/migration-methods.js +0 -46
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-configs.js +0 -251
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-helpers-methods.js +0 -433
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-process-methods.js +0 -606
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-migration-methods.js +0 -378
- package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-space-fix-methods.js +0 -174
- package/.claude/worktrees/fix-save-button-disable/backend/utils.js +0 -277
- package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
- package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-ids.js +0 -159
- package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-urls.js +0 -201
- package/.claude/worktrees/fix-save-button-disable/eslint.config.js +0 -120
- package/.claude/worktrees/fix-save-button-disable/index.js +0 -5
- package/.claude/worktrees/fix-save-button-disable/package-lock.json +0 -12160
- package/.claude/worktrees/fix-save-button-disable/package.json +0 -60
- package/.claude/worktrees/fix-save-button-disable/pages/ContactUs.js +0 -129
- package/.claude/worktrees/fix-save-button-disable/pages/Home.js +0 -779
- package/.claude/worktrees/fix-save-button-disable/pages/LearnMore.js +0 -27
- package/.claude/worktrees/fix-save-button-disable/pages/LoadingPage.js +0 -29
- package/.claude/worktrees/fix-save-button-disable/pages/Profile.js +0 -354
- package/.claude/worktrees/fix-save-button-disable/pages/QAPage.js +0 -39
- package/.claude/worktrees/fix-save-button-disable/pages/SaveAlerts.js +0 -13
- package/.claude/worktrees/fix-save-button-disable/pages/SelectBannerImages.js +0 -46
- package/.claude/worktrees/fix-save-button-disable/pages/deleteConfirm.js +0 -19
- package/.claude/worktrees/fix-save-button-disable/pages/index.js +0 -12
- package/.claude/worktrees/fix-save-button-disable/pages/personalDetails.js +0 -2486
- package/.claude/worktrees/fix-save-button-disable/public/Utils/homePage.js +0 -798
- package/.claude/worktrees/fix-save-button-disable/public/Utils/personalDetailsUtils.js +0 -25
- package/.claude/worktrees/fix-save-button-disable/public/Utils/sharedUtils.js +0 -235
- package/.claude/worktrees/fix-save-button-disable/public/consts.js +0 -113
- package/.claude/worktrees/fix-save-button-disable/public/index.js +0 -5
- package/.claude/worktrees/fix-save-button-disable/public/messages.js +0 -16
- package/.claude/worktrees/fix-save-button-disable/public/sso-auth-methods.js +0 -43
- package/.husky/pre-commit +0 -19
- package/.prettierignore +0 -7
- package/.prettierrc.json +0 -16
- package/backend/__tests__/daily-pull-execution-check.test.js +0 -124
- package/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
- package/backend/__tests__/login-email-sync.test.js +0 -49
- package/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
- package/backend/__tests__/url-uniqueness.test.js +0 -194
- package/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
- package/dev-only-scripts/find-duplicate-ids.js +0 -159
- package/dev-only-scripts/find-duplicate-urls.js +0 -201
- package/eslint.config.js +0 -120
- package/pages/contactForLocation.js +0 -28
|
@@ -1,2486 +0,0 @@
|
|
|
1
|
-
const { location: wixLocation } = require('@wix/site-location');
|
|
2
|
-
const { window: wixWindow } = require('@wix/site-window');
|
|
3
|
-
const _ = require('lodash');
|
|
4
|
-
|
|
5
|
-
const {
|
|
6
|
-
ADDRESS_STATUS_TYPES,
|
|
7
|
-
DEFAULT_BUSINESS_NAME_TEXT,
|
|
8
|
-
FREE_WEBSITE_TEXT_STATES,
|
|
9
|
-
LIGHTBOX_NAMES,
|
|
10
|
-
} = require('../public/consts');
|
|
11
|
-
const { handleOnCustomValidation, isNotValidUrl } = require('../public/Utils/personalDetailsUtils');
|
|
12
|
-
const {
|
|
13
|
-
generateId,
|
|
14
|
-
isWixHostedImage,
|
|
15
|
-
normalizeExternalUrl,
|
|
16
|
-
} = require('../public/Utils/sharedUtils');
|
|
17
|
-
|
|
18
|
-
const MAX_PHONES_COUNT = 10;
|
|
19
|
-
const MAX_ADDRESSES_COUNT = 10;
|
|
20
|
-
|
|
21
|
-
/** US 10-digit NANP: strip non-digits; drop leading 1 from 11-digit input. */
|
|
22
|
-
function digitsFromPhoneInput(value) {
|
|
23
|
-
if (!value || typeof value !== 'string') return '';
|
|
24
|
-
let d = value.replace(/\D/g, '');
|
|
25
|
-
if (d.length === 11 && d[0] === '1') d = d.slice(1);
|
|
26
|
-
return d.slice(0, 10);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Format as (###) ###-#### while typing. */
|
|
30
|
-
function formatUsPhoneInput(digits) {
|
|
31
|
-
if (!digits) return '';
|
|
32
|
-
const d = digits.slice(0, 10);
|
|
33
|
-
if (d.length <= 3) return `(${d}`;
|
|
34
|
-
if (d.length <= 6) return `(${d.slice(0, 3)}) ${d.slice(3)}`;
|
|
35
|
-
return `(${d.slice(0, 3)}) ${d.slice(3, 6)}-${d.slice(6)}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function formatPhoneNumberForInput(value) {
|
|
39
|
-
return formatUsPhoneInput(digitsFromPhoneInput(value || ''));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function phoneDigitsEqual(a, b) {
|
|
43
|
-
const da = digitsFromPhoneInput(String(a || ''));
|
|
44
|
-
const db = digitsFromPhoneInput(String(b || ''));
|
|
45
|
-
return da.length > 0 && db.length > 0 && da === db;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const ADDRESS_STATES = {
|
|
49
|
-
VIEW: 'addressViewState',
|
|
50
|
-
EDIT: 'addressEditState',
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const TESTIMONIAL_STATES = {
|
|
54
|
-
VIEW: 'testimonialState',
|
|
55
|
-
ADD: 'addTestimonialState',
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const GALLERY_STATES = {
|
|
59
|
-
VIEW: 'imageState',
|
|
60
|
-
ADD: 'addImageState',
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const MAIN_STATE_BOX_STATES = {
|
|
64
|
-
FORM_STATE: 'formState',
|
|
65
|
-
UNAUTHORIZED_STATE: 'unauthorizedState',
|
|
66
|
-
ERROR_STATE: 'errorState',
|
|
67
|
-
LOADING_STATE: 'loading',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const FALLBACK_ADDRESS_STATUS = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
71
|
-
|
|
72
|
-
const PAGE_NAME = 'Personal Details';
|
|
73
|
-
const BUTTON_NAMES = {
|
|
74
|
-
PERSONAL: 'Save Personal Details',
|
|
75
|
-
BUSINESS: 'Save Business & Services',
|
|
76
|
-
CONTACT: 'Save Contact & Booking',
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const FORM_SECTION_HANDLER_MAP = {
|
|
80
|
-
PERSONAL: { section: 'personal', handler: null }, // handler will be set in init
|
|
81
|
-
BUSINESS_SERVICES: { section: 'businessServices', handler: null },
|
|
82
|
-
CONTACT_BOOKING: { section: 'contactBooking', handler: null },
|
|
83
|
-
DIRECTORY_OPT_OUT: { section: 'directoryOptOut', handler: null },
|
|
84
|
-
WEBSITE_OPT_OUT: { section: 'websiteOptOut', handler: null },
|
|
85
|
-
};
|
|
86
|
-
const ContactEmailValidity = {
|
|
87
|
-
valid: true,
|
|
88
|
-
validationMessage: '',
|
|
89
|
-
};
|
|
90
|
-
const CONTACT_EMAIL_VALIDATION_MESSAGES = {
|
|
91
|
-
REQUIRED: 'Email is required',
|
|
92
|
-
ALREADY_TAKEN: 'Email is already taken. Please choose a different one.',
|
|
93
|
-
ERROR: 'There was an error. Please try again.',
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
async function personalDetailsOnReady({
|
|
97
|
-
$w: _$w,
|
|
98
|
-
getInterestAll,
|
|
99
|
-
saveRegistrationData,
|
|
100
|
-
validateMemberToken,
|
|
101
|
-
checkUrlUniqueness,
|
|
102
|
-
isEmailAlreadyUsed,
|
|
103
|
-
trackClick,
|
|
104
|
-
}) {
|
|
105
|
-
let itemMemberObj = {};
|
|
106
|
-
let originalMemberData = {};
|
|
107
|
-
let selectedServices = [];
|
|
108
|
-
const uploadedImages = {
|
|
109
|
-
profileImage: '',
|
|
110
|
-
logoImage: '',
|
|
111
|
-
bannerImage: '',
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const formHasUnsavedChanges = {
|
|
115
|
-
[FORM_SECTION_HANDLER_MAP.PERSONAL.section]: false,
|
|
116
|
-
[FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: false,
|
|
117
|
-
[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: false,
|
|
118
|
-
[FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.section]: false,
|
|
119
|
-
[FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.section]: false,
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const slugValidationTimeout = {};
|
|
123
|
-
const contactEmailValidationTimeout = {};
|
|
124
|
-
let isSlugValid = true;
|
|
125
|
-
let currentSlugValidationId = 0;
|
|
126
|
-
let currentContactEmailValidationId = 0;
|
|
127
|
-
|
|
128
|
-
const SLUG_FLAGS = {
|
|
129
|
-
VALID: '#validSlugFlag',
|
|
130
|
-
INVALID: '#invalidSlugFlag',
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const SLUG_MESSAGES = {
|
|
134
|
-
INVALID_FORMAT: 'Enter a valid URL. You can use letters, numbers or dashes.',
|
|
135
|
-
TAKEN: 'Enter a new URL slug. This one is already taken.',
|
|
136
|
-
ERROR: 'There was an error. Please try again.',
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
_$w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.LOADING_STATE);
|
|
140
|
-
|
|
141
|
-
// Set up handler functions with access to closures
|
|
142
|
-
FORM_SECTION_HANDLER_MAP.PERSONAL.handler = () => checkPersonalDataChanged();
|
|
143
|
-
FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.handler = () => checkBusinessDataChanged();
|
|
144
|
-
FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.handler = () => checkContactDataChanged();
|
|
145
|
-
FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.handler = () => checkDirectoryOptOutDataChanged();
|
|
146
|
-
FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.handler = () => checkWebsiteOptOutDataChanged();
|
|
147
|
-
|
|
148
|
-
const showUnauthorizedState = () => {
|
|
149
|
-
console.log('❌ Unauthorized');
|
|
150
|
-
_$w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.UNAUTHORIZED_STATE);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
let memberData, isValid, isStudent, membersExternalPortalUrl;
|
|
154
|
-
|
|
155
|
-
// Main initialization
|
|
156
|
-
const queryParams = await wixLocation.query();
|
|
157
|
-
const memberTokenId = queryParams.token;
|
|
158
|
-
const baseUrl = await wixLocation.baseUrl();
|
|
159
|
-
|
|
160
|
-
console.log('memberTokenId', memberTokenId);
|
|
161
|
-
|
|
162
|
-
if (!memberTokenId) {
|
|
163
|
-
showUnauthorizedState();
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
const {
|
|
169
|
-
memberData: { isStudent: _isStudent, ...memberDataResponse },
|
|
170
|
-
isValid: isValidResponse,
|
|
171
|
-
membersExternalPortalUrl: _membersExternalPortalUrl,
|
|
172
|
-
} = await validateMemberToken(memberTokenId);
|
|
173
|
-
memberData = memberDataResponse;
|
|
174
|
-
isValid = isValidResponse;
|
|
175
|
-
isStudent = _isStudent;
|
|
176
|
-
membersExternalPortalUrl = _membersExternalPortalUrl;
|
|
177
|
-
} catch (error) {
|
|
178
|
-
console.error(`Error in validateMemberToken memberTokenId : ${memberTokenId}`, error);
|
|
179
|
-
_$w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.ERROR_STATE);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.log('memberData frontend', memberData);
|
|
184
|
-
if (!isValid) {
|
|
185
|
-
showUnauthorizedState();
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
console.log('✅ Authorized 2', { memberTokenId });
|
|
190
|
-
_$w('#loginButton2').hide();
|
|
191
|
-
_$w('#goBackButton').show();
|
|
192
|
-
|
|
193
|
-
_$w('#goBackButton').onClick(async () => {
|
|
194
|
-
try {
|
|
195
|
-
const isFormHasUnsavedChanges = Object.values(formHasUnsavedChanges).some(Boolean);
|
|
196
|
-
if (isFormHasUnsavedChanges) {
|
|
197
|
-
wixWindow.openLightbox(LIGHTBOX_NAMES.SAVE_ALERT, {
|
|
198
|
-
membersExternalPortalUrl,
|
|
199
|
-
});
|
|
200
|
-
} else {
|
|
201
|
-
await wixLocation.to(membersExternalPortalUrl);
|
|
202
|
-
}
|
|
203
|
-
} catch (error) {
|
|
204
|
-
console.error('Logout failed:', error);
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
itemMemberObj = memberData;
|
|
208
|
-
originalMemberData = JSON.parse(JSON.stringify(memberData));
|
|
209
|
-
// Initialize selectedServices based on memberData
|
|
210
|
-
selectedServices = Array.isArray(itemMemberObj.areasOfPractices)
|
|
211
|
-
? itemMemberObj.areasOfPractices.map(label => ({ _id: generateId(), label: String(label) }))
|
|
212
|
-
: [];
|
|
213
|
-
|
|
214
|
-
_$w('#mainMultiStateBox').changeState(MAIN_STATE_BOX_STATES.FORM_STATE);
|
|
215
|
-
init();
|
|
216
|
-
setupStepTrackingWrapper();
|
|
217
|
-
//initially disable save buttons
|
|
218
|
-
_$w('#savePersonalButton').disable();
|
|
219
|
-
_$w('#saveBusinessButton').disable();
|
|
220
|
-
_$w('#saveContactBookingButton').disable();
|
|
221
|
-
onFormDataChanged();
|
|
222
|
-
|
|
223
|
-
function setupStepTrackingWrapper() {
|
|
224
|
-
const stepPairs = [
|
|
225
|
-
{
|
|
226
|
-
mark: '#personalDetailsMark',
|
|
227
|
-
sing: '#personalDetailsSign',
|
|
228
|
-
step: '#personalDetailsStep',
|
|
229
|
-
text: '#personalDetailsText',
|
|
230
|
-
css: 'personal',
|
|
231
|
-
vector: '#personalDetailsVector',
|
|
232
|
-
},
|
|
233
|
-
{
|
|
234
|
-
mark: '#businessServicesMark',
|
|
235
|
-
sing: '#businessServicesSign',
|
|
236
|
-
step: '#businessServicesStep',
|
|
237
|
-
text: '#businessServicesText',
|
|
238
|
-
css: 'business',
|
|
239
|
-
vector: '#businessServicesVector',
|
|
240
|
-
},
|
|
241
|
-
{
|
|
242
|
-
mark: '#contactMark',
|
|
243
|
-
sing: '#contactSign',
|
|
244
|
-
step: '#contactStep',
|
|
245
|
-
text: '#contactText',
|
|
246
|
-
css: 'contact',
|
|
247
|
-
vector: '#contactVector',
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
mark: '#galleryMark',
|
|
251
|
-
sing: '#gallerySign',
|
|
252
|
-
step: '#galleryStep',
|
|
253
|
-
text: '#galleryText',
|
|
254
|
-
css: 'gallery',
|
|
255
|
-
vector: '#galleryVector',
|
|
256
|
-
},
|
|
257
|
-
];
|
|
258
|
-
|
|
259
|
-
setupStepTracking(stepPairs);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function setupStepTracking(stepPairs) {
|
|
263
|
-
stepPairs.forEach(({ mark, sing, step, text, css, vector }) => {
|
|
264
|
-
_$w(mark).onViewportEnter(() => {
|
|
265
|
-
_$w(sing).customClassList.add('current-step');
|
|
266
|
-
_$w(step).customClassList.add('highlighted-text');
|
|
267
|
-
_$w(text).customClassList.add('highlighted-text');
|
|
268
|
-
_$w(vector).customClassList.add('disabeld-step');
|
|
269
|
-
_$w('#accordion').customClassList.add(css);
|
|
270
|
-
_$w(mark).scrollTo();
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
_$w(mark).onViewportLeave(() => {
|
|
274
|
-
_$w(sing).customClassList.remove('current-step');
|
|
275
|
-
_$w(step).customClassList.remove('highlighted-text');
|
|
276
|
-
_$w(text).customClassList.remove('highlighted-text');
|
|
277
|
-
_$w(vector).customClassList.remove('disabeld-step');
|
|
278
|
-
_$w('#accordion').customClassList.remove(css);
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function init() {
|
|
284
|
-
const fullProfilePageLink = `${baseUrl}/profile/${itemMemberObj.url}`;
|
|
285
|
-
setPersonalDetails(fullProfilePageLink);
|
|
286
|
-
setBusinessServices();
|
|
287
|
-
setContactBooking(fullProfilePageLink);
|
|
288
|
-
initGallery();
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function initGallery() {
|
|
292
|
-
_$w('#galleryRepeater').onItemReady(handleGalleryItem);
|
|
293
|
-
_$w('#uploadGalleryImageButton').onChange(async event => {
|
|
294
|
-
const $item = _$w.at(event.context);
|
|
295
|
-
const uploadButton = $item('#uploadGalleryImageButton');
|
|
296
|
-
if (uploadButton.value.length === 0) return;
|
|
297
|
-
try {
|
|
298
|
-
const uploadedFiles = await uploadButton.uploadFiles();
|
|
299
|
-
// Initialize gallery array if it doesn't exist
|
|
300
|
-
if (!itemMemberObj.gallery) {
|
|
301
|
-
itemMemberObj.gallery = [];
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
uploadedFiles.forEach(file => {
|
|
305
|
-
itemMemberObj.gallery.unshift({ src: file.fileUrl });
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
await saveGalleryToCMS();
|
|
309
|
-
setGallery();
|
|
310
|
-
} catch (error) {
|
|
311
|
-
_$w('#uploadFailedText').expand();
|
|
312
|
-
setTimeout(() => {
|
|
313
|
-
_$w('#uploadFailedText').collapse();
|
|
314
|
-
}, 5000);
|
|
315
|
-
console.error('Upload failed:', error);
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
_$w('#deleteImageButton').onClick(async event => {
|
|
319
|
-
const itemId = event.context.itemId;
|
|
320
|
-
const itemData = _$w('#galleryRepeater').data.find(item => item._id === itemId);
|
|
321
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
322
|
-
if (result && result.toDelete) {
|
|
323
|
-
itemMemberObj.gallery = itemMemberObj.gallery.filter(img => img.src !== itemData.image.src);
|
|
324
|
-
await saveGalleryToCMS();
|
|
325
|
-
setGallery(); // Re-render
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
//
|
|
329
|
-
setGallery();
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function setPersonalDetails(fullProfilePageLink) {
|
|
333
|
-
_$w('#firstNameInput').value = itemMemberObj.firstName || '';
|
|
334
|
-
_$w('#lastNameInput').value = itemMemberObj.lastName || '';
|
|
335
|
-
|
|
336
|
-
_$w('#slugInput').value = itemMemberObj.url || '';
|
|
337
|
-
|
|
338
|
-
isSlugValid = true;
|
|
339
|
-
|
|
340
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
341
|
-
_$w(SLUG_FLAGS.INVALID).collapse();
|
|
342
|
-
|
|
343
|
-
_$w('#profileLink').text = fullProfilePageLink;
|
|
344
|
-
_$w('#profileLink').link = fullProfilePageLink;
|
|
345
|
-
_$w('#profileLink').target = '_blank';
|
|
346
|
-
_$w('#licenceNoText').text = (itemMemberObj.licenses || [])
|
|
347
|
-
.map(val => val.license)
|
|
348
|
-
.filter(Boolean)
|
|
349
|
-
.join(', ');
|
|
350
|
-
|
|
351
|
-
const handleIsStudent = () => {
|
|
352
|
-
if (isStudent) {
|
|
353
|
-
_$w('#optCheckbox').disable();
|
|
354
|
-
_$w('#optCheckbox').checked = false;
|
|
355
|
-
_$w('#optCheckbox').customClassList.add('disabled-text');
|
|
356
|
-
_$w('#optCheckbox').customClassList.add('disabled-checkbox');
|
|
357
|
-
} else {
|
|
358
|
-
_$w('#optCheckbox').enable();
|
|
359
|
-
_$w('#optCheckbox').checked = !itemMemberObj.optOut;
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
handleIsStudent();
|
|
364
|
-
_$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
|
|
365
|
-
handleOptWebsiteCheckboxEnable(itemMemberObj.showWebsite);
|
|
366
|
-
toggleFreeWebsiteText(itemMemberObj.showWixUrl);
|
|
367
|
-
|
|
368
|
-
setupOptOutCheckbox(
|
|
369
|
-
'#optCheckbox',
|
|
370
|
-
'#optConfirmationBox',
|
|
371
|
-
'#yesOptButton',
|
|
372
|
-
'#cancelOptButton',
|
|
373
|
-
confirmed => handleOptConfirmation(confirmed, '#optCheckbox', '#optConfirmationBox', 'optOut')
|
|
374
|
-
);
|
|
375
|
-
|
|
376
|
-
setupOptOutCheckbox(
|
|
377
|
-
'#optWebsiteCheckbox',
|
|
378
|
-
'#optWebsiteConfirmationBox',
|
|
379
|
-
'#yesOptWebsiteButton',
|
|
380
|
-
'#cancelOptwebsiteButton',
|
|
381
|
-
confirmed =>
|
|
382
|
-
handleOptConfirmation(
|
|
383
|
-
confirmed,
|
|
384
|
-
'#optWebsiteCheckbox',
|
|
385
|
-
'#optWebsiteConfirmationBox',
|
|
386
|
-
'showWixUrl'
|
|
387
|
-
)
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
function handleOptWebsiteCheckboxEnable(showExistingWebsite) {
|
|
392
|
-
if (showExistingWebsite) {
|
|
393
|
-
_$w('#optWebsiteCheckbox').checked = false;
|
|
394
|
-
_$w('#optWebsiteCheckbox').customClassList.add('disabled-text');
|
|
395
|
-
_$w('#optWebsiteCheckbox').customClassList.add('disabled-checkbox');
|
|
396
|
-
_$w('#optWebsiteCheckbox').disable();
|
|
397
|
-
} else {
|
|
398
|
-
_$w('#optWebsiteCheckbox').customClassList.remove('disabled-text');
|
|
399
|
-
_$w('#optWebsiteCheckbox').customClassList.remove('disabled-checkbox');
|
|
400
|
-
_$w('#optWebsiteCheckbox').enable();
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function setupOptOutCheckbox(
|
|
405
|
-
checkboxId,
|
|
406
|
-
confirmationBoxId,
|
|
407
|
-
confirmBtnId,
|
|
408
|
-
cancelBtnId,
|
|
409
|
-
confirmCallback
|
|
410
|
-
) {
|
|
411
|
-
const checkbox = _$w(checkboxId);
|
|
412
|
-
const box = _$w(confirmationBoxId);
|
|
413
|
-
|
|
414
|
-
checkbox.onChange(e => {
|
|
415
|
-
if (!e.target.checked) {
|
|
416
|
-
box.expand();
|
|
417
|
-
checkbox.disable();
|
|
418
|
-
checkbox.customClassList.add('disabled-text');
|
|
419
|
-
} else {
|
|
420
|
-
confirmCallback(true);
|
|
421
|
-
}
|
|
422
|
-
let sectionHandlerType;
|
|
423
|
-
if (checkboxId === '#optCheckbox') {
|
|
424
|
-
sectionHandlerType = FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT;
|
|
425
|
-
} else if (checkboxId === '#optWebsiteCheckbox') {
|
|
426
|
-
sectionHandlerType = FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT;
|
|
427
|
-
}
|
|
428
|
-
checkFormChanges(sectionHandlerType);
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
_$w(confirmBtnId).onClick(() => confirmCallback(true));
|
|
432
|
-
_$w(cancelBtnId).onClick(() => confirmCallback(false));
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function toggleFreeWebsiteText(isFreeWebsiteEnabled) {
|
|
436
|
-
if (isFreeWebsiteEnabled) {
|
|
437
|
-
_$w('#freeWebsiteText').text = FREE_WEBSITE_TEXT_STATES.ENABLED;
|
|
438
|
-
} else {
|
|
439
|
-
_$w('#freeWebsiteText').text = FREE_WEBSITE_TEXT_STATES.DISABLED;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async function handleOptConfirmation(confirmed, optCheckbox, optConfirmationBox, field) {
|
|
444
|
-
const checkbox = _$w(optCheckbox);
|
|
445
|
-
const box = _$w(optConfirmationBox);
|
|
446
|
-
if (confirmed) {
|
|
447
|
-
const toSaveOptValue = optCheckbox === '#optCheckbox' ? !checkbox.checked : checkbox.checked;
|
|
448
|
-
const formData = {
|
|
449
|
-
...itemMemberObj,
|
|
450
|
-
[field]: toSaveOptValue,
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
await saveData(formData);
|
|
454
|
-
if (field === 'showWixUrl') {
|
|
455
|
-
const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
|
|
456
|
-
if (toSaveOptValue) {
|
|
457
|
-
if (!showExistingUrl) {
|
|
458
|
-
_$w('#showUrlWixCheckbox').checked = true;
|
|
459
|
-
}
|
|
460
|
-
_$w('#showUrlWixCheckbox').enable();
|
|
461
|
-
} else {
|
|
462
|
-
_$w('#showUrlWixCheckbox').checked = false;
|
|
463
|
-
_$w('#showUrlWixCheckbox').disable();
|
|
464
|
-
}
|
|
465
|
-
toggleFreeWebsiteText(toSaveOptValue);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
box.collapse();
|
|
470
|
-
checkbox.enable();
|
|
471
|
-
checkbox.customClassList.remove('disabled-text');
|
|
472
|
-
|
|
473
|
-
if (!confirmed) {
|
|
474
|
-
checkbox.checked = !checkbox.checked;
|
|
475
|
-
}
|
|
476
|
-
const section =
|
|
477
|
-
field === 'showWixUrl'
|
|
478
|
-
? FORM_SECTION_HANDLER_MAP.WEBSITE_OPT_OUT.section
|
|
479
|
-
: FORM_SECTION_HANDLER_MAP.DIRECTORY_OPT_OUT.section;
|
|
480
|
-
formHasUnsavedChanges[section] = false;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function onFormDataChanged() {
|
|
484
|
-
const CHANGE_EVENTS = {
|
|
485
|
-
ON_CHANGE: 'onChange',
|
|
486
|
-
ON_INPUT: 'onInput',
|
|
487
|
-
};
|
|
488
|
-
const elements = {
|
|
489
|
-
$firstNameInput: _$w('#firstNameInput'),
|
|
490
|
-
$lastNameInput: _$w('#lastNameInput'),
|
|
491
|
-
$businessNameCheckbox: _$w('#businessNameCheckbox'),
|
|
492
|
-
$yearJoinedcheckbox: _$w('#yearJoinedcheckbox'),
|
|
493
|
-
$aboutInput: _$w('#aboutInput'),
|
|
494
|
-
$businessNameInput: _$w('#businessNameInput'),
|
|
495
|
-
$uploadProfileButton: _$w('#uploadProfileButton'),
|
|
496
|
-
$uploadLogoButton: _$w('#uploadLogoButton'),
|
|
497
|
-
$uploadBannerButton: _$w('#uploadBannerButton'),
|
|
498
|
-
$showContactFormCheckbox: _$w('#showCotactFormCheckbox'),
|
|
499
|
-
$contactFormEmailInput: _$w('#contactFormEmailInput'),
|
|
500
|
-
$schedulingLinkInput: _$w('#schedulingLinkInput'),
|
|
501
|
-
$UrlInput: _$w('#UrlInput'),
|
|
502
|
-
$slugInput: _$w('#slugInput'),
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
const formChangeEventBindings = {
|
|
506
|
-
[FORM_SECTION_HANDLER_MAP.PERSONAL.section]: [
|
|
507
|
-
{ $elem: elements.$firstNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
508
|
-
{ $elem: elements.$lastNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
509
|
-
{ $elem: elements.$slugInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
510
|
-
],
|
|
511
|
-
[FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: [
|
|
512
|
-
{ $elem: elements.$businessNameCheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
513
|
-
{ $elem: elements.$yearJoinedcheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
514
|
-
{ $elem: elements.$aboutInput, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
515
|
-
{ $elem: elements.$businessNameInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
516
|
-
{ $elem: elements.$uploadProfileButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
517
|
-
{ $elem: elements.$uploadLogoButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
518
|
-
{ $elem: elements.$uploadBannerButton, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
519
|
-
],
|
|
520
|
-
[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: [
|
|
521
|
-
{ $elem: elements.$showContactFormCheckbox, changeEvent: CHANGE_EVENTS.ON_CHANGE },
|
|
522
|
-
{ $elem: elements.$schedulingLinkInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
523
|
-
{ $elem: elements.$UrlInput, changeEvent: CHANGE_EVENTS.ON_INPUT },
|
|
524
|
-
],
|
|
525
|
-
};
|
|
526
|
-
Object.keys(formChangeEventBindings).forEach(section => {
|
|
527
|
-
formChangeEventBindings[section].forEach(({ $elem, changeEvent }) => {
|
|
528
|
-
$elem[changeEvent](() => {
|
|
529
|
-
const handlerMap = Object.values(FORM_SECTION_HANDLER_MAP).find(
|
|
530
|
-
handlerMap => handlerMap.section === section
|
|
531
|
-
);
|
|
532
|
-
checkFormChanges(handlerMap);
|
|
533
|
-
if (section === FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section) {
|
|
534
|
-
setTimeout(syncOtherWebsiteUrlValidity, 0);
|
|
535
|
-
}
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
elements.$contactFormEmailInput.onCustomValidation((value, reject) => {
|
|
541
|
-
if (!value) {
|
|
542
|
-
reject(CONTACT_EMAIL_VALIDATION_MESSAGES.REQUIRED);
|
|
543
|
-
return;
|
|
544
|
-
}
|
|
545
|
-
// When value is non-empty, ignore stale "required" state (from when field was empty).
|
|
546
|
-
// Blur can run validation before async isEmailAlreadyUsed completes.
|
|
547
|
-
if (
|
|
548
|
-
!ContactEmailValidity.valid &&
|
|
549
|
-
ContactEmailValidity.validationMessage !== CONTACT_EMAIL_VALIDATION_MESSAGES.REQUIRED
|
|
550
|
-
) {
|
|
551
|
-
reject(ContactEmailValidity.validationMessage);
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
// Helper to force trigger contact email custom validation programmatically (onCustomValidation
|
|
556
|
-
// does not run with async validators, so we trigger it manually on input).
|
|
557
|
-
const forceTriggerContactEmailCustomValidation = value => {
|
|
558
|
-
elements.$contactFormEmailInput.value = value;
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
const setContactEmailInvalid = (message, value) => {
|
|
562
|
-
ContactEmailValidity.valid = false;
|
|
563
|
-
ContactEmailValidity.validationMessage = message;
|
|
564
|
-
forceTriggerContactEmailCustomValidation(value);
|
|
565
|
-
};
|
|
566
|
-
|
|
567
|
-
elements.$contactFormEmailInput.onInput(event => {
|
|
568
|
-
const value = event.target.value;
|
|
569
|
-
_$w('#saveContactBookingButton').disable();
|
|
570
|
-
|
|
571
|
-
if (contactEmailValidationTimeout.validation) {
|
|
572
|
-
clearTimeout(contactEmailValidationTimeout.validation);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (!value) {
|
|
576
|
-
setContactEmailInvalid(CONTACT_EMAIL_VALIDATION_MESSAGES.REQUIRED, value);
|
|
577
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
const validationId = ++currentContactEmailValidationId;
|
|
582
|
-
contactEmailValidationTimeout.validation = setTimeout(async () => {
|
|
583
|
-
try {
|
|
584
|
-
if (validationId !== currentContactEmailValidationId) {
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const isAlreadyUsed = await isEmailAlreadyUsed(value, itemMemberObj.memberId);
|
|
589
|
-
if (validationId !== currentContactEmailValidationId) {
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
if (isAlreadyUsed) {
|
|
594
|
-
setContactEmailInvalid(CONTACT_EMAIL_VALIDATION_MESSAGES.ALREADY_TAKEN, value);
|
|
595
|
-
} else {
|
|
596
|
-
ContactEmailValidity.valid = true;
|
|
597
|
-
ContactEmailValidity.validationMessage = '';
|
|
598
|
-
forceTriggerContactEmailCustomValidation(value);
|
|
599
|
-
}
|
|
600
|
-
} catch (error) {
|
|
601
|
-
console.error('Email validation error:', error);
|
|
602
|
-
if (validationId === currentContactEmailValidationId) {
|
|
603
|
-
setContactEmailInvalid(CONTACT_EMAIL_VALIDATION_MESSAGES.ERROR, value);
|
|
604
|
-
}
|
|
605
|
-
} finally {
|
|
606
|
-
if (validationId === currentContactEmailValidationId) {
|
|
607
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
}, 500);
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
_$w('#slugInput').onInput(event => {
|
|
614
|
-
_$w('#savePersonalButton').disable();
|
|
615
|
-
const slug = event.target.value;
|
|
616
|
-
|
|
617
|
-
isSlugValid = false;
|
|
618
|
-
|
|
619
|
-
if (slugValidationTimeout.slugValidation) {
|
|
620
|
-
clearTimeout(slugValidationTimeout.slugValidation);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
const validationId = ++currentSlugValidationId;
|
|
624
|
-
|
|
625
|
-
slugValidationTimeout.slugValidation = setTimeout(async () => {
|
|
626
|
-
try {
|
|
627
|
-
if (validationId !== currentSlugValidationId) {
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const result = await validateSlugRealTime(slug);
|
|
632
|
-
|
|
633
|
-
if (validationId === currentSlugValidationId) {
|
|
634
|
-
isSlugValid = result.isValid;
|
|
635
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.PERSONAL);
|
|
636
|
-
}
|
|
637
|
-
} catch (error) {
|
|
638
|
-
console.error('Slug validation error:', error);
|
|
639
|
-
if (validationId === currentSlugValidationId) {
|
|
640
|
-
isSlugValid = false;
|
|
641
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.PERSONAL);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}, 800);
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
function syncOtherWebsiteUrlValidity() {
|
|
649
|
-
const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
|
|
650
|
-
const $urlInput = _$w('#UrlInput');
|
|
651
|
-
$urlInput.required = showExistingUrl;
|
|
652
|
-
if (showExistingUrl) {
|
|
653
|
-
$urlInput.updateValidityIndication();
|
|
654
|
-
} else {
|
|
655
|
-
$urlInput.resetValidityIndication();
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
function checkFormChanges(formSectionHandler) {
|
|
660
|
-
let isFormDataChanged = false;
|
|
661
|
-
const toggleSaveDataButton = (formDataType, isFormDataChanged) => {
|
|
662
|
-
const saveButtonMap = {
|
|
663
|
-
[FORM_SECTION_HANDLER_MAP.PERSONAL.section]: '#savePersonalButton',
|
|
664
|
-
[FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section]: '#saveBusinessButton',
|
|
665
|
-
[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section]: '#saveContactBookingButton',
|
|
666
|
-
};
|
|
667
|
-
|
|
668
|
-
const buttonSelector = saveButtonMap[formDataType];
|
|
669
|
-
if (!buttonSelector) {
|
|
670
|
-
throw new Error(`No save button defined for form section: ${formDataType}`);
|
|
671
|
-
}
|
|
672
|
-
const $saveDataButton = _$w(buttonSelector);
|
|
673
|
-
|
|
674
|
-
let isUrlValid = true,
|
|
675
|
-
isEmailValid = true,
|
|
676
|
-
isNameValid = true,
|
|
677
|
-
isSlugValidLocal = true;
|
|
678
|
-
if (formDataType === FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section) {
|
|
679
|
-
isEmailValid = _$w('#contactFormEmailInput').valid;
|
|
680
|
-
const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
|
|
681
|
-
const otherWebsiteValue = (_$w('#UrlInput').value || '').trim();
|
|
682
|
-
const isOtherWebsiteRequiredValid =
|
|
683
|
-
!showExistingUrl || (otherWebsiteValue !== '' && !isNotValidUrl(otherWebsiteValue));
|
|
684
|
-
isUrlValid =
|
|
685
|
-
isOtherWebsiteRequiredValid &&
|
|
686
|
-
!isNotValidUrl(_$w('#UrlInput').value) &&
|
|
687
|
-
!isNotValidUrl(_$w('#schedulingLinkInput').value);
|
|
688
|
-
syncOtherWebsiteUrlValidity();
|
|
689
|
-
}
|
|
690
|
-
if (formDataType === FORM_SECTION_HANDLER_MAP.PERSONAL.section) {
|
|
691
|
-
isNameValid = _$w('#firstNameInput').valid && _$w('#lastNameInput').valid;
|
|
692
|
-
isSlugValidLocal = isSlugValid;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
if (isFormDataChanged && isUrlValid && isEmailValid && isNameValid && isSlugValidLocal) {
|
|
696
|
-
$saveDataButton.enable();
|
|
697
|
-
} else {
|
|
698
|
-
$saveDataButton.disable();
|
|
699
|
-
}
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
if (formSectionHandler) {
|
|
703
|
-
const { section, handler } = formSectionHandler;
|
|
704
|
-
isFormDataChanged = handler();
|
|
705
|
-
formHasUnsavedChanges[section] = isFormDataChanged;
|
|
706
|
-
if (
|
|
707
|
-
[
|
|
708
|
-
FORM_SECTION_HANDLER_MAP.PERSONAL.section,
|
|
709
|
-
FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section,
|
|
710
|
-
FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section,
|
|
711
|
-
].includes(section)
|
|
712
|
-
) {
|
|
713
|
-
toggleSaveDataButton(section, isFormDataChanged);
|
|
714
|
-
}
|
|
715
|
-
} else {
|
|
716
|
-
Object.values(FORM_SECTION_HANDLER_MAP).forEach(({ section, handler }) => {
|
|
717
|
-
formHasUnsavedChanges[section] = handler();
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
function checkWebsiteOptOutDataChanged() {
|
|
723
|
-
const currentWebsiteOptInData = _$w('#optWebsiteCheckbox').checked;
|
|
724
|
-
const originalWebsiteOptInData = originalMemberData.showWixUrl;
|
|
725
|
-
return !_.isEqual(currentWebsiteOptInData, originalWebsiteOptInData);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
function checkDirectoryOptOutDataChanged() {
|
|
729
|
-
const currentDirectoryOptOutData = _$w('#optCheckbox').checked;
|
|
730
|
-
const originalDirectoryOptOutData = !originalMemberData.optOut;
|
|
731
|
-
return !_.isEqual(currentDirectoryOptOutData, originalDirectoryOptOutData);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function checkBusinessDataChanged() {
|
|
735
|
-
const currentBusinessData = getBusinessAndServicesData();
|
|
736
|
-
const originalBusinessData = {
|
|
737
|
-
showBusinessName: originalMemberData.showBusinessName,
|
|
738
|
-
businessName: originalMemberData.businessName,
|
|
739
|
-
showABMP: originalMemberData.showABMP,
|
|
740
|
-
aboutService: originalMemberData.aboutService,
|
|
741
|
-
profileImage: originalMemberData.profileImage,
|
|
742
|
-
logoImage: originalMemberData.logoImage,
|
|
743
|
-
bannerImages: originalMemberData.bannerImages || [],
|
|
744
|
-
areasOfPractices: originalMemberData.areasOfPractices || [],
|
|
745
|
-
testimonial: originalMemberData.testimonial || [],
|
|
746
|
-
};
|
|
747
|
-
return !_.isEqual(currentBusinessData, originalBusinessData);
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
function checkContactDataChanged() {
|
|
751
|
-
const currentContactData = getContactAndBookingData();
|
|
752
|
-
const originalContactData = {
|
|
753
|
-
showContactForm: originalMemberData.showContactForm,
|
|
754
|
-
contactFormEmail: originalMemberData.contactFormEmail,
|
|
755
|
-
toShowPhone: originalMemberData.toShowPhone,
|
|
756
|
-
bookingUrl: originalMemberData.bookingUrl,
|
|
757
|
-
website: originalMemberData.website,
|
|
758
|
-
showWebsite: originalMemberData.showWebsite,
|
|
759
|
-
showWixUrl: originalMemberData.showWixUrl,
|
|
760
|
-
addressDisplayOption: originalMemberData.addressDisplayOption,
|
|
761
|
-
addresses: originalMemberData.addresses,
|
|
762
|
-
phones: Array.isArray(originalMemberData.phones) ? originalMemberData.phones : [],
|
|
763
|
-
};
|
|
764
|
-
// Normalize so empty string and undefined are treated the same
|
|
765
|
-
const str = v => (v != null ? v : '').toString().trim();
|
|
766
|
-
const normalizeContactForCompare = data => ({
|
|
767
|
-
...data,
|
|
768
|
-
website: str(data.website),
|
|
769
|
-
bookingUrl: str(data.bookingUrl),
|
|
770
|
-
contactFormEmail: str(data.contactFormEmail),
|
|
771
|
-
});
|
|
772
|
-
return !_.isEqual(
|
|
773
|
-
normalizeContactForCompare(currentContactData),
|
|
774
|
-
normalizeContactForCompare(originalContactData)
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
function setBusinessServices() {
|
|
779
|
-
_$w('#businessNameText').text = itemMemberObj.businessName || DEFAULT_BUSINESS_NAME_TEXT;
|
|
780
|
-
_$w('#businessNameCheckbox').checked = itemMemberObj.showBusinessName;
|
|
781
|
-
_$w('#yearJoinedcheckbox').checked = itemMemberObj.showABMP;
|
|
782
|
-
_$w('#aboutInput').value = itemMemberObj.aboutService;
|
|
783
|
-
_$w('#businessNameInput').value = itemMemberObj.businessName;
|
|
784
|
-
_$w('#clearBusinessNameBtn').onClick(() => {
|
|
785
|
-
_$w('#businessNameInput').value = '';
|
|
786
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
// Get memberships array
|
|
790
|
-
const memberships = Array.isArray(itemMemberObj.memberships) ? itemMemberObj.memberships : [];
|
|
791
|
-
// Find Site Association Member Since
|
|
792
|
-
const siteAssociationMemberSince = memberships.find(m => m.isSiteAssociation)?.membersince;
|
|
793
|
-
// Set yearJoinedText
|
|
794
|
-
if (siteAssociationMemberSince) {
|
|
795
|
-
_$w('#yearJoinedText').text = siteAssociationMemberSince;
|
|
796
|
-
} else {
|
|
797
|
-
_$w('#yearJoinedText').text = 'Year joined not provided';
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
uploadImageFromLightbox();
|
|
801
|
-
setupImageUploadAndDeleteHandlers();
|
|
802
|
-
setupServiceSelection();
|
|
803
|
-
setInterestData();
|
|
804
|
-
setupTestimonials();
|
|
805
|
-
|
|
806
|
-
// Initialize areasOfPractices array if it doesn't exist
|
|
807
|
-
if (!itemMemberObj.areasOfPractices) {
|
|
808
|
-
itemMemberObj.areasOfPractices = [];
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (Array.isArray(itemMemberObj.areasOfPractices)) {
|
|
812
|
-
selectedServices = itemMemberObj.areasOfPractices.map(label => ({
|
|
813
|
-
_id: generateId(),
|
|
814
|
-
label: String(label),
|
|
815
|
-
}));
|
|
816
|
-
renderServices();
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
displayExistingImagesFromCMS();
|
|
820
|
-
|
|
821
|
-
_$w('#savePersonalButton').onClick(savePersonalDetails);
|
|
822
|
-
_$w('#saveBusinessButton').onClick(saveBusinessServices);
|
|
823
|
-
_$w('#servicesRepeater').onItemReady(($item, itemData) => {
|
|
824
|
-
$item('#serviceNameText').text = itemData.label;
|
|
825
|
-
});
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
function uploadImage(uploadButton, imageKey, updateUI) {
|
|
829
|
-
_$w(uploadButton).onChange(async () => {
|
|
830
|
-
if (_$w(uploadButton).value?.length > 0) {
|
|
831
|
-
try {
|
|
832
|
-
const uploadedFiles = await _$w(uploadButton).uploadFiles();
|
|
833
|
-
uploadedFiles.forEach(file => {
|
|
834
|
-
uploadedImages[imageKey] = file.fileUrl;
|
|
835
|
-
updateUI(file);
|
|
836
|
-
});
|
|
837
|
-
} catch (error) {
|
|
838
|
-
console.error(`File upload error: ${error.errorCode}`, error.errorDescription);
|
|
839
|
-
}
|
|
840
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
841
|
-
}
|
|
842
|
-
});
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
function uploadImageFromLightbox() {
|
|
846
|
-
_$w('#bannerLightboxButton').onClick(async () => {
|
|
847
|
-
const returnedImage = await wixWindow.openLightbox(LIGHTBOX_NAMES.SELECT_BANNER_IMAGES);
|
|
848
|
-
console.log('uploadedImages', returnedImage);
|
|
849
|
-
|
|
850
|
-
if (returnedImage && returnedImage.image) {
|
|
851
|
-
console.log('Image returned from lightbox:', returnedImage);
|
|
852
|
-
|
|
853
|
-
// Update stored image
|
|
854
|
-
uploadedImages.bannerImage = returnedImage.image;
|
|
855
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
856
|
-
_$w('#bannerImage').src = returnedImage.image;
|
|
857
|
-
_$w('#bannerImageName').text = extractFileName(returnedImage.image);
|
|
858
|
-
_$w('#bannerImageContainer').expand();
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
function extractFileName(fileUrl) {
|
|
864
|
-
try {
|
|
865
|
-
const url = new URL(fileUrl);
|
|
866
|
-
const pathParts = url.pathname.split('/');
|
|
867
|
-
return pathParts[pathParts.length - 1] || '';
|
|
868
|
-
} catch {
|
|
869
|
-
return '';
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
function setupImageUploadAndDeleteHandlers() {
|
|
874
|
-
uploadImage('#uploadProfileButton', 'profileImage', file => {
|
|
875
|
-
_$w('#profileImage').src = file.fileUrl;
|
|
876
|
-
_$w('#profileImageName').text = formatFileName(file.fileName);
|
|
877
|
-
_$w('#profileImageContainer').expand();
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
uploadImage('#uploadLogoButton', 'logoImage', file => {
|
|
881
|
-
_$w('#logoImage').src = file.fileUrl;
|
|
882
|
-
_$w('#logoImageName').text = file.fileName;
|
|
883
|
-
_$w('#logoImageContainer').expand();
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
uploadImage('#uploadBannerButton', 'bannerImage', file => {
|
|
887
|
-
_$w('#bannerImage').src = file.fileUrl;
|
|
888
|
-
_$w('#bannerImageName').text = file.fileName;
|
|
889
|
-
_$w('#bannerImageContainer').expand();
|
|
890
|
-
});
|
|
891
|
-
|
|
892
|
-
setupDeleteHandler(
|
|
893
|
-
'#deleteProfileImage',
|
|
894
|
-
'#profileImage',
|
|
895
|
-
'#profileImageName',
|
|
896
|
-
'#profileImageContainer',
|
|
897
|
-
'profileImage',
|
|
898
|
-
'#uploadProfileButton'
|
|
899
|
-
);
|
|
900
|
-
setupDeleteHandler(
|
|
901
|
-
'#deleteLogoImage',
|
|
902
|
-
'#logoImage',
|
|
903
|
-
'#logoImageName',
|
|
904
|
-
'#logoImageContainer',
|
|
905
|
-
'logoImage',
|
|
906
|
-
'#uploadLogoButton'
|
|
907
|
-
);
|
|
908
|
-
setupDeleteHandler(
|
|
909
|
-
'#deleteBannerImage',
|
|
910
|
-
'#bannerImage',
|
|
911
|
-
'#bannerImageName',
|
|
912
|
-
'#bannerImageContainer',
|
|
913
|
-
'bannerImage',
|
|
914
|
-
'#uploadBannerButton'
|
|
915
|
-
);
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
function setupDeleteHandler(deleteBtn, imgId, nameId, containerId, imageKey, uploadBtnId) {
|
|
919
|
-
_$w(deleteBtn).onClick(async () => {
|
|
920
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
921
|
-
|
|
922
|
-
if (result && result.toDelete) {
|
|
923
|
-
_$w(imgId).src = '';
|
|
924
|
-
_$w(nameId).text = '';
|
|
925
|
-
_$w(containerId).collapse();
|
|
926
|
-
uploadedImages[imageKey] = '';
|
|
927
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
928
|
-
_$w(uploadBtnId).reset();
|
|
929
|
-
}
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
function displayExistingImagesFromCMS() {
|
|
934
|
-
const imageMap = [
|
|
935
|
-
{
|
|
936
|
-
key: 'profileImage',
|
|
937
|
-
imageSelector: '#profileImage',
|
|
938
|
-
nameSelector: '#profileImageName',
|
|
939
|
-
containerSelector: '#profileImageContainer',
|
|
940
|
-
},
|
|
941
|
-
{
|
|
942
|
-
key: 'logoImage',
|
|
943
|
-
imageSelector: '#logoImage',
|
|
944
|
-
nameSelector: '#logoImageName',
|
|
945
|
-
containerSelector: '#logoImageContainer',
|
|
946
|
-
},
|
|
947
|
-
{
|
|
948
|
-
key: 'bannerImages',
|
|
949
|
-
imageSelector: '#bannerImage',
|
|
950
|
-
nameSelector: '#bannerImageName',
|
|
951
|
-
containerSelector: '#bannerImageContainer',
|
|
952
|
-
isArray: true,
|
|
953
|
-
},
|
|
954
|
-
];
|
|
955
|
-
|
|
956
|
-
imageMap.forEach(({ key, imageSelector, nameSelector, containerSelector, isArray }) => {
|
|
957
|
-
const imageValue = isArray
|
|
958
|
-
? Array.isArray(itemMemberObj[key]) && itemMemberObj[key].length > 0
|
|
959
|
-
? itemMemberObj[key][0]
|
|
960
|
-
: null
|
|
961
|
-
: itemMemberObj[key];
|
|
962
|
-
|
|
963
|
-
if (imageValue && imageValue?.trim()) {
|
|
964
|
-
// Only set profile image if it's Wix-hosted; other images will always be wix url
|
|
965
|
-
const isProfileImage = imageSelector === '#profileImage';
|
|
966
|
-
const shouldSetImage = !isProfileImage || isWixHostedImage(imageValue);
|
|
967
|
-
|
|
968
|
-
if (shouldSetImage) {
|
|
969
|
-
_$w(imageSelector).src = imageValue;
|
|
970
|
-
}
|
|
971
|
-
_$w(nameSelector).text = formatFileName(extractFileName(imageValue));
|
|
972
|
-
_$w(containerSelector).expand();
|
|
973
|
-
uploadedImages[key === 'bannerImages' ? 'bannerImage' : key] = imageValue;
|
|
974
|
-
}
|
|
975
|
-
});
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
async function setInterestData() {
|
|
979
|
-
const interestsData = await getInterestAll();
|
|
980
|
-
|
|
981
|
-
_$w('#removeServiceButton').onClick(async event => {
|
|
982
|
-
const itemId = event.context.itemId;
|
|
983
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
984
|
-
|
|
985
|
-
if (result && result.toDelete) {
|
|
986
|
-
selectedServices = selectedServices.filter(service => service._id !== itemId);
|
|
987
|
-
renderServices();
|
|
988
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
|
|
992
|
-
if (Array.isArray(interestsData) && interestsData.length > 0) {
|
|
993
|
-
const formattedData = interestsData.map((val, index) => ({
|
|
994
|
-
_id: String(index),
|
|
995
|
-
value: val,
|
|
996
|
-
}));
|
|
997
|
-
|
|
998
|
-
_$w('#repeaterInterest').data = formattedData;
|
|
999
|
-
_$w('#repeaterInterest').onItemReady(($item, itemData, _index) => {
|
|
1000
|
-
$item('#interestText').text = itemData.value;
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
const debounce_fun = _.debounce(async () => {
|
|
1006
|
-
const searchValue = _$w('#intrestInput').value.trim().toLowerCase();
|
|
1007
|
-
await filterInterests(searchValue);
|
|
1008
|
-
}, 250);
|
|
1009
|
-
|
|
1010
|
-
function setupServiceSelection() {
|
|
1011
|
-
const intrestInput = _$w('#intrestInput');
|
|
1012
|
-
|
|
1013
|
-
intrestInput.onClick(() => {
|
|
1014
|
-
if (intrestInput.value) {
|
|
1015
|
-
intrestInput.onClick(async () => {
|
|
1016
|
-
await filterInterests(intrestInput.value);
|
|
1017
|
-
});
|
|
1018
|
-
} else {
|
|
1019
|
-
setInterestData();
|
|
1020
|
-
}
|
|
1021
|
-
_$w('#containerRepeaterInterest').expand();
|
|
1022
|
-
});
|
|
1023
|
-
|
|
1024
|
-
intrestInput.onKeyPress(event => {
|
|
1025
|
-
debounce_fun();
|
|
1026
|
-
|
|
1027
|
-
if (event.key === 'Enter') {
|
|
1028
|
-
const typedValue = intrestInput.value.trim();
|
|
1029
|
-
|
|
1030
|
-
if (
|
|
1031
|
-
typedValue &&
|
|
1032
|
-
!selectedServices.some(
|
|
1033
|
-
service => service.label.toLowerCase() === typedValue.toLowerCase()
|
|
1034
|
-
)
|
|
1035
|
-
) {
|
|
1036
|
-
selectedServices.unshift({
|
|
1037
|
-
_id: generateId(),
|
|
1038
|
-
label: typedValue,
|
|
1039
|
-
});
|
|
1040
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
1041
|
-
renderServices();
|
|
1042
|
-
intrestInput.value = '';
|
|
1043
|
-
_$w('#containerRepeaterInterest').collapse();
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
});
|
|
1047
|
-
|
|
1048
|
-
_$w('#repeaterInterest').onItemReady(($item, itemData) => {
|
|
1049
|
-
$item('#interestText').text = itemData.value;
|
|
1050
|
-
|
|
1051
|
-
$item('#intrestItem').onClick(() => {
|
|
1052
|
-
if (!selectedServices.some(service => service.label === itemData.value)) {
|
|
1053
|
-
selectedServices.unshift({
|
|
1054
|
-
_id: generateId(),
|
|
1055
|
-
label: itemData.value,
|
|
1056
|
-
});
|
|
1057
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
1058
|
-
renderServices();
|
|
1059
|
-
_$w('#intrestInput').value = '';
|
|
1060
|
-
_$w('#containerRepeaterInterest').collapse();
|
|
1061
|
-
}
|
|
1062
|
-
});
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
function renderServices() {
|
|
1067
|
-
setupRepeater('#servicesRepeater', [...selectedServices]);
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
function setupRepeater(repeaterId, data) {
|
|
1071
|
-
const repeater = _$w(repeaterId);
|
|
1072
|
-
repeater.data = data;
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
/**
|
|
1076
|
-
* Logs user data changes for debugging and investigation purposes
|
|
1077
|
-
* @param {string} saveType - Type of save operation (personal, business, contact)
|
|
1078
|
-
* @param {Object} beforeData - Data before save
|
|
1079
|
-
* @param {Object} afterData - Data after save
|
|
1080
|
-
* @param {boolean} success - Whether the save was successful
|
|
1081
|
-
*/
|
|
1082
|
-
function logUserDataChanges(saveType, beforeData, afterData, success) {
|
|
1083
|
-
const timestamp = new Date().toISOString();
|
|
1084
|
-
|
|
1085
|
-
console.group(
|
|
1086
|
-
`User Data Change Log - ${saveType.toUpperCase()} - ${success ? 'SUCCESS' : 'FAILED'}`
|
|
1087
|
-
);
|
|
1088
|
-
console.log('Change Details:', {
|
|
1089
|
-
timestamp,
|
|
1090
|
-
saveType,
|
|
1091
|
-
success,
|
|
1092
|
-
});
|
|
1093
|
-
|
|
1094
|
-
console.log('BEFORE Save:', beforeData);
|
|
1095
|
-
console.log('AFTER Save:', afterData);
|
|
1096
|
-
|
|
1097
|
-
// Calculate and log specific changes
|
|
1098
|
-
const changes = {};
|
|
1099
|
-
Object.keys(afterData).forEach(key => {
|
|
1100
|
-
if (JSON.stringify(beforeData[key]) !== JSON.stringify(afterData[key])) {
|
|
1101
|
-
changes[key] = {
|
|
1102
|
-
before: beforeData[key],
|
|
1103
|
-
after: afterData[key],
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
});
|
|
1107
|
-
|
|
1108
|
-
if (Object.keys(changes).length > 0) {
|
|
1109
|
-
console.log('Specific Changes:', changes);
|
|
1110
|
-
} else {
|
|
1111
|
-
console.log('No changes detected in data comparison');
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
console.groupEnd();
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
async function saveData(formData) {
|
|
1118
|
-
// Capture data before save for logging
|
|
1119
|
-
const beforeSaveData = JSON.parse(JSON.stringify(itemMemberObj));
|
|
1120
|
-
|
|
1121
|
-
const { type, saveData: saved } = await saveRegistrationData(formData, itemMemberObj._id);
|
|
1122
|
-
|
|
1123
|
-
if (type === 'success') {
|
|
1124
|
-
// Log the successful change
|
|
1125
|
-
logUserDataChanges('general', beforeSaveData, saved, true);
|
|
1126
|
-
|
|
1127
|
-
itemMemberObj = { ...saved };
|
|
1128
|
-
originalMemberData = JSON.parse(JSON.stringify(saved));
|
|
1129
|
-
return {
|
|
1130
|
-
success: true,
|
|
1131
|
-
message: 'The information was saved successfully.',
|
|
1132
|
-
};
|
|
1133
|
-
} else {
|
|
1134
|
-
// Log the failed attempt
|
|
1135
|
-
logUserDataChanges('general', beforeSaveData, formData, false);
|
|
1136
|
-
|
|
1137
|
-
return {
|
|
1138
|
-
success: false,
|
|
1139
|
-
message:
|
|
1140
|
-
"It looks like something went wrong — the information wasn't saved. Please try again later.",
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
function isValidSlugFormat(slug) {
|
|
1146
|
-
if (!slug || slug.length === 0) return false;
|
|
1147
|
-
if (slug.length < 3 || slug.length > 50) return false;
|
|
1148
|
-
|
|
1149
|
-
const slugRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/;
|
|
1150
|
-
return slugRegex.test(slug);
|
|
1151
|
-
}
|
|
1152
|
-
|
|
1153
|
-
async function validateSlugRealTime(slug) {
|
|
1154
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
1155
|
-
_$w(SLUG_FLAGS.INVALID).collapse();
|
|
1156
|
-
|
|
1157
|
-
const trimmedSlug = slug.trim();
|
|
1158
|
-
|
|
1159
|
-
if (!isValidSlugFormat(trimmedSlug)) {
|
|
1160
|
-
_$w('#invalidSlugMessage').text = SLUG_MESSAGES.INVALID_FORMAT;
|
|
1161
|
-
_$w(SLUG_FLAGS.INVALID).expand();
|
|
1162
|
-
return { isValid: false };
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
if (trimmedSlug === (originalMemberData.url || '')) {
|
|
1166
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
1167
|
-
_$w(SLUG_FLAGS.INVALID).collapse();
|
|
1168
|
-
return { isValid: true };
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
try {
|
|
1172
|
-
const result = await checkUrlUniqueness(trimmedSlug, itemMemberObj.memberId);
|
|
1173
|
-
const isUnique = result.isUnique;
|
|
1174
|
-
|
|
1175
|
-
if (isUnique) {
|
|
1176
|
-
_$w(SLUG_FLAGS.VALID).expand();
|
|
1177
|
-
_$w(SLUG_FLAGS.INVALID).collapse();
|
|
1178
|
-
return { isValid: true };
|
|
1179
|
-
} else {
|
|
1180
|
-
_$w('#invalidSlugMessage').text = SLUG_MESSAGES.TAKEN;
|
|
1181
|
-
_$w(SLUG_FLAGS.INVALID).expand();
|
|
1182
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
1183
|
-
return { isValid: false };
|
|
1184
|
-
}
|
|
1185
|
-
} catch (error) {
|
|
1186
|
-
console.error('Error checking slug uniqueness:', error);
|
|
1187
|
-
_$w('#invalidSlugMessage').text = SLUG_MESSAGES.ERROR;
|
|
1188
|
-
_$w(SLUG_FLAGS.INVALID).expand();
|
|
1189
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
1190
|
-
return { isValid: false };
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
function getPersonalData() {
|
|
1195
|
-
const firstName = _$w('#firstNameInput').value.trim();
|
|
1196
|
-
const lastName = _$w('#lastNameInput').value.trim();
|
|
1197
|
-
const fullName = `${firstName} ${lastName}`.trim();
|
|
1198
|
-
const url = _$w('#slugInput').value.trim();
|
|
1199
|
-
|
|
1200
|
-
return {
|
|
1201
|
-
firstName,
|
|
1202
|
-
lastName,
|
|
1203
|
-
fullName,
|
|
1204
|
-
url,
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
function checkPersonalDataChanged() {
|
|
1209
|
-
const currentPersonalData = getPersonalData();
|
|
1210
|
-
const originalPersonalData = {
|
|
1211
|
-
firstName: originalMemberData.firstName || '',
|
|
1212
|
-
lastName: originalMemberData.lastName || '',
|
|
1213
|
-
fullName: originalMemberData.fullName || '',
|
|
1214
|
-
url: originalMemberData.url || '',
|
|
1215
|
-
};
|
|
1216
|
-
return !_.isEqual(currentPersonalData, originalPersonalData);
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
function getBusinessAndServicesData() {
|
|
1220
|
-
const getCurrentTestimonials = () =>
|
|
1221
|
-
_$w('#testimonialRepeater')
|
|
1222
|
-
.data.filter(item => item.isAdd === false)
|
|
1223
|
-
.map(item => item.text)
|
|
1224
|
-
.filter(Boolean) || itemMemberObj.testimonial;
|
|
1225
|
-
|
|
1226
|
-
return {
|
|
1227
|
-
showBusinessName: _$w('#businessNameCheckbox').checked,
|
|
1228
|
-
businessName: _$w('#businessNameInput').value,
|
|
1229
|
-
showABMP: _$w('#yearJoinedcheckbox').checked,
|
|
1230
|
-
profileImage: uploadedImages.profileImage,
|
|
1231
|
-
logoImage: uploadedImages.logoImage,
|
|
1232
|
-
bannerImages: uploadedImages.bannerImage ? [uploadedImages.bannerImage] : [],
|
|
1233
|
-
areasOfPractices: selectedServices.map(service => service.label),
|
|
1234
|
-
aboutService: _$w('#aboutInput').value,
|
|
1235
|
-
testimonial: getCurrentTestimonials(),
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
async function savePersonalDetails() {
|
|
1240
|
-
const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
|
|
1241
|
-
const personalChanges = getPersonalData();
|
|
1242
|
-
const originalUrl = beforeData.url;
|
|
1243
|
-
|
|
1244
|
-
// Only send personal fields + required identifiers (partial update)
|
|
1245
|
-
const formData = {
|
|
1246
|
-
_id: itemMemberObj._id,
|
|
1247
|
-
memberId: itemMemberObj.memberId,
|
|
1248
|
-
...personalChanges,
|
|
1249
|
-
};
|
|
1250
|
-
|
|
1251
|
-
trackClick({
|
|
1252
|
-
pageName: PAGE_NAME,
|
|
1253
|
-
buttonName: BUTTON_NAMES.PERSONAL,
|
|
1254
|
-
data: formData,
|
|
1255
|
-
});
|
|
1256
|
-
|
|
1257
|
-
// Log the specific personal data changes
|
|
1258
|
-
console.group('Personal Details Save Attempt');
|
|
1259
|
-
console.log('Current Data:', beforeData);
|
|
1260
|
-
console.log('Changes Being Applied:', personalChanges);
|
|
1261
|
-
console.log('Final Form Data (partial):', formData);
|
|
1262
|
-
console.groupEnd();
|
|
1263
|
-
|
|
1264
|
-
const result = await saveData(formData);
|
|
1265
|
-
|
|
1266
|
-
if (result.success) {
|
|
1267
|
-
formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.PERSONAL.section] = false;
|
|
1268
|
-
|
|
1269
|
-
if (personalChanges.url && personalChanges.url !== originalUrl) {
|
|
1270
|
-
const newProfileLink = `${baseUrl}/profile/${personalChanges.url}`;
|
|
1271
|
-
console.log('🔗 Updating profile link:', {
|
|
1272
|
-
originalUrl,
|
|
1273
|
-
newUrl: personalChanges.url,
|
|
1274
|
-
newProfileLink,
|
|
1275
|
-
});
|
|
1276
|
-
_$w('#profileLink').text = newProfileLink;
|
|
1277
|
-
_$w('#profileLink').link = newProfileLink;
|
|
1278
|
-
_$w('#urlWebsiteText').text = newProfileLink;
|
|
1279
|
-
|
|
1280
|
-
_$w(SLUG_FLAGS.VALID).collapse();
|
|
1281
|
-
_$w(SLUG_FLAGS.INVALID).collapse();
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
_$w('#savePersonalButton').disable();
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
handleSaveDataFeedback(_$w('#personalMessage'), result.message);
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
async function saveBusinessServices() {
|
|
1291
|
-
const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
|
|
1292
|
-
const businessChanges = getBusinessAndServicesData();
|
|
1293
|
-
|
|
1294
|
-
// Only send business fields + required identifiers (partial update)
|
|
1295
|
-
const formData = {
|
|
1296
|
-
_id: itemMemberObj._id,
|
|
1297
|
-
memberId: itemMemberObj.memberId,
|
|
1298
|
-
...businessChanges,
|
|
1299
|
-
};
|
|
1300
|
-
|
|
1301
|
-
trackClick({
|
|
1302
|
-
pageName: PAGE_NAME,
|
|
1303
|
-
buttonName: BUTTON_NAMES.BUSINESS,
|
|
1304
|
-
data: formData,
|
|
1305
|
-
});
|
|
1306
|
-
|
|
1307
|
-
// Log the specific business data changes
|
|
1308
|
-
console.group('Business Services Save Attempt');
|
|
1309
|
-
console.log('Current Data:', beforeData);
|
|
1310
|
-
console.log('Changes Being Applied:', businessChanges);
|
|
1311
|
-
console.log('Final Form Data (partial):', formData);
|
|
1312
|
-
console.log('Image Changes:', {
|
|
1313
|
-
profileImage: uploadedImages.profileImage,
|
|
1314
|
-
logoImage: uploadedImages.logoImage,
|
|
1315
|
-
bannerImage: uploadedImages.bannerImage,
|
|
1316
|
-
});
|
|
1317
|
-
console.log('Services Selected:', selectedServices);
|
|
1318
|
-
console.groupEnd();
|
|
1319
|
-
|
|
1320
|
-
const result = await saveData(formData);
|
|
1321
|
-
if (result.success) {
|
|
1322
|
-
formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES.section] = false;
|
|
1323
|
-
_$w('#saveBusinessButton').disable();
|
|
1324
|
-
}
|
|
1325
|
-
handleSaveDataFeedback(_$w('#businessMessage'), result.message);
|
|
1326
|
-
_$w('#businessNameText').text = formData.businessName || DEFAULT_BUSINESS_NAME_TEXT;
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
function setupTestimonials() {
|
|
1330
|
-
const addTestimonialButton = _$w('#addTestimonialButton');
|
|
1331
|
-
|
|
1332
|
-
addTestimonialButton.onClick(handleAddTestimonial);
|
|
1333
|
-
_$w('#deleteTestimonialButton').onClick(async event => {
|
|
1334
|
-
const data = _$w('#testimonialRepeater').data || [];
|
|
1335
|
-
const clickedIndex = data.findIndex(item => item._id === event.context.itemId);
|
|
1336
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
1337
|
-
|
|
1338
|
-
if (
|
|
1339
|
-
result &&
|
|
1340
|
-
result.toDelete &&
|
|
1341
|
-
clickedIndex > 0 &&
|
|
1342
|
-
Array.isArray(itemMemberObj.testimonial)
|
|
1343
|
-
) {
|
|
1344
|
-
itemMemberObj.testimonial.splice(clickedIndex - 1, 1);
|
|
1345
|
-
renderTestimonials();
|
|
1346
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
1347
|
-
}
|
|
1348
|
-
});
|
|
1349
|
-
|
|
1350
|
-
renderTestimonials();
|
|
1351
|
-
_$w('#testimonialRepeater').onItemReady(($item, itemData) => {
|
|
1352
|
-
const msb = $item('#testimonialMSB');
|
|
1353
|
-
if (itemData.isAdd) {
|
|
1354
|
-
msb.changeState(TESTIMONIAL_STATES.ADD);
|
|
1355
|
-
} else {
|
|
1356
|
-
msb.changeState(TESTIMONIAL_STATES.VIEW);
|
|
1357
|
-
$item('#testimonialText').text = itemData.text;
|
|
1358
|
-
}
|
|
1359
|
-
});
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
function renderTestimonials() {
|
|
1363
|
-
const testimonials =
|
|
1364
|
-
itemMemberObj.testimonial === null
|
|
1365
|
-
? []
|
|
1366
|
-
: Array.isArray(itemMemberObj.testimonial)
|
|
1367
|
-
? itemMemberObj.testimonial
|
|
1368
|
-
: [];
|
|
1369
|
-
const addItem = { _id: 'add-item', text: '', isAdd: true };
|
|
1370
|
-
const testimonialData = [
|
|
1371
|
-
addItem,
|
|
1372
|
-
...testimonials.map(text => ({ _id: generateId(), text, isAdd: false })),
|
|
1373
|
-
];
|
|
1374
|
-
|
|
1375
|
-
setupRepeater('#testimonialRepeater', testimonialData);
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
function handleAddTestimonial(event) {
|
|
1379
|
-
const $clickedItem = _$w.at(event.context);
|
|
1380
|
-
const input = $clickedItem('#testimonialsInput');
|
|
1381
|
-
const newText = input.value;
|
|
1382
|
-
|
|
1383
|
-
if (newText?.trim()) {
|
|
1384
|
-
// Initialize testimonial array if it doesn't exist
|
|
1385
|
-
if (!itemMemberObj.testimonial) {
|
|
1386
|
-
itemMemberObj.testimonial = [];
|
|
1387
|
-
}
|
|
1388
|
-
itemMemberObj.testimonial.push(newText.trim());
|
|
1389
|
-
input.value = '';
|
|
1390
|
-
renderTestimonials();
|
|
1391
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.BUSINESS_SERVICES);
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
function setContactBooking(fullProfilePageLink) {
|
|
1396
|
-
// derive booleans only once
|
|
1397
|
-
const showWixUrlCheckbox = !itemMemberObj.showWebsite && itemMemberObj.showWixUrl;
|
|
1398
|
-
const showExistingUrlCheckbox = itemMemberObj.showWebsite;
|
|
1399
|
-
_$w('#contactFormEmailInput').required = true;
|
|
1400
|
-
// basic fields
|
|
1401
|
-
_$w('#showCotactFormCheckbox').checked = itemMemberObj.showContactForm;
|
|
1402
|
-
_$w('#contactFormEmailInput').value = itemMemberObj.contactFormEmail;
|
|
1403
|
-
_$w('#schedulingLinkInput').value = itemMemberObj.bookingUrl;
|
|
1404
|
-
|
|
1405
|
-
// URL part
|
|
1406
|
-
_$w('#UrlInput').value = itemMemberObj.website || '';
|
|
1407
|
-
_$w('#showUrlWixCheckbox').checked = showWixUrlCheckbox;
|
|
1408
|
-
_$w('#showExsistingUrlCheckbox').checked = showExistingUrlCheckbox;
|
|
1409
|
-
_$w('#urlWebsiteText').text = fullProfilePageLink;
|
|
1410
|
-
|
|
1411
|
-
// custom validation for url inputs and email
|
|
1412
|
-
handleOnCustomValidation(_$w('#UrlInput'));
|
|
1413
|
-
handleOnCustomValidation(_$w('#schedulingLinkInput'));
|
|
1414
|
-
|
|
1415
|
-
// enable/disable & styling in one pass
|
|
1416
|
-
if (showWixUrlCheckbox) {
|
|
1417
|
-
_$w('#UrlInput').disable();
|
|
1418
|
-
_$w('#urlWebsiteText').customClassList.add('highlighted-text');
|
|
1419
|
-
} else if (showExistingUrlCheckbox) {
|
|
1420
|
-
_$w('#UrlInput').enable();
|
|
1421
|
-
_$w('#urlWebsiteText').customClassList.remove('highlighted-text');
|
|
1422
|
-
} else {
|
|
1423
|
-
// neither checked: disable input & remove highlight
|
|
1424
|
-
_$w('#UrlInput').disable();
|
|
1425
|
-
_$w('#urlWebsiteText').customClassList.remove('highlighted-text');
|
|
1426
|
-
}
|
|
1427
|
-
syncOtherWebsiteUrlValidity();
|
|
1428
|
-
|
|
1429
|
-
// clear buttons
|
|
1430
|
-
_$w('#clearSchedulingLinkInput').onClick(() => {
|
|
1431
|
-
_$w('#schedulingLinkInput').value = '';
|
|
1432
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1433
|
-
});
|
|
1434
|
-
_$w('#clearExistingUrlLinkInput').onClick(() => {
|
|
1435
|
-
_$w('#UrlInput').value = '';
|
|
1436
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1437
|
-
});
|
|
1438
|
-
|
|
1439
|
-
// toggle handlers
|
|
1440
|
-
_$w('#showUrlWixCheckbox').onChange(e => {
|
|
1441
|
-
if (e.target.checked) {
|
|
1442
|
-
_$w('#showExsistingUrlCheckbox').checked = false;
|
|
1443
|
-
_$w('#UrlInput').disable();
|
|
1444
|
-
_$w('#urlWebsiteText').customClassList.add('highlighted-text');
|
|
1445
|
-
}
|
|
1446
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1447
|
-
});
|
|
1448
|
-
_$w('#showExsistingUrlCheckbox').onChange(e => {
|
|
1449
|
-
if (e.target.checked) {
|
|
1450
|
-
_$w('#showUrlWixCheckbox').checked = false;
|
|
1451
|
-
_$w('#UrlInput').enable();
|
|
1452
|
-
_$w('#urlWebsiteText').customClassList.remove('highlighted-text');
|
|
1453
|
-
}
|
|
1454
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1455
|
-
});
|
|
1456
|
-
|
|
1457
|
-
setupAddressRepeater();
|
|
1458
|
-
setupPhoneRepeater();
|
|
1459
|
-
_$w('#saveContactBookingButton').onClick(saveContactBooking);
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
/**
|
|
1463
|
-
* Converts our internal address format to AddressInput component format
|
|
1464
|
-
*/
|
|
1465
|
-
function convertToAddressInputFormat(address) {
|
|
1466
|
-
if (!address) return null;
|
|
1467
|
-
|
|
1468
|
-
if (!address.line1 && !address.city && !address.state && !address.postalcode) {
|
|
1469
|
-
return null;
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
const formatted = formatFullAddress(address);
|
|
1473
|
-
|
|
1474
|
-
const hasValidCoordinates = Boolean(address.latitude && address.longitude);
|
|
1475
|
-
const location = hasValidCoordinates
|
|
1476
|
-
? { latitude: address.latitude, longitude: address.longitude }
|
|
1477
|
-
: null;
|
|
1478
|
-
|
|
1479
|
-
return {
|
|
1480
|
-
formatted,
|
|
1481
|
-
location,
|
|
1482
|
-
streetAddress: {
|
|
1483
|
-
name: extractStreetName(address.line1),
|
|
1484
|
-
number: extractStreetNumber(address.line1),
|
|
1485
|
-
apt: address.line2 || '',
|
|
1486
|
-
},
|
|
1487
|
-
city: address.city || '',
|
|
1488
|
-
subdivision: address.state || '',
|
|
1489
|
-
country: address.country || 'US',
|
|
1490
|
-
postalCode: address.postalcode || '',
|
|
1491
|
-
};
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
/**
|
|
1495
|
-
* Converts AddressInput format back to our internal address format
|
|
1496
|
-
*/
|
|
1497
|
-
function parseAddressInput(addressInputValue, existingAddress = null) {
|
|
1498
|
-
if (!addressInputValue) return null;
|
|
1499
|
-
|
|
1500
|
-
let line1 = '';
|
|
1501
|
-
let line2 = '';
|
|
1502
|
-
|
|
1503
|
-
if (addressInputValue.streetAddress) {
|
|
1504
|
-
const number = addressInputValue.streetAddress.number || '';
|
|
1505
|
-
const name = addressInputValue.streetAddress.name || '';
|
|
1506
|
-
line1 = `${number} ${name}`.trim();
|
|
1507
|
-
|
|
1508
|
-
// Capture apartment/suite/building info from streetAddress.apt (undocumented but exists)
|
|
1509
|
-
if (addressInputValue.streetAddress.apt) {
|
|
1510
|
-
line2 = addressInputValue.streetAddress.apt;
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
if (!line1 && addressInputValue.formatted) {
|
|
1515
|
-
line1 = addressInputValue.formatted.split(',')[0]?.trim() || '';
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
// If line2 is still empty, try to extract building/suite info from formatted address
|
|
1519
|
-
if (!line2 && addressInputValue.formatted) {
|
|
1520
|
-
const formattedParts = addressInputValue.formatted.split(',').map(part => part.trim());
|
|
1521
|
-
// Look for BLDG/STE/APT/UNIT/SUITE info in the formatted parts
|
|
1522
|
-
for (let i = 1; i < formattedParts.length; i++) {
|
|
1523
|
-
const part = formattedParts[i];
|
|
1524
|
-
const lowerPart = part.toLowerCase();
|
|
1525
|
-
if (
|
|
1526
|
-
lowerPart.includes('bldg') ||
|
|
1527
|
-
lowerPart.includes('ste') ||
|
|
1528
|
-
lowerPart.includes('apt') ||
|
|
1529
|
-
lowerPart.includes('unit') ||
|
|
1530
|
-
lowerPart.includes('suite') ||
|
|
1531
|
-
lowerPart.includes('#') ||
|
|
1532
|
-
lowerPart.includes('building')
|
|
1533
|
-
) {
|
|
1534
|
-
line2 = part;
|
|
1535
|
-
break;
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
return {
|
|
1541
|
-
key: existingAddress?.key || generateId(),
|
|
1542
|
-
line1,
|
|
1543
|
-
line2: line2 || existingAddress?.line2 || '',
|
|
1544
|
-
city: addressInputValue.city || '',
|
|
1545
|
-
state: addressInputValue.subdivision || '',
|
|
1546
|
-
postalcode: addressInputValue.postalCode || '',
|
|
1547
|
-
country: addressInputValue.country || 'US',
|
|
1548
|
-
latitude: addressInputValue.location?.latitude || existingAddress?.latitude || 0,
|
|
1549
|
-
longitude: addressInputValue.location?.longitude || existingAddress?.longitude || 0,
|
|
1550
|
-
addressStatus: existingAddress?.addressStatus || FALLBACK_ADDRESS_STATUS,
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
function setupAddressRepeater() {
|
|
1555
|
-
_$w('#addressesList').onItemReady(($item, itemData, index) =>
|
|
1556
|
-
handleAddressItem($item, itemData, index)
|
|
1557
|
-
);
|
|
1558
|
-
renderAddressesList();
|
|
1559
|
-
|
|
1560
|
-
_$w('#newAddressButton').onClick(addNewAddress);
|
|
1561
|
-
|
|
1562
|
-
setupAddressRepeaterEventListeners();
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
function setupAddressRepeaterEventListeners() {
|
|
1566
|
-
_$w('#mainAddressCheckbox').onChange(async event => {
|
|
1567
|
-
const data = _$w('#addressesList').data;
|
|
1568
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1569
|
-
const $item = _$w.at(event.context);
|
|
1570
|
-
const checkbox = $item('#mainAddressCheckbox');
|
|
1571
|
-
const isBeingUnchecked = !event.target.checked;
|
|
1572
|
-
|
|
1573
|
-
if (isBeingUnchecked) {
|
|
1574
|
-
const addressDisplayOptions = itemMemberObj.addressDisplayOption || [];
|
|
1575
|
-
const mainAddressOption = addressDisplayOptions.find(opt => opt.isMain === true);
|
|
1576
|
-
|
|
1577
|
-
// If this address is the main address, prevent unchecking
|
|
1578
|
-
if (mainAddressOption && mainAddressOption.key === clickedItemData._id) {
|
|
1579
|
-
checkbox.checked = true;
|
|
1580
|
-
await wixWindow.openLightbox(LIGHTBOX_NAMES.MAIN_ADDRESS_ERROR);
|
|
1581
|
-
return;
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
_$w('#mainAddressCheckbox').checked = false;
|
|
1586
|
-
checkbox.checked = true;
|
|
1587
|
-
|
|
1588
|
-
// Primary address cannot be hidden: update model and repeater data so UI and save stay in sync
|
|
1589
|
-
if (clickedItemData.address.addressStatus === ADDRESS_STATUS_TYPES.DONT_SHOW) {
|
|
1590
|
-
updateAddressStatus(clickedItemData._id, ADDRESS_STATUS_TYPES.STATE_CITY_ZIP);
|
|
1591
|
-
$item('#addressStatusOptions').value = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
1592
|
-
const currentData = _$w('#addressesList').data || [];
|
|
1593
|
-
const dataWithStatusFixed = currentData.map(item =>
|
|
1594
|
-
item._id === clickedItemData._id
|
|
1595
|
-
? {
|
|
1596
|
-
...item,
|
|
1597
|
-
address: {
|
|
1598
|
-
...item.address,
|
|
1599
|
-
addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
|
|
1600
|
-
},
|
|
1601
|
-
}
|
|
1602
|
-
: item
|
|
1603
|
-
);
|
|
1604
|
-
_$w('#addressesList').data = dataWithStatusFixed;
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
updateMainAddressSelection(clickedItemData._id);
|
|
1608
|
-
refreshAddressListIsMainState();
|
|
1609
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
|
-
_$w('#addressStatusOptions').onChange(event => {
|
|
1613
|
-
const data = _$w('#addressesList').data || [];
|
|
1614
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1615
|
-
const newStatus = event.target.value;
|
|
1616
|
-
const $item = _$w.at(event.context);
|
|
1617
|
-
const isMain = $item('#mainAddressCheckbox').checked;
|
|
1618
|
-
|
|
1619
|
-
if (isMain && newStatus === ADDRESS_STATUS_TYPES.DONT_SHOW) {
|
|
1620
|
-
$item('#addressStatusOptions').value = ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
1621
|
-
return;
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
updateAddressStatus(clickedItemData._id, newStatus);
|
|
1625
|
-
|
|
1626
|
-
// Keep repeater data in sync (required for new addresses not yet in itemMemberObj.addresses)
|
|
1627
|
-
const updatedData = data.map(item =>
|
|
1628
|
-
item._id === clickedItemData._id
|
|
1629
|
-
? { ...item, address: { ...item.address, addressStatus: newStatus } }
|
|
1630
|
-
: item
|
|
1631
|
-
);
|
|
1632
|
-
_$w('#addressesList').data = updatedData;
|
|
1633
|
-
|
|
1634
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1635
|
-
});
|
|
1636
|
-
|
|
1637
|
-
_$w('#addressItemEditBtn').onClick(event => {
|
|
1638
|
-
const data = _$w('#addressesList').data;
|
|
1639
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1640
|
-
const $item = _$w.at(event.context);
|
|
1641
|
-
$item('#addressItemStates').changeState(ADDRESS_STATES.EDIT);
|
|
1642
|
-
const addressInputValue = convertToAddressInputFormat(clickedItemData.address);
|
|
1643
|
-
$item('#addressEditInput').value = addressInputValue;
|
|
1644
|
-
});
|
|
1645
|
-
|
|
1646
|
-
_$w('#addressItemRemoveBtn').onClick(async event => {
|
|
1647
|
-
const data = _$w('#addressesList').data || [];
|
|
1648
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1649
|
-
if (data.length <= 1 || clickedItemData?.isMain) {
|
|
1650
|
-
return; // At least one address required; primary address cannot be deleted
|
|
1651
|
-
}
|
|
1652
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
1653
|
-
if (result && result.toDelete) {
|
|
1654
|
-
removeAddress(clickedItemData._id);
|
|
1655
|
-
}
|
|
1656
|
-
});
|
|
1657
|
-
|
|
1658
|
-
_$w('#addressEditInput').onChange(event => {
|
|
1659
|
-
const $item = _$w.at(event.context);
|
|
1660
|
-
validateAddressCompleteness($item);
|
|
1661
|
-
});
|
|
1662
|
-
|
|
1663
|
-
_$w('#addressEditCancelBtn').onClick(event => {
|
|
1664
|
-
const data = _$w('#addressesList').data;
|
|
1665
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1666
|
-
const $item = _$w.at(event.context);
|
|
1667
|
-
if (clickedItemData.isNewAddress) {
|
|
1668
|
-
removeNewAddressFromRepeater(clickedItemData._id);
|
|
1669
|
-
} else {
|
|
1670
|
-
$item('#addressItemStates').changeState(ADDRESS_STATES.VIEW);
|
|
1671
|
-
$item('#addressValidationMessage').hide();
|
|
1672
|
-
}
|
|
1673
|
-
});
|
|
1674
|
-
|
|
1675
|
-
_$w('#addressEditSaveBtn').onClick(event => {
|
|
1676
|
-
const data = _$w('#addressesList').data;
|
|
1677
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
1678
|
-
const $item = _$w.at(event.context);
|
|
1679
|
-
saveAddressFromSingleInput($item, clickedItemData);
|
|
1680
|
-
});
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
function addNewAddress() {
|
|
1684
|
-
const currentData = _$w('#addressesList').data || [];
|
|
1685
|
-
|
|
1686
|
-
if (currentData.length >= MAX_ADDRESSES_COUNT) {
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
const newAddressId = generateId();
|
|
1691
|
-
|
|
1692
|
-
const newAddress = {
|
|
1693
|
-
key: newAddressId,
|
|
1694
|
-
line1: '',
|
|
1695
|
-
line2: '',
|
|
1696
|
-
city: '',
|
|
1697
|
-
state: '',
|
|
1698
|
-
postalcode: '',
|
|
1699
|
-
country: 'US',
|
|
1700
|
-
latitude: 0,
|
|
1701
|
-
longitude: 0,
|
|
1702
|
-
addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
|
|
1703
|
-
};
|
|
1704
|
-
|
|
1705
|
-
const isFirstAddress = currentData.length === 0;
|
|
1706
|
-
const newAddressItem = {
|
|
1707
|
-
_id: newAddressId,
|
|
1708
|
-
address: newAddress,
|
|
1709
|
-
isMain: isFirstAddress,
|
|
1710
|
-
addressStatus: ADDRESS_STATUS_TYPES.STATE_CITY_ZIP,
|
|
1711
|
-
isNewAddress: true,
|
|
1712
|
-
};
|
|
1713
|
-
|
|
1714
|
-
if (isFirstAddress) {
|
|
1715
|
-
updateMainAddressSelection(newAddressId);
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
renderAddressesList([...currentData, newAddressItem]);
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
function handleAddressItem($item, itemData, index) {
|
|
1722
|
-
const multiStateBox = $item('#addressItemStates');
|
|
1723
|
-
|
|
1724
|
-
setupAddressViewState($item, itemData, index);
|
|
1725
|
-
setupAddressEditState($item, itemData, index);
|
|
1726
|
-
|
|
1727
|
-
if (itemData.isNewAddress) {
|
|
1728
|
-
multiStateBox.changeState(ADDRESS_STATES.EDIT);
|
|
1729
|
-
} else {
|
|
1730
|
-
multiStateBox.changeState(ADDRESS_STATES.VIEW);
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
function setupAddressViewState($item, itemData, index) {
|
|
1735
|
-
const formattedAddress = formatFullAddress(itemData.address);
|
|
1736
|
-
|
|
1737
|
-
$item('#addressItemtext').text = formattedAddress;
|
|
1738
|
-
$item('#addressItemNumber').text = `Location ${index + 1}`;
|
|
1739
|
-
$item('#mainAddressCheckbox').checked = itemData.isMain || false;
|
|
1740
|
-
|
|
1741
|
-
const addressStatus = itemData.address.addressStatus || ADDRESS_STATUS_TYPES.STATE_CITY_ZIP;
|
|
1742
|
-
$item('#addressStatusOptions').value = addressStatus;
|
|
1743
|
-
|
|
1744
|
-
const addressCount = (_$w('#addressesList').data || []).length;
|
|
1745
|
-
const canDelete = addressCount > 1 && !itemData.isMain;
|
|
1746
|
-
if (canDelete) {
|
|
1747
|
-
$item('#addressItemRemoveBtn').enable();
|
|
1748
|
-
} else {
|
|
1749
|
-
$item('#addressItemRemoveBtn').disable();
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
|
|
1753
|
-
function setupAddressEditState($item, itemData, _index) {
|
|
1754
|
-
$item('#addressEditInput').enable();
|
|
1755
|
-
|
|
1756
|
-
const addressInputValue = convertToAddressInputFormat(itemData.address);
|
|
1757
|
-
$item('#addressEditInput').value = addressInputValue;
|
|
1758
|
-
|
|
1759
|
-
$item('#addressValidationMessage').hide();
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
function validateAddressCompleteness($item) {
|
|
1763
|
-
const addressInput = $item('#addressEditInput');
|
|
1764
|
-
const saveBtn = $item('#addressEditSaveBtn');
|
|
1765
|
-
const validationMessage = $item('#addressValidationMessage');
|
|
1766
|
-
|
|
1767
|
-
const addressValue = addressInput.value;
|
|
1768
|
-
|
|
1769
|
-
if (!addressValue) {
|
|
1770
|
-
showAddressValidationError(validationMessage, saveBtn, 'Please provide a complete address');
|
|
1771
|
-
return false;
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
const missingFields = [];
|
|
1775
|
-
|
|
1776
|
-
if (!addressValue.streetAddress?.name) {
|
|
1777
|
-
missingFields.push('street name');
|
|
1778
|
-
}
|
|
1779
|
-
|
|
1780
|
-
if (!addressValue.streetAddress?.number) {
|
|
1781
|
-
missingFields.push('street number');
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
if (!addressValue.city) {
|
|
1785
|
-
missingFields.push('city');
|
|
1786
|
-
}
|
|
1787
|
-
|
|
1788
|
-
if (!addressValue.subdivision) {
|
|
1789
|
-
missingFields.push('state');
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
if (!addressValue.postalCode) {
|
|
1793
|
-
missingFields.push('postal code');
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
if (!addressValue.location?.latitude || !addressValue.location?.longitude) {
|
|
1797
|
-
missingFields.push('valid location details');
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1800
|
-
if (missingFields.length > 0) {
|
|
1801
|
-
const message = `Please provide: ${missingFields.join(', ')}`;
|
|
1802
|
-
showAddressValidationError(validationMessage, saveBtn, message);
|
|
1803
|
-
return false;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
hideAddressValidationError(validationMessage, saveBtn);
|
|
1807
|
-
return true;
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
function showAddressValidationError(validationMessage, saveBtn, message) {
|
|
1811
|
-
validationMessage.text = message;
|
|
1812
|
-
validationMessage.show();
|
|
1813
|
-
saveBtn.disable();
|
|
1814
|
-
}
|
|
1815
|
-
|
|
1816
|
-
function hideAddressValidationError(validationMessage, saveBtn) {
|
|
1817
|
-
validationMessage.hide();
|
|
1818
|
-
saveBtn.enable();
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
function saveAddressFromSingleInput($item, itemData) {
|
|
1822
|
-
if (!validateAddressCompleteness($item)) {
|
|
1823
|
-
return;
|
|
1824
|
-
}
|
|
1825
|
-
|
|
1826
|
-
const addressInput = $item('#addressEditInput');
|
|
1827
|
-
const addressValue = addressInput.value;
|
|
1828
|
-
|
|
1829
|
-
const convertedAddress = parseAddressInput(addressValue, itemData.address);
|
|
1830
|
-
|
|
1831
|
-
const formattedAddress = formatFullAddress(convertedAddress);
|
|
1832
|
-
$item('#addressItemtext').text = formattedAddress;
|
|
1833
|
-
|
|
1834
|
-
if (itemData.isNewAddress) {
|
|
1835
|
-
setNewAddress(itemData._id, convertedAddress);
|
|
1836
|
-
} else {
|
|
1837
|
-
updateAddress(itemData._id, convertedAddress);
|
|
1838
|
-
}
|
|
1839
|
-
|
|
1840
|
-
$item('#addressItemStates').changeState(ADDRESS_STATES.VIEW);
|
|
1841
|
-
$item('#addressValidationMessage').hide();
|
|
1842
|
-
}
|
|
1843
|
-
|
|
1844
|
-
function removeNewAddressFromRepeater(addressId) {
|
|
1845
|
-
const currentData = _$w('#addressesList').data || [];
|
|
1846
|
-
const updatedData = currentData.filter(item => item._id !== addressId);
|
|
1847
|
-
_$w('#addressesList').data = updatedData;
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
function setNewAddress(addressId, addressData) {
|
|
1851
|
-
if (!itemMemberObj.addresses) {
|
|
1852
|
-
itemMemberObj.addresses = [];
|
|
1853
|
-
}
|
|
1854
|
-
const isFirstAddress = itemMemberObj.addresses.length === 0;
|
|
1855
|
-
itemMemberObj.addresses.push(addressData);
|
|
1856
|
-
|
|
1857
|
-
if (!itemMemberObj.addressDisplayOption) {
|
|
1858
|
-
itemMemberObj.addressDisplayOption = [];
|
|
1859
|
-
}
|
|
1860
|
-
const existingOption = itemMemberObj.addressDisplayOption.find(
|
|
1861
|
-
opt => opt.key === (addressData.key || addressId)
|
|
1862
|
-
);
|
|
1863
|
-
if (!existingOption) {
|
|
1864
|
-
itemMemberObj.addressDisplayOption.push({
|
|
1865
|
-
key: addressData.key || addressId,
|
|
1866
|
-
isMain: isFirstAddress,
|
|
1867
|
-
});
|
|
1868
|
-
}
|
|
1869
|
-
|
|
1870
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
function extractStreetNumber(line1) {
|
|
1874
|
-
if (!line1) return '';
|
|
1875
|
-
const match = line1.match(/^\d+/);
|
|
1876
|
-
return match ? match[0] : '';
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
function extractStreetName(line1) {
|
|
1880
|
-
if (!line1) return '';
|
|
1881
|
-
return line1.replace(/^\d+\s*/, '').trim();
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
function renderAddressesList(updatedAddresses) {
|
|
1885
|
-
let addressData = updatedAddresses || [];
|
|
1886
|
-
|
|
1887
|
-
if (!addressData || addressData?.length === 0) {
|
|
1888
|
-
const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
|
|
1889
|
-
const displayOptions = Array.isArray(itemMemberObj.addressDisplayOption)
|
|
1890
|
-
? itemMemberObj.addressDisplayOption
|
|
1891
|
-
: [];
|
|
1892
|
-
|
|
1893
|
-
addressData = addresses.map((address, index) => {
|
|
1894
|
-
const displayOption = displayOptions.find(opt => opt.key === address.key);
|
|
1895
|
-
return {
|
|
1896
|
-
_id: address.key || `address_${index}`,
|
|
1897
|
-
address,
|
|
1898
|
-
isMain: displayOption?.isMain || false,
|
|
1899
|
-
addressStatus: address.addressStatus || FALLBACK_ADDRESS_STATUS,
|
|
1900
|
-
isNewAddress: false,
|
|
1901
|
-
};
|
|
1902
|
-
});
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
const repeater = _$w('#addressesList');
|
|
1906
|
-
repeater.data = addressData;
|
|
1907
|
-
|
|
1908
|
-
updateAddressAddButtonState();
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
function updateAddressAddButtonState() {
|
|
1912
|
-
const currentData = _$w('#addressesList').data || [];
|
|
1913
|
-
const newAddressButton = _$w('#newAddressButton');
|
|
1914
|
-
|
|
1915
|
-
if (currentData.length >= MAX_ADDRESSES_COUNT) {
|
|
1916
|
-
newAddressButton.disable();
|
|
1917
|
-
} else {
|
|
1918
|
-
newAddressButton.enable();
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
function updateMainAddressSelection(selectedId) {
|
|
1923
|
-
if (!itemMemberObj.addressDisplayOption) {
|
|
1924
|
-
itemMemberObj.addressDisplayOption = [];
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
itemMemberObj.addressDisplayOption.forEach(option => {
|
|
1928
|
-
option.isMain = false;
|
|
1929
|
-
});
|
|
1930
|
-
|
|
1931
|
-
let selectedOption = itemMemberObj.addressDisplayOption.find(opt => opt.key === selectedId);
|
|
1932
|
-
|
|
1933
|
-
// If the option doesn't exist, create it
|
|
1934
|
-
if (!selectedOption) {
|
|
1935
|
-
selectedOption = { key: selectedId, isMain: false };
|
|
1936
|
-
itemMemberObj.addressDisplayOption.push(selectedOption);
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
selectedOption.isMain = true;
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
/**
|
|
1943
|
-
* Refreshes each repeater item's isMain from itemMemberObj.addressDisplayOption,
|
|
1944
|
-
* updates repeater data, and explicitly updates each item's delete button state
|
|
1945
|
-
* (repeater may not re-run onItemReady when only isMain changes).
|
|
1946
|
-
* Uses forEachItem because Wix Repeater does not have .at(index).
|
|
1947
|
-
*/
|
|
1948
|
-
function refreshAddressListIsMainState() {
|
|
1949
|
-
const repeater = _$w('#addressesList');
|
|
1950
|
-
const currentData = repeater.data || [];
|
|
1951
|
-
const displayOptions = itemMemberObj.addressDisplayOption || [];
|
|
1952
|
-
const updatedData = currentData.map(item => {
|
|
1953
|
-
const opt = displayOptions.find(o => o.key === item._id);
|
|
1954
|
-
return { ...item, isMain: opt?.isMain || false };
|
|
1955
|
-
});
|
|
1956
|
-
repeater.data = updatedData;
|
|
1957
|
-
|
|
1958
|
-
const addressCount = updatedData.length;
|
|
1959
|
-
repeater.forEachItem(($item, _itemData, index) => {
|
|
1960
|
-
const isMain = index < updatedData.length ? updatedData[index].isMain : false;
|
|
1961
|
-
const canDelete = addressCount > 1 && !isMain;
|
|
1962
|
-
if (canDelete) {
|
|
1963
|
-
$item('#addressItemRemoveBtn').enable();
|
|
1964
|
-
} else {
|
|
1965
|
-
$item('#addressItemRemoveBtn').disable();
|
|
1966
|
-
}
|
|
1967
|
-
});
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
function updateAddressStatus(addressId, newStatus) {
|
|
1971
|
-
const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
|
|
1972
|
-
const addressIndex = addresses.findIndex(
|
|
1973
|
-
addr => (addr.key || `address_${addresses.indexOf(addr)}`) === addressId
|
|
1974
|
-
);
|
|
1975
|
-
|
|
1976
|
-
if (addressIndex !== -1) {
|
|
1977
|
-
itemMemberObj.addresses[addressIndex].addressStatus = newStatus;
|
|
1978
|
-
}
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
function updateAddress(addressId, newAddressValue) {
|
|
1982
|
-
const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
|
|
1983
|
-
const addressIndex = addresses.findIndex(
|
|
1984
|
-
addr => (addr.key || `address_${addresses.indexOf(addr)}`) === addressId
|
|
1985
|
-
);
|
|
1986
|
-
|
|
1987
|
-
if (addressIndex !== -1) {
|
|
1988
|
-
itemMemberObj.addresses[addressIndex] = {
|
|
1989
|
-
...itemMemberObj.addresses[addressIndex],
|
|
1990
|
-
...newAddressValue,
|
|
1991
|
-
};
|
|
1992
|
-
|
|
1993
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
|
|
1997
|
-
function removeAddress(addressId) {
|
|
1998
|
-
const currentData = _$w('#addressesList').data || [];
|
|
1999
|
-
const itemToRemove = currentData.find(item => item._id === addressId);
|
|
2000
|
-
if (currentData.length <= 1 || itemToRemove?.isMain) {
|
|
2001
|
-
return; // At least one address required; primary address cannot be deleted
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
|
-
if (itemMemberObj.addresses) {
|
|
2005
|
-
itemMemberObj.addresses = itemMemberObj.addresses.filter(
|
|
2006
|
-
addr => (addr.key || `address_${itemMemberObj.addresses.indexOf(addr)}`) !== addressId
|
|
2007
|
-
);
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
if (itemMemberObj.addressDisplayOption) {
|
|
2011
|
-
itemMemberObj.addressDisplayOption = itemMemberObj.addressDisplayOption.filter(
|
|
2012
|
-
opt => opt.key !== addressId
|
|
2013
|
-
);
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
renderAddressesList();
|
|
2017
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2018
|
-
}
|
|
2019
|
-
|
|
2020
|
-
function formatFullAddress(addr) {
|
|
2021
|
-
if (!addr) return '';
|
|
2022
|
-
|
|
2023
|
-
const parts = [];
|
|
2024
|
-
|
|
2025
|
-
if (addr.line1) parts.push(addr.line1);
|
|
2026
|
-
if (addr.line2) parts.push(addr.line2); // Include building/suite info
|
|
2027
|
-
if (addr.city) parts.push(addr.city);
|
|
2028
|
-
if (addr.state && addr.postalcode) {
|
|
2029
|
-
parts.push(`${addr.state} ${addr.postalcode}`);
|
|
2030
|
-
} else if (addr.state) {
|
|
2031
|
-
parts.push(addr.state);
|
|
2032
|
-
} else if (addr.postalcode) {
|
|
2033
|
-
parts.push(addr.postalcode);
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
return parts.join(', ') || 'No address entered';
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
function setupPhoneRepeater() {
|
|
2040
|
-
_$w('#phoneNumbersList').onItemReady(handlePhoneItem);
|
|
2041
|
-
renderPhonesList();
|
|
2042
|
-
|
|
2043
|
-
_$w('#addPhoneButton').onClick(addNewPhone);
|
|
2044
|
-
|
|
2045
|
-
setupPhoneRepeaterEventListeners();
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
function setupPhoneRepeaterEventListeners() {
|
|
2049
|
-
_$w('#phoneInput').onInput(event => {
|
|
2050
|
-
const data = _$w('#phoneNumbersList').data;
|
|
2051
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
2052
|
-
const digits = digitsFromPhoneInput(event.target.value);
|
|
2053
|
-
const formatted = formatUsPhoneInput(digits);
|
|
2054
|
-
if (event.target.value !== formatted) {
|
|
2055
|
-
event.target.value = formatted;
|
|
2056
|
-
}
|
|
2057
|
-
|
|
2058
|
-
updatePhoneNumber(clickedItemData._id, formatted);
|
|
2059
|
-
|
|
2060
|
-
if (clickedItemData.isNewPhone && digits.length > 0) {
|
|
2061
|
-
addNewPhoneToData(clickedItemData._id, formatted);
|
|
2062
|
-
clickedItemData.isNewPhone = false;
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2066
|
-
});
|
|
2067
|
-
|
|
2068
|
-
_$w('#showPhoneCheckbox').onChange(event => {
|
|
2069
|
-
const data = _$w('#phoneNumbersList').data || [];
|
|
2070
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
2071
|
-
if (!clickedItemData) {
|
|
2072
|
-
return;
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
const $item = _$w.at(event.context);
|
|
2076
|
-
const isChecked = event.target.checked;
|
|
2077
|
-
let updated;
|
|
2078
|
-
if (isChecked) {
|
|
2079
|
-
// Same pattern as #mainAddressCheckbox: clear every row, then select one (repeater
|
|
2080
|
-
// often does not refresh sibling items' controls when only `data` changes).
|
|
2081
|
-
_$w('#showPhoneCheckbox').checked = false;
|
|
2082
|
-
$item('#showPhoneCheckbox').checked = true;
|
|
2083
|
-
updated = data.map(item =>
|
|
2084
|
-
item._id === clickedItemData._id
|
|
2085
|
-
? { ...item, showPhone: true }
|
|
2086
|
-
: { ...item, showPhone: false }
|
|
2087
|
-
);
|
|
2088
|
-
updateShowPhoneSelection(clickedItemData._id, true);
|
|
2089
|
-
} else {
|
|
2090
|
-
updated = data.map(item =>
|
|
2091
|
-
item._id === clickedItemData._id ? { ...item, showPhone: false } : item
|
|
2092
|
-
);
|
|
2093
|
-
updateShowPhoneSelection(clickedItemData._id, false);
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
|
-
renderPhonesList(updated);
|
|
2097
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2098
|
-
});
|
|
2099
|
-
|
|
2100
|
-
_$w('#removePhoneBtn').onClick(async event => {
|
|
2101
|
-
const data = _$w('#phoneNumbersList').data;
|
|
2102
|
-
const clickedItemData = data.find(item => item._id === event.context.itemId);
|
|
2103
|
-
const result = await wixWindow.openLightbox(LIGHTBOX_NAMES.DELETE_CONFIRM);
|
|
2104
|
-
if (result && result.toDelete) {
|
|
2105
|
-
removePhone(clickedItemData._id);
|
|
2106
|
-
}
|
|
2107
|
-
});
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
function addNewPhone() {
|
|
2111
|
-
const currentData = _$w('#phoneNumbersList').data || [];
|
|
2112
|
-
|
|
2113
|
-
if (currentData.length >= MAX_PHONES_COUNT) {
|
|
2114
|
-
return;
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
const newPhoneId = generateId();
|
|
2118
|
-
const newPhoneItem = {
|
|
2119
|
-
_id: newPhoneId,
|
|
2120
|
-
phoneNumber: '',
|
|
2121
|
-
showPhone: false,
|
|
2122
|
-
isNewPhone: true,
|
|
2123
|
-
phoneIndex: currentData.length + 1,
|
|
2124
|
-
};
|
|
2125
|
-
|
|
2126
|
-
renderPhonesList([...currentData, newPhoneItem]);
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
|
-
function handlePhoneItem($item, itemData) {
|
|
2130
|
-
$item('#phoneInput').value = formatPhoneNumberForInput(itemData.phoneNumber || '');
|
|
2131
|
-
$item('#showPhoneCheckbox').checked = itemData.showPhone || false;
|
|
2132
|
-
$item('#phoneNumberLabel').text = `Phone ${itemData.phoneIndex}`;
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
/** Only one "Show your phone" row at a time; aligns UI with itemMemberObj.toShowPhone. */
|
|
2136
|
-
function normalizePhoneShowFlags(phoneData) {
|
|
2137
|
-
if (!phoneData?.length) return phoneData;
|
|
2138
|
-
|
|
2139
|
-
const cms = (itemMemberObj.toShowPhone || '').trim();
|
|
2140
|
-
if (cms) {
|
|
2141
|
-
let matched = false;
|
|
2142
|
-
return phoneData.map(p => {
|
|
2143
|
-
const t = (p.phoneNumber || '').trim();
|
|
2144
|
-
const isMatch = Boolean(t && phoneDigitsEqual(t, cms));
|
|
2145
|
-
if (isMatch && !matched) {
|
|
2146
|
-
matched = true;
|
|
2147
|
-
return { ...p, showPhone: true };
|
|
2148
|
-
}
|
|
2149
|
-
return { ...p, showPhone: false };
|
|
2150
|
-
});
|
|
2151
|
-
}
|
|
2152
|
-
|
|
2153
|
-
const trueIdxs = phoneData.map((p, i) => (p.showPhone ? i : -1)).filter(i => i >= 0);
|
|
2154
|
-
if (trueIdxs.length <= 1) return phoneData;
|
|
2155
|
-
|
|
2156
|
-
const keep = trueIdxs[0];
|
|
2157
|
-
return phoneData.map((p, i) => ({
|
|
2158
|
-
...p,
|
|
2159
|
-
showPhone: i === keep,
|
|
2160
|
-
}));
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
function renderPhonesList(updatedPhones) {
|
|
2164
|
-
let phoneData = updatedPhones || [];
|
|
2165
|
-
|
|
2166
|
-
if (!phoneData || phoneData?.length === 0) {
|
|
2167
|
-
const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
|
|
2168
|
-
|
|
2169
|
-
phoneData = phones.map((phone, index) => ({
|
|
2170
|
-
_id: `phone_${index}`,
|
|
2171
|
-
phoneNumber: phone,
|
|
2172
|
-
showPhone: phoneDigitsEqual(phone, itemMemberObj.toShowPhone),
|
|
2173
|
-
isNewPhone: false,
|
|
2174
|
-
phoneIndex: index + 1,
|
|
2175
|
-
}));
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2178
|
-
phoneData = normalizePhoneShowFlags(phoneData);
|
|
2179
|
-
|
|
2180
|
-
const repeater = _$w('#phoneNumbersList');
|
|
2181
|
-
|
|
2182
|
-
repeater.data = phoneData;
|
|
2183
|
-
refreshPhoneShowCheckboxState(phoneData);
|
|
2184
|
-
updatePhoneAddButtonState();
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
/**
|
|
2188
|
-
* Repeater may not re-run onItemReady for sibling items when only showPhone flags change;
|
|
2189
|
-
* keep each #showPhoneCheckbox in sync with data (same idea as refreshAddressListIsMainState).
|
|
2190
|
-
*/
|
|
2191
|
-
function refreshPhoneShowCheckboxState(phoneData) {
|
|
2192
|
-
const repeater = _$w('#phoneNumbersList');
|
|
2193
|
-
const data = phoneData || repeater.data || [];
|
|
2194
|
-
repeater.forEachItem(($item, _itemData, index) => {
|
|
2195
|
-
const show = index < data.length ? data[index].showPhone : false;
|
|
2196
|
-
$item('#showPhoneCheckbox').checked = Boolean(show);
|
|
2197
|
-
});
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
function updatePhoneAddButtonState() {
|
|
2201
|
-
const currentData = _$w('#phoneNumbersList').data || [];
|
|
2202
|
-
const addPhoneButton = _$w('#addPhoneButton');
|
|
2203
|
-
|
|
2204
|
-
if (currentData.length >= MAX_PHONES_COUNT) {
|
|
2205
|
-
addPhoneButton.disable();
|
|
2206
|
-
} else {
|
|
2207
|
-
addPhoneButton.enable();
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
function updatePhoneNumber(phoneId, newPhoneNumber) {
|
|
2212
|
-
const currentData = _$w('#phoneNumbersList').data || [];
|
|
2213
|
-
const itemIndex = currentData.findIndex(item => item._id === phoneId);
|
|
2214
|
-
|
|
2215
|
-
if (itemIndex !== -1) {
|
|
2216
|
-
const item = currentData[itemIndex];
|
|
2217
|
-
const prevTrimmed = (item.phoneNumber || '').trim();
|
|
2218
|
-
const newTrimmed = newPhoneNumber.trim();
|
|
2219
|
-
|
|
2220
|
-
item.phoneNumber = newPhoneNumber;
|
|
2221
|
-
|
|
2222
|
-
if (item.showPhone) {
|
|
2223
|
-
itemMemberObj.toShowPhone = newTrimmed || null;
|
|
2224
|
-
} else if (
|
|
2225
|
-
itemMemberObj.toShowPhone &&
|
|
2226
|
-
prevTrimmed &&
|
|
2227
|
-
phoneDigitsEqual(itemMemberObj.toShowPhone, prevTrimmed)
|
|
2228
|
-
) {
|
|
2229
|
-
itemMemberObj.toShowPhone = newTrimmed || null;
|
|
2230
|
-
}
|
|
2231
|
-
|
|
2232
|
-
renderPhonesList(currentData);
|
|
2233
|
-
syncPhonesFromRepeater();
|
|
2234
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
|
|
2238
|
-
function syncPhonesFromRepeater() {
|
|
2239
|
-
const phoneData = _$w('#phoneNumbersList').data || [];
|
|
2240
|
-
itemMemberObj.phones = phoneData
|
|
2241
|
-
.filter(item => !item.isNewPhone && item.phoneNumber.trim())
|
|
2242
|
-
.map(item => item.phoneNumber);
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
function addNewPhoneToData(phoneId, phoneNumber) {
|
|
2246
|
-
if (!itemMemberObj.phones) {
|
|
2247
|
-
itemMemberObj.phones = [];
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
|
-
itemMemberObj.phones.push(phoneNumber);
|
|
2251
|
-
renderPhonesList();
|
|
2252
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
function removePhone(phoneId) {
|
|
2256
|
-
const currentData = _$w('#phoneNumbersList').data || [];
|
|
2257
|
-
const phoneToRemove = currentData.find(item => item._id === phoneId);
|
|
2258
|
-
|
|
2259
|
-
if (phoneToRemove) {
|
|
2260
|
-
if (itemMemberObj.phones) {
|
|
2261
|
-
itemMemberObj.phones = itemMemberObj.phones.filter(
|
|
2262
|
-
phone => !phoneDigitsEqual(phone, phoneToRemove.phoneNumber)
|
|
2263
|
-
);
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
// Clear toShowPhone if it was the removed phone or if it's no longer in the list
|
|
2267
|
-
// (handles format mismatch e.g. "(406)655-4940" vs "(406) 655-4940")
|
|
2268
|
-
const remainingPhones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
|
|
2269
|
-
if (
|
|
2270
|
-
itemMemberObj.toShowPhone &&
|
|
2271
|
-
!remainingPhones.some(p => phoneDigitsEqual(p, itemMemberObj.toShowPhone))
|
|
2272
|
-
) {
|
|
2273
|
-
itemMemberObj.toShowPhone = null;
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
|
-
const updatedData = currentData.filter(item => item._id !== phoneId);
|
|
2277
|
-
renderPhonesList(updatedData);
|
|
2278
|
-
checkFormChanges(FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
function updateShowPhoneSelection(phoneId, isVisible) {
|
|
2283
|
-
const currentData = _$w('#phoneNumbersList').data || [];
|
|
2284
|
-
const selectedItem = currentData.find(item => item._id === phoneId);
|
|
2285
|
-
|
|
2286
|
-
if (!selectedItem) {
|
|
2287
|
-
return;
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
const trimmed = (selectedItem.phoneNumber || '').trim();
|
|
2291
|
-
|
|
2292
|
-
if (isVisible) {
|
|
2293
|
-
itemMemberObj.toShowPhone = trimmed || null;
|
|
2294
|
-
return;
|
|
2295
|
-
}
|
|
2296
|
-
|
|
2297
|
-
if (
|
|
2298
|
-
itemMemberObj.toShowPhone === trimmed ||
|
|
2299
|
-
phoneDigitsEqual(itemMemberObj.toShowPhone, trimmed)
|
|
2300
|
-
) {
|
|
2301
|
-
itemMemberObj.toShowPhone = null;
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
|
|
2305
|
-
function getToShowPhone() {
|
|
2306
|
-
const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
|
|
2307
|
-
const toShow = itemMemberObj.toShowPhone || null;
|
|
2308
|
-
// Never expose toShowPhone when phones is empty, so save payload clears it in CMS
|
|
2309
|
-
if (phones.length === 0) return null;
|
|
2310
|
-
// If toShowPhone is not in the list (e.g. format mismatch), treat as none selected
|
|
2311
|
-
if (toShow && !phones.some(p => phoneDigitsEqual(p, toShow))) return null;
|
|
2312
|
-
return toShow;
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2315
|
-
function getContactAndBookingData() {
|
|
2316
|
-
const showWixUrl = _$w('#showUrlWixCheckbox').checked;
|
|
2317
|
-
const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
|
|
2318
|
-
|
|
2319
|
-
const addresses = Array.isArray(itemMemberObj.addresses) ? itemMemberObj.addresses : [];
|
|
2320
|
-
const phones = Array.isArray(itemMemberObj.phones) ? itemMemberObj.phones : [];
|
|
2321
|
-
|
|
2322
|
-
const rawWebsite = (_$w('#UrlInput').value || '').trim();
|
|
2323
|
-
const rawBooking = (_$w('#schedulingLinkInput').value || '').trim();
|
|
2324
|
-
|
|
2325
|
-
return {
|
|
2326
|
-
showContactForm: _$w('#showCotactFormCheckbox').checked,
|
|
2327
|
-
contactFormEmail: _$w('#contactFormEmailInput').value,
|
|
2328
|
-
toShowPhone: getToShowPhone(),
|
|
2329
|
-
bookingUrl: rawBooking ? normalizeExternalUrl(rawBooking) : '',
|
|
2330
|
-
website: rawWebsite ? normalizeExternalUrl(rawWebsite) : '',
|
|
2331
|
-
showWebsite: showExistingUrl,
|
|
2332
|
-
showWixUrl,
|
|
2333
|
-
addresses,
|
|
2334
|
-
addressDisplayOption: itemMemberObj.addressDisplayOption || [],
|
|
2335
|
-
phones,
|
|
2336
|
-
};
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
async function saveContactBooking() {
|
|
2340
|
-
const showExistingUrl = _$w('#showExsistingUrlCheckbox').checked;
|
|
2341
|
-
const otherWebsiteValue = (_$w('#UrlInput').value || '').trim();
|
|
2342
|
-
const isOtherWebsiteInvalid =
|
|
2343
|
-
showExistingUrl && (otherWebsiteValue === '' || isNotValidUrl(otherWebsiteValue));
|
|
2344
|
-
if (isOtherWebsiteInvalid) {
|
|
2345
|
-
const $urlInput = _$w('#UrlInput');
|
|
2346
|
-
$urlInput.required = true;
|
|
2347
|
-
$urlInput.updateValidityIndication();
|
|
2348
|
-
return;
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
const beforeData = JSON.parse(JSON.stringify(itemMemberObj));
|
|
2352
|
-
const contactChanges = getContactAndBookingData();
|
|
2353
|
-
|
|
2354
|
-
// Only send contact fields + required identifiers (partial update)
|
|
2355
|
-
const formData = {
|
|
2356
|
-
_id: itemMemberObj._id,
|
|
2357
|
-
memberId: itemMemberObj.memberId,
|
|
2358
|
-
...contactChanges,
|
|
2359
|
-
};
|
|
2360
|
-
|
|
2361
|
-
trackClick({
|
|
2362
|
-
pageName: PAGE_NAME,
|
|
2363
|
-
buttonName: BUTTON_NAMES.CONTACT,
|
|
2364
|
-
data: formData,
|
|
2365
|
-
});
|
|
2366
|
-
|
|
2367
|
-
// Log the specific contact & booking data changes
|
|
2368
|
-
console.group('Contact & Booking Save Attempt');
|
|
2369
|
-
console.log('Current Data:', beforeData);
|
|
2370
|
-
console.log('Changes Being Applied:', contactChanges);
|
|
2371
|
-
console.log('Final Form Data (partial):', formData);
|
|
2372
|
-
console.log('Address Changes:', {
|
|
2373
|
-
addressCount: contactChanges.addresses?.length || 0,
|
|
2374
|
-
addressDisplayOptions: contactChanges.addressDisplayOption,
|
|
2375
|
-
});
|
|
2376
|
-
console.log('Phone Selection:', contactChanges.toShowPhone);
|
|
2377
|
-
console.groupEnd();
|
|
2378
|
-
|
|
2379
|
-
const result = await saveData(formData);
|
|
2380
|
-
if (result.success) {
|
|
2381
|
-
if (beforeData.showWebsite !== contactChanges.showWebsite) {
|
|
2382
|
-
handleOptWebsiteCheckboxEnable(showExistingUrl);
|
|
2383
|
-
}
|
|
2384
|
-
// Sync Personal Details opt-in from saved member.
|
|
2385
|
-
_$w('#optWebsiteCheckbox').checked = itemMemberObj.showWixUrl;
|
|
2386
|
-
toggleFreeWebsiteText(itemMemberObj.showWixUrl);
|
|
2387
|
-
formHasUnsavedChanges[FORM_SECTION_HANDLER_MAP.CONTACT_BOOKING.section] = false;
|
|
2388
|
-
_$w('#saveContactBookingButton').disable();
|
|
2389
|
-
}
|
|
2390
|
-
handleSaveDataFeedback(_$w('#contactMessage'), result.message);
|
|
2391
|
-
}
|
|
2392
|
-
|
|
2393
|
-
function handleSaveDataFeedback($messageElement, message) {
|
|
2394
|
-
$messageElement.text = message;
|
|
2395
|
-
$messageElement.expand();
|
|
2396
|
-
setTimeout(() => {
|
|
2397
|
-
$messageElement.collapse();
|
|
2398
|
-
}, 5000);
|
|
2399
|
-
}
|
|
2400
|
-
|
|
2401
|
-
function setGallery() {
|
|
2402
|
-
// Initialize gallery array if it doesn't exist
|
|
2403
|
-
if (!itemMemberObj.gallery) {
|
|
2404
|
-
itemMemberObj.gallery = [];
|
|
2405
|
-
}
|
|
2406
|
-
const gallery = itemMemberObj.gallery;
|
|
2407
|
-
|
|
2408
|
-
const galleryData = buildGalleryData(gallery);
|
|
2409
|
-
|
|
2410
|
-
_$w('#galleryRepeater').data = galleryData;
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
function buildGalleryData(gallery) {
|
|
2414
|
-
return [
|
|
2415
|
-
{ _id: 'add-item', isAdd: true },
|
|
2416
|
-
...gallery.map(image => ({
|
|
2417
|
-
_id: generateId(),
|
|
2418
|
-
image,
|
|
2419
|
-
isAdd: false,
|
|
2420
|
-
})),
|
|
2421
|
-
];
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
|
-
function handleGalleryItem($item, itemData) {
|
|
2425
|
-
const multiStateBox = $item('#galleryMSB');
|
|
2426
|
-
|
|
2427
|
-
if (itemData.isAdd) {
|
|
2428
|
-
setupAddImageState($item, multiStateBox);
|
|
2429
|
-
} else {
|
|
2430
|
-
setupImageState($item, itemData, multiStateBox);
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
|
|
2434
|
-
function setupAddImageState($item, multiStateBox) {
|
|
2435
|
-
multiStateBox.changeState(GALLERY_STATES.ADD);
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
function setupImageState($item, itemData, multiStateBox) {
|
|
2439
|
-
multiStateBox.changeState(GALLERY_STATES.VIEW);
|
|
2440
|
-
$item('#galleryImage').src = itemData.image.src;
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
async function saveGalleryToCMS() {
|
|
2444
|
-
// Only send gallery field + required identifiers (partial update)
|
|
2445
|
-
const formData = {
|
|
2446
|
-
_id: itemMemberObj._id,
|
|
2447
|
-
memberId: itemMemberObj.memberId,
|
|
2448
|
-
gallery: itemMemberObj.gallery,
|
|
2449
|
-
};
|
|
2450
|
-
|
|
2451
|
-
await saveData(formData);
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
function formatFileName(fullName, maxBaseLength = 23) {
|
|
2455
|
-
const dotIndex = fullName.lastIndexOf('.');
|
|
2456
|
-
if (dotIndex === -1 || fullName.length <= maxBaseLength + 4) return fullName;
|
|
2457
|
-
|
|
2458
|
-
const name = fullName.slice(0, dotIndex);
|
|
2459
|
-
const ext = fullName.slice(dotIndex);
|
|
2460
|
-
return `${name.slice(0, maxBaseLength)}...${ext}`;
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
async function filterInterests(searchValue) {
|
|
2464
|
-
const container = _$w('#containerRepeaterInterest');
|
|
2465
|
-
const repeater = _$w('#repeaterInterest');
|
|
2466
|
-
|
|
2467
|
-
const allInterests = await getInterestAll();
|
|
2468
|
-
const filtered = allInterests
|
|
2469
|
-
.filter(val => val.toLowerCase().includes(searchValue))
|
|
2470
|
-
.map(val => ({ _id: generateId(), value: val }));
|
|
2471
|
-
|
|
2472
|
-
if (filtered.length > 0) {
|
|
2473
|
-
repeater.data = filtered;
|
|
2474
|
-
container.expand();
|
|
2475
|
-
} else {
|
|
2476
|
-
repeater.data = [];
|
|
2477
|
-
container.collapse();
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
return filtered;
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
|
|
2484
|
-
module.exports = {
|
|
2485
|
-
personalDetailsOnReady,
|
|
2486
|
-
};
|