mod-build 3.6.75-beta.2 → 4.0.0-alpha.2
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/.eslintignore +3 -0
- package/.eslintrc +18 -0
- package/CHANGELOG.md +2 -252
- package/README.md +16 -263
- package/gulp-tasks/grab-cdn.js +0 -10
- package/package.json +18 -68
- package/siteconfig.js +38 -0
- package/src/data/footer.js +117 -0
- package/src/data/seasons.js +5 -7
- package/src/index.html +18 -0
- package/src/main.js +45 -0
- package/src/scripts/has-qs-params.js +6 -5
- package/src/scripts/url-cleaner.js +3 -3
- package/src/scripts/utils.js +178 -0
- package/src/styles/home.scss +1 -0
- package/src/templates/_partials/scripts/deferred-styles.html +16 -16
- package/src/templates/_partials/scripts/vwo-redirect-callback.html +43 -45
- package/src/templates/components/head.html +70 -0
- package/tasks/clean.js +13 -0
- package/tasks/grab-cdn.js +107 -0
- package/tasks/grab-form-helpers.js +94 -0
- package/tasks/grab-shared-components.js +81 -0
- package/tasks/grab-shared-scripts.js +267 -0
- package/tasks/serve.js +15 -0
- package/tasks/templates.js +168 -0
- package/template.js +801 -0
- package/vite.config.js +56 -0
- package/.eslintrc.yml +0 -59
- package/src/data/common.js +0 -704
- package/src/data/components/qs-footer.js +0 -55
- package/src/data/components/quote-footer.js +0 -73
- package/src/scripts/apt-block.js +0 -919
- package/src/scripts/components/custom-selects.js +0 -48
- package/src/scripts/components/radio-panels.js +0 -45
- package/src/scripts/es6-1.js +0 -6
- package/src/scripts/es6-2.js +0 -2
- package/src/scripts/qs-form.js +0 -839
- package/src/scripts/vendor/maxmind-geoip2.js +0 -2
- package/src/scripts/vendor/swiper.min.js +0 -13
- package/src/styles/apt-block.scss +0 -888
- package/src/templates/_partials/apt-block.html +0 -30
- package/src/templates/_partials/scripts/analytics.html +0 -4
- package/src/templates/_partials/scripts/go-page-hiding-snippet.html +0 -8
- package/src/templates/_partials/scripts/google-maps.html +0 -1
- package/src/templates/_partials/scripts/google-optimize.html +0 -12
- package/src/templates/_partials/scripts/gtm-editorials/body/google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-editorials/head/google-tag-manager-head.html +0 -10
- package/src/templates/_partials/scripts/gtm-hil/body/google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-hil/head/google-tag-manager-head.html +0 -10
- package/src/templates/_partials/scripts/gtm-pro/body/google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-pro/head/google-tag-manager-head.html +0 -10
- package/src/templates/_partials/scripts/gtm-quote/body/google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-quote/head/google-tag-manager-head.html +0 -9
- package/src/templates/_partials/scripts/gtm-whitelabel/body/mod-google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-whitelabel/body/non-mod-google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-whitelabel/head/mod-google-tag-manager-head.html +0 -10
- package/src/templates/_partials/scripts/gtm-whitelabel/head/non-mod-google-tag-manager-head.html +0 -9
- package/src/templates/_partials/scripts/gtm-wordpress/body/google-tag-manager-body.html +0 -5
- package/src/templates/_partials/scripts/gtm-wordpress/head/google-tag-manager-head.html +0 -9
- package/src/templates/_partials/scripts/visual-website-optimizer.html +0 -5
- package/src/templates/index.html +0 -46
- /package/{src → public}/favicon.ico +0 -0
package/src/scripts/apt-block.js
DELETED
|
@@ -1,919 +0,0 @@
|
|
|
1
|
-
/* global modUtils modForm heap aptBlockTCPA:true */
|
|
2
|
-
/* eslint brace-style: 0 */
|
|
3
|
-
|
|
4
|
-
(function($) {
|
|
5
|
-
var apiUrl = modUtils.getApiDomain() + '/v1/',
|
|
6
|
-
getParams = modUtils.getUrlParamsToObject(),
|
|
7
|
-
projectId,
|
|
8
|
-
$aptBlock, $aptBlockMatches, $aptBlockTitle, $aptBlockLoading,
|
|
9
|
-
$instantAptBlock, $instantAptBlockMatches,
|
|
10
|
-
servicesProcessingCount = 0,
|
|
11
|
-
serviceAptSetIndex = 1,
|
|
12
|
-
instantAptsTimeSlotLimit = 4,
|
|
13
|
-
instantAptsData = {
|
|
14
|
-
services: [],
|
|
15
|
-
index: 0
|
|
16
|
-
},
|
|
17
|
-
gaTrackerNames = ['main', 'instantAppts'];
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Request data from API
|
|
21
|
-
* @param {String} type - request type
|
|
22
|
-
* @param {String} url - url to request
|
|
23
|
-
* @param {Object} data - data to send
|
|
24
|
-
* @param {Function} successCallback - success callback
|
|
25
|
-
* @param {Function} errorCallback - error callback
|
|
26
|
-
*/
|
|
27
|
-
function apiRequestData(type, url, data, successCallback, errorCallback) {
|
|
28
|
-
var ajaxSettings = {
|
|
29
|
-
url: url,
|
|
30
|
-
type: type,
|
|
31
|
-
data: data,
|
|
32
|
-
dataType: 'json',
|
|
33
|
-
crossDomain: true,
|
|
34
|
-
success: successCallback,
|
|
35
|
-
error: errorCallback
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
// Modify it for POST request
|
|
39
|
-
if ('post' === type) {
|
|
40
|
-
ajaxSettings.data = JSON.stringify(data);
|
|
41
|
-
ajaxSettings.contentType = 'application/json';
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// And send
|
|
45
|
-
$.ajax(ajaxSettings);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Get available services from API
|
|
50
|
-
* @param {Function} successCallback - success callback
|
|
51
|
-
* @param {Function} errorCallback - error callback
|
|
52
|
-
*/
|
|
53
|
-
function apiGetAvailableServices(successCallback, errorCallback) {
|
|
54
|
-
apiRequestData('get', apiUrl + 'projects/' + projectId + '/available-services', {}, successCallback, errorCallback);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get available schedule for a service
|
|
59
|
-
* @param {String} serviceId - service id
|
|
60
|
-
* @param {Function} successCallback - success callback
|
|
61
|
-
* @param {Function} errorCallback - error callback
|
|
62
|
-
*/
|
|
63
|
-
function apiGetServiceAvailableSchedule(serviceId, successCallback, errorCallback) {
|
|
64
|
-
apiRequestData('get', apiUrl + 'projects/' + projectId + '/available-services/' + serviceId + '/available-schedule', {}, successCallback, errorCallback);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Post appointment data to API
|
|
69
|
-
* @param {Object} data - appointment data
|
|
70
|
-
* @param {Function} successCallback - success callback
|
|
71
|
-
* @param {Function} errorCallback - error callback
|
|
72
|
-
*/
|
|
73
|
-
function apiPostAppointmentData(data, successCallback, errorCallback) {
|
|
74
|
-
apiRequestData('post', apiUrl + 'appointments', data, successCallback, errorCallback);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Get saved scheduled appointment data
|
|
79
|
-
* @param {String} pId - project id
|
|
80
|
-
* @returns {Object} data - data
|
|
81
|
-
*/
|
|
82
|
-
function getSavedScheduledAppsDataFromCookies(pId) {
|
|
83
|
-
var scheduledApptsData = modUtils.getCookie('scheduledAppts_' + pId);
|
|
84
|
-
|
|
85
|
-
if (scheduledApptsData) {
|
|
86
|
-
try {
|
|
87
|
-
return JSON.parse(scheduledApptsData);
|
|
88
|
-
}
|
|
89
|
-
catch (e) {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Save scheduled appointment data
|
|
99
|
-
* @param {String} pId - project id
|
|
100
|
-
* @param {String} sId - service id
|
|
101
|
-
* @param {Object} data - data to save
|
|
102
|
-
*/
|
|
103
|
-
function saveScheduledAppsDataInCookies(pId, sId, data) {
|
|
104
|
-
var scheduledApptsData = getSavedScheduledAppsDataFromCookies(pId);
|
|
105
|
-
|
|
106
|
-
if (!scheduledApptsData) {
|
|
107
|
-
scheduledApptsData = {};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if ('undefined' === typeof scheduledApptsData[sId]) {
|
|
111
|
-
scheduledApptsData[sId] = [];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
scheduledApptsData[sId].push(data);
|
|
115
|
-
modUtils.setCookie('scheduledAppts_' + pId, JSON.stringify(scheduledApptsData), 60 * 24 * 7);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Create acronym
|
|
120
|
-
* @param {String} name - phrase to convert to acronym
|
|
121
|
-
* @param {Integer} maxLength - max lengths
|
|
122
|
-
* @returns {String} - acronym
|
|
123
|
-
*/
|
|
124
|
-
function createAcronym(name, maxLength) {
|
|
125
|
-
var abbr = '';
|
|
126
|
-
|
|
127
|
-
// Default length
|
|
128
|
-
if (!maxLength) {
|
|
129
|
-
maxLength = 2;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Split by spaces or -
|
|
133
|
-
var words = String(name).split(/[\s|-]/);
|
|
134
|
-
|
|
135
|
-
// Go through each word
|
|
136
|
-
for (var i = 0; i < words.length; i ++) {
|
|
137
|
-
var firstLetter = words[i].charAt(0);
|
|
138
|
-
|
|
139
|
-
// Check if it is uppercase
|
|
140
|
-
if (/[A-Z]|[\u0080-\u024F]/.test(firstLetter) && firstLetter === firstLetter.toUpperCase()) {
|
|
141
|
-
abbr += firstLetter;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return abbr.substring(0, maxLength);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Format date to "Wednesday, 11/22"
|
|
150
|
-
* @param {String} dateTimeStrUTC - date & time string in UTC
|
|
151
|
-
* @param {String} dateStr - date string in yyyy-mm-dd format
|
|
152
|
-
* @param {String} timeZone - time zone
|
|
153
|
-
* @returns {String} - formatted date
|
|
154
|
-
*/
|
|
155
|
-
function formatDate(dateTimeStrUTC, dateStr, timeZone) {
|
|
156
|
-
var dateArr = dateTimeStrUTC.split(/[\-\+ :T]/),
|
|
157
|
-
date = new Date(),
|
|
158
|
-
formatter,
|
|
159
|
-
formattedDate,
|
|
160
|
-
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
formatter = new Intl.DateTimeFormat('en-US', {
|
|
164
|
-
weekday: 'short',
|
|
165
|
-
month: 'numeric',
|
|
166
|
-
day: 'numeric',
|
|
167
|
-
timeZone: timeZone
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
date.setUTCFullYear(dateArr[0]);
|
|
171
|
-
date.setUTCMonth(dateArr[1] - 1);
|
|
172
|
-
date.setUTCDate(dateArr[2]);
|
|
173
|
-
date.setUTCHours(dateArr[3]);
|
|
174
|
-
date.setUTCMinutes(dateArr[4]);
|
|
175
|
-
date.setUTCSeconds(0);
|
|
176
|
-
|
|
177
|
-
formattedDate = formatter.format(date);
|
|
178
|
-
}
|
|
179
|
-
catch (e) {
|
|
180
|
-
dateArr = dateStr.split('-');
|
|
181
|
-
|
|
182
|
-
formattedDate = months[dateArr[1] - 1] + ' ' + (dateArr[2] * 1);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return formattedDate;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Update service's date display
|
|
190
|
-
* @param {Object} $service - jQuery DOM object of a service
|
|
191
|
-
* @param {String} date - date string
|
|
192
|
-
*/
|
|
193
|
-
function updateServiceSelectedDate($service, date) {
|
|
194
|
-
$service.find('[data-bind="apt-match-schedule-date"]').text(date);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Update service's time display
|
|
199
|
-
* @param {Object} $service - jQuery DOM object of a service
|
|
200
|
-
* @param {String} time - time string
|
|
201
|
-
*/
|
|
202
|
-
function updateServiceSelectedTime($service, time) {
|
|
203
|
-
$service.find('[data-bind="apt-match-schedule-time"]').text(time);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Update time selector with date's times
|
|
208
|
-
* @param {Object} $service - jQuery DOM object of a service block
|
|
209
|
-
* @param {Object} $timeSelector - jQuery DOM object of a time selector
|
|
210
|
-
* @param {Array} appointmentTimes - array of available times
|
|
211
|
-
* @param {String} timeZoneAbbr - timezone abbr
|
|
212
|
-
*/
|
|
213
|
-
function updateTimeSelector($service, $timeSelector, appointmentTimes, timeZoneAbbr) {
|
|
214
|
-
// Empty time selector
|
|
215
|
-
$timeSelector
|
|
216
|
-
.empty()
|
|
217
|
-
.prop('title', false);
|
|
218
|
-
|
|
219
|
-
// Check if times are not empty
|
|
220
|
-
if (appointmentTimes) {
|
|
221
|
-
$timeSelector.parents('[data-bind="apt-match-schedule-control"]').removeClass('apt-match__schedule-control--date-only');
|
|
222
|
-
$timeSelector.prop('disabled', false);
|
|
223
|
-
|
|
224
|
-
$.each(appointmentTimes, function() {
|
|
225
|
-
var appointmentTime = this,
|
|
226
|
-
$option;
|
|
227
|
-
|
|
228
|
-
$option = $('<option value="' + appointmentTime.startDateTimeUTC + '">' + appointmentTime.startTime + ' ' + timeZoneAbbr + '</option>');
|
|
229
|
-
|
|
230
|
-
$timeSelector.append($option);
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
$timeSelector.parents('[data-bind="apt-match-schedule-control"]').addClass('apt-match__schedule-control--date-only');
|
|
235
|
-
$timeSelector
|
|
236
|
-
.append($('<option>Time</option>'))
|
|
237
|
-
.prop('disabled', true)
|
|
238
|
-
.prop('title', 'Please select date first');
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
$timeSelector.on('change', function() {
|
|
242
|
-
updateServiceSelectedTime($service, $(this).find('option:selected').text().trim());
|
|
243
|
-
});
|
|
244
|
-
$timeSelector.trigger('change');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Detect trade from URL
|
|
249
|
-
* @returns {String} trade
|
|
250
|
-
*/
|
|
251
|
-
function detectTrade() {
|
|
252
|
-
var url = window.location.href.toLowerCase(),
|
|
253
|
-
trade;
|
|
254
|
-
|
|
255
|
-
if (url.indexOf('window') > -1) {
|
|
256
|
-
trade = 'windows';
|
|
257
|
-
}
|
|
258
|
-
else if (url.indexOf('heating') > -1) {
|
|
259
|
-
trade = 'hvac';
|
|
260
|
-
}
|
|
261
|
-
else if (url.indexOf('solar') > -1) {
|
|
262
|
-
trade = 'solar';
|
|
263
|
-
}
|
|
264
|
-
else if (url.indexOf('roof') > -1) {
|
|
265
|
-
trade = 'roof';
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
trade = 'other';
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return trade;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Track event in heap
|
|
276
|
-
* @param {String} eventName - event name
|
|
277
|
-
* @param {Object} props - props
|
|
278
|
-
*/
|
|
279
|
-
function trackEventInHeap(eventName, props) {
|
|
280
|
-
if (!props) {
|
|
281
|
-
props = {};
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
props = $.extend({}, props, {
|
|
285
|
-
trade: detectTrade()
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
if ('undefined' !== typeof heap) {
|
|
289
|
-
heap.track(eventName, props);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Track apt view
|
|
295
|
-
* @param {String} type - appt type
|
|
296
|
-
*/
|
|
297
|
-
function trackAptView(type) {
|
|
298
|
-
var eventNamePrefix = 'instant' === type ? 'Instant Apt' : 'Apt';
|
|
299
|
-
|
|
300
|
-
// Track in GA
|
|
301
|
-
for (var i = 0; i < gaTrackerNames.length; i ++) {
|
|
302
|
-
modUtils.gaSend({
|
|
303
|
-
hitType: 'event',
|
|
304
|
-
eventCategory: eventNamePrefix,
|
|
305
|
-
eventAction: eventNamePrefix + ' View',
|
|
306
|
-
eventLabel: getParams.projectId
|
|
307
|
-
}, gaTrackerNames[i]);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Track in Heap
|
|
311
|
-
trackEventInHeap(eventNamePrefix + ' View');
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Track apt set
|
|
316
|
-
* @param {String} type - appt type
|
|
317
|
-
* @param {Object} opts - options
|
|
318
|
-
*/
|
|
319
|
-
function trackAptSet(type, opts) {
|
|
320
|
-
var eventNamePrefix = 'instant' === type ? 'Instant Apt' : 'Apt',
|
|
321
|
-
eventName,
|
|
322
|
-
$services;
|
|
323
|
-
|
|
324
|
-
if ('instant' === type) {
|
|
325
|
-
eventName = eventNamePrefix + ' set #' + (instantAptsData.index + 1) + '/' + instantAptsTimeSlotLimit;
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
$services = $aptBlockMatches.find('[data-bind="apt-match"]'),
|
|
329
|
-
eventName = eventNamePrefix + ' set #' + serviceAptSetIndex + ' ' + ($services.index(opts.$service) + 1) + '/' + $services.length;
|
|
330
|
-
serviceAptSetIndex ++;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Track in GA
|
|
334
|
-
for (var i = 0; i < gaTrackerNames.length; i ++) {
|
|
335
|
-
modUtils.gaSend({
|
|
336
|
-
hitType: 'event',
|
|
337
|
-
eventCategory: eventNamePrefix,
|
|
338
|
-
eventAction: eventName,
|
|
339
|
-
eventLabel: getParams.projectId
|
|
340
|
-
}, gaTrackerNames[i]);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// Track in Heap
|
|
344
|
-
trackEventInHeap(eventName);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Switch service block state
|
|
349
|
-
* @param {Object} $service - jQuery DOM object of a service
|
|
350
|
-
* @param {String} state - state id
|
|
351
|
-
*/
|
|
352
|
-
function switchServiceBlockState($service, state) {
|
|
353
|
-
$service.find('[data-bind="apt-match-step-schedule"]').hide();
|
|
354
|
-
$service.find('[data-bind="apt-match-step-confirm"]').hide();
|
|
355
|
-
$service.find('[data-bind="apt-match-step-confirmed"]').hide();
|
|
356
|
-
$service.find('[data-bind="apt-match-step-' + state + '"]').show();
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Switch instant apt block state
|
|
361
|
-
* @param {Object} $intantAptForm - jQuery DOM object of a instant apt form
|
|
362
|
-
* @param {String} state - state id
|
|
363
|
-
*/
|
|
364
|
-
function switchInstantAptBlockState($intantAptForm, state) {
|
|
365
|
-
$intantAptForm.removeClass('apt-instant-match__form--loading apt-instant-match__form--scheduled');
|
|
366
|
-
|
|
367
|
-
if ('' !== state) {
|
|
368
|
-
$intantAptForm.addClass('apt-instant-match__form--' + state);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Switch match block to/from loading state
|
|
374
|
-
* @param {Object} $service - jQuery DOM object of a service
|
|
375
|
-
* @param {Bool} loading - true form loading state, false for normal
|
|
376
|
-
*/
|
|
377
|
-
function switchMatchLoadingState($service, loading) {
|
|
378
|
-
var loadingCls = 'apt-match--loading';
|
|
379
|
-
|
|
380
|
-
if (loading) {
|
|
381
|
-
$service.addClass(loadingCls);
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
$service.removeClass(loadingCls);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Check if loading state is still needed
|
|
390
|
-
*/
|
|
391
|
-
function checkLoadingState() {
|
|
392
|
-
if (!servicesProcessingCount) {
|
|
393
|
-
var servicesProcessedCount = $aptBlock.find('[data-bind="apt-match"]').length;
|
|
394
|
-
|
|
395
|
-
$aptBlockTitle.text('You’ve got ' + servicesProcessedCount + ' instant match' + (servicesProcessedCount === 1 ? '' : 'es') + ':');
|
|
396
|
-
$aptBlockLoading.remove();
|
|
397
|
-
|
|
398
|
-
// Remove the block if no matching
|
|
399
|
-
if (!servicesProcessedCount) {
|
|
400
|
-
$aptBlock.remove();
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* Init service block
|
|
407
|
-
* @param {Object} service - service data object
|
|
408
|
-
*/
|
|
409
|
-
function initServiceBlock(service) {
|
|
410
|
-
var template, $service;
|
|
411
|
-
|
|
412
|
-
template = [
|
|
413
|
-
/* eslint-disable indent */
|
|
414
|
-
'<div data-bind="apt-match" class="apt-match" id="service-' + service.serviceId + '">',
|
|
415
|
-
'<div class="apt-match__logo' + (!service.contractorLogoUrl ? ' apt-match__logo--placeholder' : '') + '"' + (service.contractorLogoUrl ? ' style="background-image: url(' + service.contractorLogoUrl + ')"' : '') + '>',
|
|
416
|
-
createAcronym(service.serviceName),
|
|
417
|
-
'</div>',
|
|
418
|
-
'<div class="apt-match__content">',
|
|
419
|
-
'<div class="apt-match__label" data-bind="apt-match-label">',
|
|
420
|
-
'Match ' + ($aptBlock.find('[data-bind="apt-match"]').length + 1),
|
|
421
|
-
'</div>',
|
|
422
|
-
'<div class="apt-match__company">',
|
|
423
|
-
service.serviceName,
|
|
424
|
-
'</div>',
|
|
425
|
-
'<form class="apt-match__form">',
|
|
426
|
-
'<div data-bind="apt-match-step-schedule">',
|
|
427
|
-
'<div class="apt-match__form-title">',
|
|
428
|
-
'Schedule an Appointment:',
|
|
429
|
-
'</div>',
|
|
430
|
-
'<div class="parent-error">',
|
|
431
|
-
'<div data-bind="apt-match-schedule-control" class="apt-match__schedule-control apt-match__schedule-control--date-only">',
|
|
432
|
-
'<span class="apt-match__schedule-control-controls">',
|
|
433
|
-
'<div class="form-control-select">',
|
|
434
|
-
'<select name="date" data-required="nonempty">',
|
|
435
|
-
'<option value="">- Date -</option>',
|
|
436
|
-
'</select>',
|
|
437
|
-
'</div>',
|
|
438
|
-
'<div class="form-control-select">',
|
|
439
|
-
'<select name="time" data-required="nonempty" disabled="disabled">',
|
|
440
|
-
'<option value="">- Time -</option>',
|
|
441
|
-
'</select>',
|
|
442
|
-
'</div>',
|
|
443
|
-
'</span>',
|
|
444
|
-
'<span class="apt-match__schedule-control-btn">',
|
|
445
|
-
'<button type="submit" class="btn btn-primary">',
|
|
446
|
-
'Schedule',
|
|
447
|
-
'</button>',
|
|
448
|
-
'</span>',
|
|
449
|
-
'</div>',
|
|
450
|
-
'<div class="apt-match__schedule-error" data-bind="apt-match-schedule-error"></div>',
|
|
451
|
-
'</div>',
|
|
452
|
-
'</div>',
|
|
453
|
-
'<div data-bind="apt-match-step-confirm" style="display: none">',
|
|
454
|
-
'<div class="apt-match__form-title">',
|
|
455
|
-
'Appointment Confirmation:',
|
|
456
|
-
'</div>',
|
|
457
|
-
'<div class="apt-match__schedule-info">',
|
|
458
|
-
'<div class="apt-match__schedule-info-row">',
|
|
459
|
-
'<div class="apt-match__schedule-info-col-left">',
|
|
460
|
-
'<div class="apt-match__schedule-info-date" data-bind="apt-match-schedule-date"> </div>',
|
|
461
|
-
'<div class="apt-match__schedule-info-time" data-bind="apt-match-schedule-time"> </div>',
|
|
462
|
-
'</div>',
|
|
463
|
-
'<div class="apt-match__schedule-info-col-right">',
|
|
464
|
-
'<button type="button" class="btn btn-link" data-bind="apt-match-schedule-edit-btn">',
|
|
465
|
-
'Edit',
|
|
466
|
-
'</button>',
|
|
467
|
-
'<button type="button" class="btn btn-primary" data-bind="apt-match-schedule-confirm-btn">',
|
|
468
|
-
'<span>',
|
|
469
|
-
'Confirm',
|
|
470
|
-
'</span>',
|
|
471
|
-
'<span class="spinner"></span>',
|
|
472
|
-
'</button>',
|
|
473
|
-
'</div>',
|
|
474
|
-
'</div>',
|
|
475
|
-
'<div class="apt-match__tcpa">',
|
|
476
|
-
aptBlockTCPA,
|
|
477
|
-
'</div>',
|
|
478
|
-
'</div>',
|
|
479
|
-
'</div>',
|
|
480
|
-
'<div data-bind="apt-match-step-confirmed" style="display: none">',
|
|
481
|
-
'<div class="apt-match__form-title">',
|
|
482
|
-
'Appointment Confirmation:',
|
|
483
|
-
'</div>',
|
|
484
|
-
'<div class="apt-match__schedule-info">',
|
|
485
|
-
'<div class="apt-match__schedule-info-row">',
|
|
486
|
-
'<div class="apt-match__schedule-info-col-left">',
|
|
487
|
-
'<div class="apt-match__schedule-info-date" data-bind="apt-match-schedule-date"> </div>',
|
|
488
|
-
'<div class="apt-match__schedule-info-time" data-bind="apt-match-schedule-time"> </div>',
|
|
489
|
-
'</div>',
|
|
490
|
-
'<div class="apt-match__schedule-info-col-right">',
|
|
491
|
-
'<div class="apt-match__success-msg">',
|
|
492
|
-
'<div class="apt-match__success-msg-title">',
|
|
493
|
-
'Success',
|
|
494
|
-
'</div>',
|
|
495
|
-
'<div class="apt-match__success-msg-text">',
|
|
496
|
-
'You should be contacted shortly to confirm your reservation.',
|
|
497
|
-
'</div>',
|
|
498
|
-
'</div>',
|
|
499
|
-
'</div>',
|
|
500
|
-
'</div>',
|
|
501
|
-
'</div>',
|
|
502
|
-
'</div>',
|
|
503
|
-
'</form>',
|
|
504
|
-
'</div>',
|
|
505
|
-
'</div>'
|
|
506
|
-
/* eslint-enable indent */
|
|
507
|
-
].join('');
|
|
508
|
-
|
|
509
|
-
// Convert template to jQuery object
|
|
510
|
-
$service = $(template);
|
|
511
|
-
|
|
512
|
-
// Init schedule form submit
|
|
513
|
-
$service.find('form').on('submit', function(e) {
|
|
514
|
-
e.preventDefault();
|
|
515
|
-
|
|
516
|
-
if (modForm.isFormValid($(this))) {
|
|
517
|
-
switchServiceBlockState($service, 'confirm');
|
|
518
|
-
}
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// Edit button
|
|
522
|
-
$service.find('[data-bind="apt-match-schedule-edit-btn"]').on('click', function(e) {
|
|
523
|
-
e.preventDefault();
|
|
524
|
-
|
|
525
|
-
switchServiceBlockState($service, 'schedule');
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// Confirm button
|
|
529
|
-
$service.find('[data-bind="apt-match-schedule-confirm-btn"]').on('click', function(e) {
|
|
530
|
-
e.preventDefault();
|
|
531
|
-
|
|
532
|
-
var $timeSelector = $service.find('[name="time"]'),
|
|
533
|
-
$dateSelector = $service.find('[name="date"]'),
|
|
534
|
-
appointmentData = {
|
|
535
|
-
projectId: projectId,
|
|
536
|
-
serviceId: service.serviceId,
|
|
537
|
-
startDateTimeUTC: $timeSelector.val()
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
if ($timeSelector.data('serviceCustomData')) {
|
|
541
|
-
appointmentData.serviceCustomData = $timeSelector.data('serviceCustomData');
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
switchMatchLoadingState($service, true);
|
|
545
|
-
apiPostAppointmentData(appointmentData, function() {
|
|
546
|
-
switchMatchLoadingState($service, false);
|
|
547
|
-
switchServiceBlockState($service, 'confirmed');
|
|
548
|
-
|
|
549
|
-
saveScheduledAppsDataInCookies(projectId, service.serviceId, {
|
|
550
|
-
service: service,
|
|
551
|
-
date: $dateSelector.val(),
|
|
552
|
-
time: $timeSelector.find('option:selected').eq(0).text()
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
trackAptSet('direct', {
|
|
556
|
-
$service: $service
|
|
557
|
-
});
|
|
558
|
-
}, function(jqXHR) {
|
|
559
|
-
switchMatchLoadingState($service, false);
|
|
560
|
-
|
|
561
|
-
$service.find('[data-bind="apt-match-schedule-error"]').text(jqXHR.responseJSON.error.message);
|
|
562
|
-
$service.find('[data-bind="apt-match-schedule-control"]').parent().addClass('has-error');
|
|
563
|
-
switchServiceBlockState($service, 'schedule');
|
|
564
|
-
});
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// Check it was scheduled
|
|
568
|
-
if (service.scheduledAppt) {
|
|
569
|
-
$service.find('[data-bind="apt-match-schedule-date"]').text(service.scheduledAppt.date);
|
|
570
|
-
$service.find('[data-bind="apt-match-schedule-time"]').text(service.scheduledAppt.time);
|
|
571
|
-
switchServiceBlockState($service, 'confirmed');
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
// Add to the matches block
|
|
575
|
-
$aptBlockMatches.append($service);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Init instant apt block
|
|
580
|
-
*/
|
|
581
|
-
function initInstantAptBlock() {
|
|
582
|
-
var template = [
|
|
583
|
-
/* eslint-disable indent */
|
|
584
|
-
'<div data-bind="apt-instant-match" class="apt-instant-match">',
|
|
585
|
-
'<div data-bind="apt-instant-match-placeholder" class="apt-instant-match__placeholder"> </div>',
|
|
586
|
-
'<div class="apt-instant-match__placeholder apt-instant-match__placeholder--loading">',
|
|
587
|
-
'<span class="apt-block__spinner"></span>',
|
|
588
|
-
'Loading...',
|
|
589
|
-
'</div>',
|
|
590
|
-
'</div>'
|
|
591
|
-
/* eslint-enable indent */
|
|
592
|
-
].join('');
|
|
593
|
-
|
|
594
|
-
for (var i = 0; i < instantAptsTimeSlotLimit; i ++) {
|
|
595
|
-
var $template = $(template);
|
|
596
|
-
|
|
597
|
-
$template.find('[data-bind="apt-instant-match-placeholder"]').text('Appointment ' + (i + 1));
|
|
598
|
-
$instantAptBlockMatches.append($template);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Display service available dates & times
|
|
604
|
-
* @param {String} serviceId - service id
|
|
605
|
-
* @param {Object} schedule - time data
|
|
606
|
-
*/
|
|
607
|
-
function displayServiceAvailableDateTime(serviceId, schedule) {
|
|
608
|
-
// Stop if no dates
|
|
609
|
-
if (!schedule.data.appointmentDates || !schedule.data.appointmentDates.length) {
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
var $service = $('#service-' + serviceId),
|
|
614
|
-
$dateSelector = $service.find('[name="date"]'),
|
|
615
|
-
$timeSelector = $service.find('[name="time"]');
|
|
616
|
-
|
|
617
|
-
// Store data
|
|
618
|
-
$dateSelector.data('timeZoneAbbr', schedule.timeZoneAbbr);
|
|
619
|
-
if (schedule.data.serviceCustomData) {
|
|
620
|
-
$timeSelector.data('serviceCustomData', schedule.data.serviceCustomData);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// Display date selector
|
|
624
|
-
$.each(schedule.data.appointmentDates, function() {
|
|
625
|
-
var appointmentDate = this,
|
|
626
|
-
$option;
|
|
627
|
-
|
|
628
|
-
// Display date only if it has times
|
|
629
|
-
if (appointmentDate.appointmentTimes && appointmentDate.appointmentTimes.length) {
|
|
630
|
-
var formattedDate = formatDate(appointmentDate.appointmentTimes[0].startDateTimeUTC, appointmentDate.date, schedule.timeZone);
|
|
631
|
-
|
|
632
|
-
$option = $('<option value="' + formattedDate + '">' + formattedDate + '</option>');
|
|
633
|
-
$option.data('appointmentTimes', appointmentDate.appointmentTimes);
|
|
634
|
-
$dateSelector.append($option);
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
$dateSelector.on('change', function() {
|
|
639
|
-
updateServiceSelectedDate($service, $(this).val());
|
|
640
|
-
updateTimeSelector($service, $timeSelector, $(this).find('option:selected').data('appointmentTimes'), $(this).data('timeZoneAbbr'));
|
|
641
|
-
});
|
|
642
|
-
$dateSelector.trigger('change');
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Display available services
|
|
647
|
-
* @param {Array} services - array of services
|
|
648
|
-
*/
|
|
649
|
-
function processAvailableServices(services) {
|
|
650
|
-
$.each(services, function() {
|
|
651
|
-
var service = this;
|
|
652
|
-
|
|
653
|
-
// Request available schedule for this contractor
|
|
654
|
-
apiGetServiceAvailableSchedule(service.serviceId, function(response) {
|
|
655
|
-
servicesProcessingCount --;
|
|
656
|
-
|
|
657
|
-
if ((response.data && response.data.appointmentDates) || service.scheduledAppt) {
|
|
658
|
-
initServiceBlock(service);
|
|
659
|
-
|
|
660
|
-
if (!service.scheduledAppt) {
|
|
661
|
-
displayServiceAvailableDateTime(service.serviceId, response);
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
checkLoadingState();
|
|
666
|
-
}, function() {
|
|
667
|
-
servicesProcessingCount --;
|
|
668
|
-
checkLoadingState();
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
servicesProcessingCount ++;
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
/**
|
|
676
|
-
* Init instant apt time slot
|
|
677
|
-
* @param {Object} service - service data object
|
|
678
|
-
* @param {Object} schedule - time data
|
|
679
|
-
* @param {Integer} index - time slot index
|
|
680
|
-
*/
|
|
681
|
-
function initInstantAptTimeSlot(service, schedule, index) {
|
|
682
|
-
var aptTimeSlotBlockId = service.serviceId + '-' + index, $form, template;
|
|
683
|
-
|
|
684
|
-
template = [
|
|
685
|
-
/* eslint-disable indent */
|
|
686
|
-
'<form data-bind="apt-instant-match-form" class="apt-instant-match__form" id="service-' + aptTimeSlotBlockId + '">',
|
|
687
|
-
'<div class="apt-instant-match__schedule">',
|
|
688
|
-
'<div class="parent-error">',
|
|
689
|
-
'<div data-bind="apt-match-schedule-control" class="apt-match__schedule-control apt-match__schedule-control--date-only">',
|
|
690
|
-
'<span class="apt-match__schedule-control-controls">',
|
|
691
|
-
'<div class="form-control-select">',
|
|
692
|
-
'<select name="date" data-required="nonempty">',
|
|
693
|
-
'<option value="">- Date -</option>',
|
|
694
|
-
'</select>',
|
|
695
|
-
'</div>',
|
|
696
|
-
'<div class="form-control-select">',
|
|
697
|
-
'<select name="time" data-required="nonempty" disabled="disabled">',
|
|
698
|
-
'<option value="">- Time -</option>',
|
|
699
|
-
'</select>',
|
|
700
|
-
'</div>',
|
|
701
|
-
'</span>',
|
|
702
|
-
'<span class="apt-match__schedule-control-btn">',
|
|
703
|
-
'<button type="submit" class="btn btn-primary">',
|
|
704
|
-
'Schedule',
|
|
705
|
-
'</button>',
|
|
706
|
-
'</span>',
|
|
707
|
-
'</div>',
|
|
708
|
-
'<div class="apt-match__schedule-error" data-bind="apt-match-schedule-error"></div>',
|
|
709
|
-
'</div>',
|
|
710
|
-
'</div>',
|
|
711
|
-
'<div class="apt-instant-match__scheduled">',
|
|
712
|
-
'<div class="apt-instant-match__scheduled-content">',
|
|
713
|
-
'<span class="apt-block__spinner"></span>',
|
|
714
|
-
'<span data-bind="apt-instant-match-scheduled-time" class="apt-instant-match__scheduled-text">',
|
|
715
|
-
'<span data-bind="apt-match-schedule-date"></span>',
|
|
716
|
-
' | ',
|
|
717
|
-
'<span data-bind="apt-match-schedule-time"></span>',
|
|
718
|
-
'</span>',
|
|
719
|
-
'</div>',
|
|
720
|
-
'</div>',
|
|
721
|
-
'</form>'
|
|
722
|
-
/* eslint-enable indent */
|
|
723
|
-
].join('');
|
|
724
|
-
|
|
725
|
-
$form = $(template);
|
|
726
|
-
|
|
727
|
-
$form.on('submit', function(e) {
|
|
728
|
-
e.preventDefault();
|
|
729
|
-
|
|
730
|
-
if (modForm.isFormValid($form)) {
|
|
731
|
-
var $timeSelector = $form.find('[name="time"]'),
|
|
732
|
-
$dateSelector = $form.find('[name="date"]'),
|
|
733
|
-
appointmentData = {
|
|
734
|
-
projectId: projectId,
|
|
735
|
-
serviceId: service.serviceId,
|
|
736
|
-
startDateTimeUTC: $timeSelector.val()
|
|
737
|
-
};
|
|
738
|
-
|
|
739
|
-
if ($timeSelector.data('serviceCustomData')) {
|
|
740
|
-
appointmentData.serviceCustomData = $timeSelector.data('serviceCustomData');
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
switchInstantAptBlockState($form, 'loading');
|
|
744
|
-
apiPostAppointmentData(appointmentData, function() {
|
|
745
|
-
switchInstantAptBlockState($form, 'scheduled');
|
|
746
|
-
|
|
747
|
-
trackAptSet('instant');
|
|
748
|
-
|
|
749
|
-
saveScheduledAppsDataInCookies(projectId, service.serviceId, {
|
|
750
|
-
date: $dateSelector.val(),
|
|
751
|
-
time: $timeSelector.find('option:selected').eq(0).text()
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
instantAptsData.index ++;
|
|
755
|
-
|
|
756
|
-
if (instantAptsData.index < instantAptsTimeSlotLimit) {
|
|
757
|
-
/* eslint-disable no-use-before-define */
|
|
758
|
-
processInstantApts();
|
|
759
|
-
/* eslint-enable no-use-before-define */
|
|
760
|
-
}
|
|
761
|
-
}, function(jqXHR) {
|
|
762
|
-
if ('undefined' !== typeof jqXHR.responseJSON.error && 'undefined' !== typeof jqXHR.responseJSON.error.message) {
|
|
763
|
-
$form.find('[data-bind="apt-match-schedule-error"]').text(jqXHR.responseJSON.error.message);
|
|
764
|
-
}
|
|
765
|
-
$form.find('[data-bind="apt-match-schedule-control"]').parent().addClass('has-error');
|
|
766
|
-
switchInstantAptBlockState($form, '');
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
// Add form to instant apt block slots
|
|
772
|
-
$instantAptBlockMatches.find('[data-bind="apt-instant-match"]').eq(index)
|
|
773
|
-
.empty()
|
|
774
|
-
.append($form);
|
|
775
|
-
|
|
776
|
-
// Display available dates & times
|
|
777
|
-
displayServiceAvailableDateTime(aptTimeSlotBlockId, schedule);
|
|
778
|
-
|
|
779
|
-
// Check if there's any info about scheduled appts in cookies
|
|
780
|
-
var scheduledApptsData = getSavedScheduledAppsDataFromCookies(projectId);
|
|
781
|
-
|
|
782
|
-
if (scheduledApptsData && 'undefined' !== typeof scheduledApptsData[service.serviceId] && 'undefined' !== typeof scheduledApptsData[service.serviceId][index]) {
|
|
783
|
-
var scheduledAppt = scheduledApptsData[service.serviceId][index];
|
|
784
|
-
|
|
785
|
-
// Switch this time slot to "scheduled" UI
|
|
786
|
-
$form.find('[data-bind="apt-match-schedule-date"]').text(scheduledAppt.date);
|
|
787
|
-
$form.find('[data-bind="apt-match-schedule-time"]').text(scheduledAppt.time);
|
|
788
|
-
switchInstantAptBlockState($form, 'scheduled');
|
|
789
|
-
|
|
790
|
-
instantAptsData.index ++;
|
|
791
|
-
|
|
792
|
-
if (instantAptsData.index < instantAptsTimeSlotLimit) {
|
|
793
|
-
/* eslint-disable no-use-before-define */
|
|
794
|
-
processInstantApts();
|
|
795
|
-
/* eslint-enable no-use-before-define */
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Process instant apts
|
|
802
|
-
*/
|
|
803
|
-
function processInstantApts() {
|
|
804
|
-
if (!instantAptsData.services || !instantAptsData.services.length) {
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
var service = instantAptsData.services[0];
|
|
809
|
-
|
|
810
|
-
// Request available schedule for instant apt
|
|
811
|
-
$instantAptBlockMatches.find('[data-bind="apt-instant-match"]').eq(instantAptsData.index).addClass('apt-instant-match--loading');
|
|
812
|
-
apiGetServiceAvailableSchedule(service.serviceId, function(response) {
|
|
813
|
-
$instantAptBlockMatches.find('[data-bind="apt-instant-match"]').eq(instantAptsData.index).removeClass('apt-instant-match--loading');
|
|
814
|
-
|
|
815
|
-
if (response.data && response.data.appointmentDates) {
|
|
816
|
-
initInstantAptTimeSlot(service, response, instantAptsData.index);
|
|
817
|
-
}
|
|
818
|
-
else {
|
|
819
|
-
if (0 === instantAptsData.index) {
|
|
820
|
-
$instantAptBlock.hide();
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}, function() {
|
|
824
|
-
if (0 === instantAptsData.index) {
|
|
825
|
-
$instantAptBlock.hide();
|
|
826
|
-
}
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Init appointment control
|
|
832
|
-
*/
|
|
833
|
-
function init() {
|
|
834
|
-
$aptBlock = $('#apt-block');
|
|
835
|
-
$aptBlockMatches = $('#apt-block-matches');
|
|
836
|
-
$aptBlockTitle = $('#apt-block-title');
|
|
837
|
-
$aptBlockLoading = $('#apt-block-loading');
|
|
838
|
-
$instantAptBlock = $('#instant-apt-block');
|
|
839
|
-
$instantAptBlockMatches = $('#apt-block-instant-matches');
|
|
840
|
-
|
|
841
|
-
if (!$aptBlock.length || !getParams.projectId) {
|
|
842
|
-
if ($aptBlock.length) {
|
|
843
|
-
$aptBlock.remove();
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
return;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
projectId = getParams.projectId;
|
|
850
|
-
|
|
851
|
-
// Empty matches and load them from API
|
|
852
|
-
$aptBlockMatches.empty();
|
|
853
|
-
apiGetAvailableServices(function(response) {
|
|
854
|
-
if (response.data && response.data.services) {
|
|
855
|
-
// Filter apt services by type
|
|
856
|
-
var normalApts = [],
|
|
857
|
-
instantApts = [];
|
|
858
|
-
|
|
859
|
-
$.each(response.data.services, function() {
|
|
860
|
-
var service = this;
|
|
861
|
-
|
|
862
|
-
if ('appointment' === service.serviceType) {
|
|
863
|
-
normalApts.push(service);
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if ('instant appointment' === service.serviceType) {
|
|
867
|
-
instantApts.push(service);
|
|
868
|
-
}
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
// Check if there are any scheduled normal appts in cookies
|
|
872
|
-
var scheduledApptsInCookies = getSavedScheduledAppsDataFromCookies(projectId);
|
|
873
|
-
|
|
874
|
-
if (scheduledApptsInCookies) {
|
|
875
|
-
$.each(scheduledApptsInCookies, function(sId, sData) {
|
|
876
|
-
if ('undefined' !== typeof sData[0] && 'undefined' !== typeof sData[0].service) {
|
|
877
|
-
sData[0].service.scheduledAppt = {
|
|
878
|
-
date: sData[0].date,
|
|
879
|
-
time: sData[0].time
|
|
880
|
-
};
|
|
881
|
-
normalApts.push(sData[0].service);
|
|
882
|
-
}
|
|
883
|
-
});
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (instantApts.length) {
|
|
887
|
-
instantAptsData.services = instantApts;
|
|
888
|
-
instantAptsData.index = 0;
|
|
889
|
-
|
|
890
|
-
$instantAptBlock.show();
|
|
891
|
-
initInstantAptBlock();
|
|
892
|
-
processInstantApts();
|
|
893
|
-
|
|
894
|
-
trackAptView('instant');
|
|
895
|
-
}
|
|
896
|
-
else if (normalApts.length) {
|
|
897
|
-
$aptBlock.show();
|
|
898
|
-
processAvailableServices(normalApts);
|
|
899
|
-
|
|
900
|
-
trackAptView('direct');
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
else {
|
|
904
|
-
$aptBlock.hide();
|
|
905
|
-
}
|
|
906
|
-
}, function() {});
|
|
907
|
-
|
|
908
|
-
// Reset error class on date & time change
|
|
909
|
-
$('body').on('change', 'select', function() {
|
|
910
|
-
modForm.unmarkFieldAsInvalid($(this));
|
|
911
|
-
$(this).parents('[data-bind="apt-match"]').find('[data-bind="apt-match-schedule-error"]').text('');
|
|
912
|
-
});
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
$(document).ready(function() {
|
|
916
|
-
init();
|
|
917
|
-
});
|
|
918
|
-
})(jQuery);
|
|
919
|
-
|