PageSourceSearch

https://quiltster.netlify.app/views/pages/QuiltworxAccountRetreatBookingSingle.js

js quiltster.netlify.app collected 2026-10-03 11:19:28 UTC 51,564 bytes, 934 lines download raw bytes

1import Cookies from '../../services/Cookies.js'
2import Urls from '../../services/Urls.js'
3import Fetcher from '../../services/Fetcher.js'
4import Helpers from '../../services/Helpers.js'
5import Cart from '../../services/Cart.js'
6import Utils from '../../services/Utils.js'
7import Router from '../../services/Router.js'
8import loadTempusDominus from '../../services/TempusDominusLoader.js'
9import RetreatBookingTime from '../../services/RetreatBookingTime.js'
10import Env from '../../services/Env.js'
11
12let injectScript = async (token) => {
13    return new Promise((resolve, reject) => {
14        const script = document.createElement('script');
15        script.src = `https://www.paypal.com/sdk/js?client-id=${encodeURIComponent(Env.quiltworxPaypalClientId())}&components=buttons&debug=true&commit=true`;
16        script['data-client-token'] = token;
17        script.addEventListener('load', resolve);
18        script.addEventListener('error', e => reject(e.error));
19        document.head.appendChild(script);
20    });
21}
22
23let injectLuxonScript = async () => {
24    return new Promise((resolve, reject) => {
25        const script = document.createElement('script');
26        script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js';
27        script.addEventListener('load', resolve);
28        script.addEventListener('error', e => reject(e.error));
29        document.head.appendChild(script);
30    });
31}
32
33function placeOrderButton(cart, holderId, override = false){
34    document.getElementById(holderId).disabled = false;
35    // document.getElementById(holderId).innerHTML = `<button class="btn btn-block btn-primary" id="">Place Order</button>`;
36
37}
38
39let generateClientToken = async () => {
40    const response = await fetch(Urls.quiltworxPaypalToken, {
41      method: "post"
42    });
43    const data = await response.json();
44    return data;
45  }
46
47let generatePaypalClient = async (cartResponse) => {
48    let clientToken = await generateClientToken();
49    clientToken['priceTotal'] = cartResponse['priceTotal'];
50    paypalClientRenderForm(clientToken);
51}
52
53function paypalClientRenderForm(json){
54
55    let errorElement = document.getElementById('card-errors');
56    let cardInput = document.getElementById('card-element');
57
58    if(json['clientToken']){
59        injectScript(json['clientToken'])
60        .then(() => {
61            paypal
62            .Buttons({
63                // Sets up the transaction when a payment button is clicked
64                createOrder: function (data, actions) {
65                    // let isValid = validateForm();
66                    // createCheckout(booking);
67                    let subtotalContainer = document.getElementById('cart-subtotal');
68                    let rightNow = new Date().getTime();
69                    let bookingUniqueId = subtotalContainer.dataset.uniqueId;
70
71                    return fetch(Urls.quiltworxPaypalOrders, {
72                        method: "post",
73                        headers: {
74                            "Content-Type": "application/json",
75                        },
76                        body: JSON.stringify({
77                            value: json['priceTotal'],
78                            token: json['clientToken'],
79                            orderId: bookingUniqueId + '-' + rightNow.toString(),
80                            capture: true,
81                        })
82                    })
83                    .then((response) => response.json())
84                    .then((order) => order.id);
85                },
86                // Finalize the transaction after payer approval
87                onApprove: function (data, actions) {
88                    
89                    let subtotalContainer = document.getElementById('cart-subtotal');
90                    let booking = {};
91                    booking.id = subtotalContainer.dataset.booking;
92                    let postBody = {
93                        value: json['priceTotal'],
94                        token: Cookies.getCookie("token"),
95                        booking: booking,
96                    }
97                    if (subtotalContainer.dataset.payment) {
98                        postBody.paymentId = subtotalContainer.dataset.payment;
99                    } else {
100                        postBody.type = subtotalContainer.dataset.type;
101                        postBody.qty = subtotalContainer.dataset.qty;
102                    }
103                    const postOptions = {
104                        method: "POST",
105                        headers: {
106                            'Content-Type': 'application/json', 
107                        },
108                        body: JSON.stringify(postBody)
109                    };
110                    if (Cookies.getCookie("token")) {
111                        postOptions.headers['Authorization'] = `Token ${Cookies.getCookie("token")}`;
112                    }
113                    return fetch(`${Urls.apiRetreatPaypalOrders}${data.orderID}/`, postOptions)
114                        .then(orderData => orderData.json())
115                        .then((orderData) => {
116                            // Successful capture! For dev/demo purposes:
117                            // console.log( "Capture result", orderData, JSON.stringify(orderData, null, 2));
118                            // let transaction = orderData.purchase_units[0].payments.captures[0];
119                            // alert(`Transaction ${transaction.status}: ${transaction.id}
120            
121                            // See console for all available details
122                            // `);
123                            // When ready to go live, remove the alert and show a success message within this page. For example:
124                            // let element = document.getElementById('paypal-button-container');
125                            // element.innerHTML = '<h3>Thank you for your payment!</h3>';
126                            // Or go to another URL:  actions.redirect('thank_you.html');
127                            if (orderData.success) {
128                                actions.redirect(window.location.origin + '/retreat-confirmation/')
129                            } else {
130                                if (orderData.message) {
131                                    Utils.grr(orderData.message, "danger");
132                                    return actions.restart();
133                                } else {
134                                    Utils.grr("There was an error; please try again.", "danger");
135                                }
136                            }
137                        });
138                    
139
140                },
141                // fundingSource: paypal.FUNDING.PAYPAL,
142   
143            })
144            .render("#paypal-button-container");
145
146        }).catch(error => {
147            console.error(error);
148        });
149        
150    }else{
151        loader.classList.remove('loader-show', 'full-screen');
152        loader.classList.add('loader-hide');
153        // placeOrderButton(cartResponse, "placeOrder");
154        // May do other errors
155        if(json["error"]){
156            Utils.grr(json["error"], "danger-stay");
157        }else{
158            Utils.grr("There was an error, please try again", "danger");
159        }
160
161    }
162}
163
164
165function apiCallback(json) {
166    // Router.router()
167    return json
168}
169
170
171let createCheckout = async (data) => {
172    try {
173        const response = await Fetcher.post(Urls.apiRetreatBookings, JSON.stringify(data), apiCallback);
174        return response;
175    } catch (err) {
176
177    }  
178}
179
180const phoneValidation = /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im;
181
182const getPhoneNumber = (phone) => {
183    const test = phoneValidation.exec(phone);
184    if(test) return phone;
185    return false;
186}
187
188const validateForm = () => {
189
190    let contactAddress = document.getElementById('contact-address');
191    let contactEmail = document.getElementById('contact-email');
192    let contactFirstName = document.getElementById('first-name');
193    let contactLastName = document.getElementById('last-name');
194    let contactPhone = document.getElementById('contact-phone');
195    let arrival = document.getElementById("arrival-timepicker");
196    let departure = document.getElementById("departure-timepicker");
197
198    let contactFields = [
199        contactAddress,
200        contactEmail,
201        contactFirstName,
202        contactLastName,
203        contactPhone,
204        arrival,
205        departure,
206    ]
207
208    let errorString = "";
209    let isValid = true;
210
211    for (const field of contactFields) {
212        if (!field.value) {
213            field.classList.add('is-invalid');
214            isValid = false;
215            errorString += `${field.previousElementSibling.innerHTML} is missing. `;
216        } else {
217            field.classList.remove('is-invalid');
218        }
219    }
220
221    if (!isValid) {
222        Utils.grr(errorString, "danger");
223    }
224
225    return isValid;
226}
227
228const createPaypalWidget = async (cartTotal) => {
229    // Paypal Setup
230    const cartResponse = {priceTotal: cartTotal};
231    let paypalToken = await generatePaypalClient(cartResponse);
232    // placeOrderButton(cartResponse, "paypal-button-container");
233}
234
235let cancelBooking = async () => {
236    const loader = null || document.getElementById('loader');
237    loader.classList.add('loader-show');
238    loader.classList.remove('loader-hide');
239    
240    let booking = {};
241    booking.id = document.getElementById("save-contact-info").dataset.booking;
242    booking.status = 'cancelled'
243    await createCheckout(booking)
244
245    loader.classList.add('loader-hide');
246    loader.classList.remove('loader-show');
247
248    Utils.grr("Booking has been cancelled.", "success");
249    document.getElementById("close-cancel-modal").click();
250    Router.router();
251}
252
253let saveContactInfo = async () => {
254
255    document.getElementById("save-contact-info").disabled = true;
256    const loader = null || document.getElementById('loader');
257    loader.classList.add('loader-show');
258    loader.classList.remove('loader-hide');
259
260    let booking = {};
261
262    booking.firstName = document.getElementById("first-name").value
263    booking.lastName = document.getElementById("last-name").value
264    booking.phone = document.getElementById("contact-phone").value
265    booking.email = document.getElementById("contact-email").value
266    booking.address = document.getElementById("contact-address").value
267    booking.arrival = document.getElementById("arrival-timepicker").value;
268    booking.departure = document.getElementById("departure-timepicker").value;
269    booking.specialNeeds = document.getElementById("special-needs").value;
270    booking.mealPreference = document.getElementById("meal-preference-select").value;
271    booking.id = document.getElementById("save-contact-info").dataset.booking;
272
273    
274    await createCheckout(booking);
275
276    document.getElementById("save-contact-info").disabled = false;
277    loader.classList.add('loader-hide');
278    loader.classList.remove('loader-show');
279
280    Utils.grr("Contact Info updated successfully.", "success");
281    document.getElementById("close-contact-modal").click();
282    Router.router();
283
284}
285
286let successfulLogin = (json) => {
287    Utils.setLoginCookies(json);
288    Cart.cartLoginAction(true);
289    Cart.miniCartUpdate();
290    $('#guestLogin').modal('hide');
291    Utils.historyPushAndRoute(Urls.appMarketplaceCheckout, true);
292
293};
294
295let setCookiesAndForward = (json) => {
296    if (json['token']) {
297        successfulLogin(json);
298        // setTimeout(function () {
299        //     successfulLogin(json);
300        // }, 500);
301    } else {
302        document.getElementById("login_submit_btn").disabled = false;
303        loader.classList.add('loader-hide');
304        loader.classList.remove('loader-show');
305        Cookies.eraseCookie('token');
306        Cookies.eraseCookie('gsqSession');
307        Cookies.eraseCookie('seller_id');
308        Cookies.eraseCookie('seller_title');
309        document.getElementById("login_submit_btn").classList.remove('action');
310        Utils.grr('Your login and/or password is incorrect. Please try again or <a href="https://www.quiltster.com" target="_blank">reset your password via Quiltster</a>.', "danger");
311    }
312};
313
314let postCredentials = async (username, password) => {
315    let body = {
316        username: username,
317        password: password
318    }
319    Fetcher.postWithoutAuth(Urls.apiLogin, Fetcher.prepareFormData(body), setCookiesAndForward);
320}
321
322let updateItem = async (id, data) => {
323    if (Cookies.getCookie('token')) {
324        try {
325            const response = await Fetcher.post(Urls.apiGetCartItem + id + "/", JSON.stringify(data), apiCallback);
326            return response;
327        } catch (err) {
328    
329        }    
330    }else if (Cookies.getCookie('guest_cart')) {
331        data.guest_cart = Cookies.getCookie('guest_cart');
332        try {
333            const response = await Fetcher.postWithoutAuth(Urls.apiGetCartItem + id + "/?guest_cart="+Cookies.getCookie('guest_cart'), Utils.jsObjectToParamString(data).substr(1), apiCallback);
334            return response;
335        } catch (err) {
336            // console.log(err);
337        }    
338    }
339}
340
341let QuiltworxAccountRetreatBookingSingle = {
342
343    render: async (request) => {
344
345        injectLuxonScript();
346        await loadTempusDominus();
347        let cart = {};
348
349        function getJsonCallback(json){
350            return json.items[0]
351        }
352        
353        let getJson = async (request) => {
354            try {
355                const response = await Fetcher.get(Urls.apiRetreatBookings + request.slug + '/', getJsonCallback);
356                return response;
357            } catch (err) {
358                // console.log(err)
359            }
360        }
361        const bookingJson = await getJson(request);
362        const booking = bookingJson['items'][0];
363
364        const startDate = luxon.DateTime.fromISO(booking.retreat.startDate);
365        const endDate = luxon.DateTime.fromISO(booking.retreat.endDate);
366        const startDateString = startDate.toLocaleString(luxon.DateTime.DATE_FULL);
367        const endDateString = endDate.toLocaleString(luxon.DateTime.DATE_FULL);
368
369        let optionsBeforeHtml = '';
370        let optionsAfterHtml = '';
371        if (booking.retreat.additionalDays) {
372            let purchasedBefore = booking.additionalDaysBefore ? parseInt(booking.additionalDaysBefore) : 0;
373            for (const dayItem of booking.retreat.additionalDays) {
374                if (dayItem.type === 'day_before') {
375                    for (const x of Array(dayItem.maxQuantity + 1 - purchasedBefore).keys()) {
376                        optionsBeforeHtml += `<option value="${x * parseFloat(dayItem.price)}">${x}</option>`
377                    }
378                }
379                if (dayItem.type === 'day_after') {
380                    let purchasedAfter = booking.additionalDaysAfter ? parseInt(booking.additionalDaysAfter) : 0;
381                    for (const x of Array(dayItem.maxQuantity + 1 - purchasedAfter).keys()) {
382                        optionsAfterHtml += `<option value="${x * parseFloat(dayItem.price)}">${x}</option>`
383                    }
384                }
385            }
386        }
387
388        Utils.setDocumentTitle(request, booking.retreat.title);
389
390        let view = /*html*/ `
391        <div class="modal fade hide" id="editContactInfoModal" tabindex="-1" role="dialog" aria-modal="true">
392            <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
393                <div class="modal-content">
394                    <!-- Close -->
395                    <button type="button" class="close" id="close-contact-modal" data-dismiss="modal" aria-label="Close">
396                        <i class="fe fe-x" aria-hidden="true"></i>
397                    </button>
398                    <!-- Content -->
399                    <div class="container-fluid">
400                        <div class="row ">
401                            <div class="col mt-5 p-3 text-center">
402                                <!-- Heading -->
403                                <h4 class="mb-3">Edit Contact Info</h4>
404                                <div class="col-12">
405                                    <form class="form text-left">
406                                        <div class="form-row mt-3">
407                                            <div class="col-6 pr-3">
408                                                <label for="first-name">First Name</label>
409                                                <input
410                                                    id="first-name"
411                                                    type="text"
412                                                    class="form-control pl-2 pr-0 contact-field"
413                                                    value="${booking.firstName}"
414                                                />
415                                            </div>
416                                            <div class="col-6 pl-3">
417                                                <label for="last-name">Last Name</label>
418                                                <input
419                                                    id="last-name"
420                                                    type="text"
421                                                    class="form-control pl-2 pr-0 contact-field"
422                                                    value="${booking.lastName}"
423                                                />
424                                            </div>
425                                        </div>
426
427                                        <div class="form-row mt-3">
428                                            <div class="col-6 pr-3">
429                                                <label for="contact-email">Email</label>
430                                                <input
431                                                    id="contact-email"
432                                                    type="email"
433                                                    class="form-control pl-2 pr-0 contact-field"
434                                                    value="${booking.email}"
435                                                />
436                                            </div>
437                                            <div class="col-6 pl-3">
438                                                <label for="contact-phone">Phone</label>
439                                                <input
440                                                    id="contact-phone"
441                                                    type="text"
442                                                    class="form-control pl-2 pr-0 contact-field"
443                                                    value="${booking.phone}"
444                                                />
445                                            </div>
446                                        </div>
447
448                                        <div class="form-row mt-3">
449                                            <div class="col-12">
450                                                <label for="address">Mailing Address</label>
451                                                <textarea
452                                                    id="contact-address"
453                                                    class="form-control contact-field"
454                                                >${booking.address ? booking.address : ''}</textarea>
455                                            </div>
456                                        </div>
457
458                                        <h4 class="mt-6">Arrival & Departure</h4>
459                                        <div class="form-row mt-3">
460                                            <div id="additional-day-note" class="alert alert-warning d-none">
461                                                <i class="fe fe-alert-circle"></i>
462                                                Be sure to include your additional day(s) in your arrival & departure time.
463                                            </div>
464                                            <div class="col-6 pr-3">
465                                                <label for="arrival-timepicker">Arrival Time</label>
466                                                <input
467                                                    id="arrival-timepicker"
468                                                    type="text"
469                                                    class="form-control pl-2 pr-0" 
470                                                    data-td-target="#arrival-timepicker" 
471                                                    value="${RetreatBookingTime.formatPickerValue(booking.arrival)}"
472                                                />
473                                            </div>
474                                            <div class="col-6 pl-3">
475                                                <label for="departure-datepicker">Departure Time</label>
476                                                <input
477                                                    id="departure-timepicker"
478                                                    type="text"
479                                                    class="form-control pl-2 pr-0"
480                                                    data-td-target="#departure-date"
481                                                    value="${RetreatBookingTime.formatPickerValue(booking.departure)}"
482                                                />
483                                            </div>
484                                        </div>
485                                        <div class="form-row mt-3">
486                                            <div class="col-12">
487                                                <label for="special-needs">Any special needs or disabilities?</label>
488                                                <textarea id="special-needs" name="special-needs" class="form-control">${booking.specialNeeds}</textarea>
489                                            </div>
490                                        </div>
491
492                                        <h4 class="mt-6">Meal Preference</h4>
493                        
494                                        <div class="col-6 form-row align-items-center">
495                                            <select id="meal-preference-select" class="custom-select custom-select-sm">
496                                                <option value="standard"${booking.mealPreference === 'standard' ? ' selected' : ''}>Standard</option>
497                                                <option value="vegetarian"${booking.mealPreference === 'vegetarian' ? ' selected' : ''}>Vegetarian</option>
498                                                <option value="vegan"${booking.mealPreference === 'vegan' ? ' selected' : ''}>Vegan</option>
499                                                <option value="gluten_free"${booking.mealPreference === 'gluten_free' ? ' selected' : ''}>Gluten free</option>
500                                                <option value="nut_free"${booking.mealPreference === 'nut_free' ? ' selected' : ''}>Nut free</option>
501                                                <option value="dairy_free"${booking.mealPreference === 'dairy_free' ? ' selected' : ''}>Dairy Free</option>
502                                                <option value="no_pork"${booking.mealPreference === 'no_pork' ? ' selected' : ''}>No Pork</option>
503                                                <option value="no_seafood"${booking.mealPreference === 'no_seafood' ? ' selected' : ''}>No Seafood</option>
504                                            </select>
505                                        </div>
506
507                                        <button type="button" id="save-contact-info" class="btn btn-primary float-end mt-6 mb-4" style="float:right;" data-booking="${booking.id}">Save Changes</button>
508
509                                    </form>
510                                </div>
511                            </div>
512                        </div>
513                    </div>
514                </div>
515            </div>
516        </div>
517
518        <div class="modal fade hide" id="confirmCancelModal" tabindex="-1" role="dialog" aria-modal="true">
519            <div class="modal-dialog modal-dialog-centered modal
519-lg" role="document">
520                <div class="modal-content">
521                    <!-- Close -->
522                    <button type="button" class="close" id="close-cancel-modal" data-dismiss="modal" aria-label="Close">
523                        <i class="fe fe-x" aria-hidden="true"></i>
524                    </button>
525                    <!-- Content -->
526                    <div class="container-fluid">
527                        <div class="row modal-body">
528
529                            <div class="col mt-5 p-3">
530                                <!-- Heading -->
531                                <h4 class="mb-6">Are you sure you want to cancel?</h4>
532                                <div class="col-12">
533
534                                    <p>Please confirm to cancel your reservation. All payments made to date are non-refundable.</p>
535                                    <p>If you would like more info, please contact <a href="mailto:[email protected]">[email protected]</a>.</p>
536                                    <p><a href="http://www.quiltworx.com/retreats/cancellation-policy/" target="_blank">Please see our cancellation policy here.</a></p>
537
538                                </div>
539                            </div>
540                        </div>
541                    </div>
542                    <div class="modal-footer mt-0 pt-0" style="justify-content: flex-end;">
543                        <!-- Button -->
544                        <button class="btn btn-sm btn-link" data-dismiss="modal">
545                            Never mind
546                        </button>
547                        <button class="btn btn-sm btn-danger" id="cancel-booking">
548                            Yes, Cancel Booking
549                        </button>
550                    </div>
551                </div>
552            </div>
553        </div>
554        
555        <section class="pb-12">
556            <div class="container">
557                <div class="row" id="booking-row">
558                    <div class="col-12 pt-3" id="retreatDescription">
559                        <!-- Heading -->
560                        <h3 class="mb-5">${booking.retreat.title}</h3>
561                        <p><strong>Event Dates: ${startDateString}${booking.retreat.endDate ? ' &mdash; ' + endDateString : ''}</strong></p>
562                        <p class="mb-8">${booking.retreat.description}</p>
563
564                        <div class="card card-lg mb-7 border">
565                            <div class="card-body pb-0 p-3">
566                                <!-- Info -->
567                                <div class="card card-sm">
568                                    <div class="card-header align-middle">
569                                        <div class="row">
570                                            <div class="col-10">
571                                                <h4 class="mb-0 mt-2 align-middle">Booking ID: ${booking.uniqueId} ${booking.status === 'cancelled' ? ' (CANCELLED)' : ``}</h4>
572                                            </div>
573                                            <div class="col-2">
574                                                ${booking.status === 'cancelled' ? '' : `<button class="btn btn-sm btn-primary btn-inline" style="float:right" data-toggle="modal" data-target="#editContactInfoModal">Edit</button>`}
575                                            </div>
576                                        </div>
577                                    </div>
578                                    <div class="card-body">
579                                        <div class="row">
580                                            <div class="col-6">
581                                                <p><strong>Retreat</strong><br />${booking.retreat.title}</p>
582                                                <p><strong>Arrival</strong><br />${booking.arrival ? `${RetreatBookingTime.formatRetreatDate(booking.arrival)} ${RetreatBookingTime.formatRetreatTime(booking.arrival)}` : '-'}</p>
583                                                <p><strong>Name</strong><br />${booking.firstName} ${booking.lastName}</p>
584                                                <p><strong>Address</strong><br />${booking.address}</p>
585                                                <p><strong>Meal Request</strong><br />${Utils.capitalize(booking.mealRequest)}</p>
586                                                <p><strong>Special Needs</strong><br />${booking.specialNeeds ? booking.specialNeeds : 'None'}</p>
587                                            </div>
588                                            <div class="col-6">
589                                                <p><strong>Ticket & Room</strong><br />${booking.room.ticket.title} - ${booking.room.title} ${booking.isPrivate ? ' (Private)' : ''}</p>
590                                                <p><strong>Departure</strong><br />${booking.departure ? `${RetreatBookingTime.formatRetreatDate(booking.departure)} ${RetreatBookingTime.formatRetreatTime(booking.departure)}` : '-'}</p>
591                                                <p><strong>Phone</strong><br /><a href="tel:${booking.phone}">${booking.phone}</a></p>
592                                                <p><strong>Email</strong><br /><a href="mailto:${booking.email}">${booking.email}</a></p>
593                                            </div>
594                                        </div>
595                                        <hr />
596                                        <div class="row">
597                                            <div class="col-6${booking.retreat.additionalDays ? '' : ' d-none'}">
598                                                <p><strong>Additional Days Before Event</strong><br />${booking.additionalDaysBefore ? booking.additionalDaysBefore : 'None'}</p>
599                                            </div>
600                                            <div class="col-6${booking.retreat.additionalDays ? '' : ' d-none'}">
601                                                <p><strong>Additional Days Before Event</strong><br />${booking.additionalDaysAfter ? booking.additionalDaysAfter : 'None'}</p>
602                                            </div>
603                                            <div class="col-6${booking.retreat.equipmentRental ? '' : ' d-none'}">
604                                                <p><strong>${booking.retreat.equipmentRental ? booking.retreat.equipmentRental.title : 'Equipment'} Rental?</strong><br />${booking.equipmentRental ? 'Yes' : 'No'}</p>
605                                            </div>
606                                        </div>
607                                    </div>
608                                </div>
609                                <div class="card card-sm">
610                                    <div class="card-header align-middle">
611                                        <div class="row">
612                                            <div class="col-10">
613                                                <h4 class="mb-0 mt-0 align-middle">Payment Schedule</h4>
614                                            </div>
615                                        </div>
616                                    </div>
617                                    <div class="card-body">
618                                        <div class="row">
619                                        <div class="table-responsive">
620                                            <table class="table">
621                                                <thead class="thead-light">
622                                                    <tr>
623                                                        <th scope="col">Type</th>
624                                                        <th scope="col" class="text-center">Paid</th>
625                                                        <th scope="col">Amount</th>
626                                                        <th scope="col">Date</th>
627                                                    </tr>
628                                                </thead>
629                                                <tbody>
630                                                    ${booking.payments ? booking.payments.map((payment, index) =>
631                                                        /*html*/`
632                                                        <tr>
633                                                            <th scope="row" class="align-middle">
634                                                                ${Utils.capitalize(payment.type)}
635                                                            </th>
636                                                            <td class="text-center${payment.isPaid ? '' : ' py-3'}">
637                                                                ${payment.isPaid ? 
638                                                                    '<div class="rounded-circle bg-success text-white mt-6 mt-sm-0" style="width:26px;height:26px;margin:0 auto;"><span class="fe fe-check"></span></div>'
639                                                                    : 
640                                                                    `<button class="btn btn-primary btn-inline btn-sm paynow-button mt-6 mt-sm-0" data-booking="${booking.id}" data-unique-id="${booking.uniqueId}" data-payment="${payment.id}" data-amount="${payment.amount}" data-type="${payment.type}">Pay Now</button>`}
641                                                            </td>
642                                                            <td class="align-middle">
643                                                                $${payment.amount}
644                                                            </td>
645                                                            <td class="align-middle">
646                                                                ${payment.paidDate ? 'Paid: ' + Utils.formatDate(payment.paidDate, false) : payment.dueDate ? 'Due: ' + Utils.formatDate(payment.dueDate, false) : ''}
647                                                            </td>
648                                                        </tr>`).join('\n')
649                                                    : ''}     
650                                                </tbody>
651                                            </table>
652                                        </div>
653                                        </div>
654                                        <div class="mb-6">
655
656
657                                            ${booking.retreat.additionalDays ? booking.retreat.additionalDays.map((dayItem) => `
658    
659                                                ${ (dayItem.type === 'day_before') && (!booking.additionalDaysBefore || (booking.additionalDaysBefore < dayItem.maxQuantity)) ? `
660                                                    <h5 class="mt-5">Additional Days Before Event</h5>
661                                                    <p>Additional days before the event can be purchased for $${dayItem.price}/day.</p>
662                                                    <form class="form-inline">
663                                                        <div class="w-25 form-row align-items-center">
664                                                            <div class="" style="white-space:nowrap;">
665                                                                <select id="additional-days-before-select" class="custom-select custom-select-sm mr-1">
666                                                                    ${optionsBeforeHtml}
667                                                                </select>
668                                                                    days
669                                                            </div>
670                                                        </div>
671                                                        <div class="w-75 pr-lg-2 text-right d-none" id="additional-days-before-subtotal-wrap">
672                                                            <span id="additional-days-before-subtotal">$0</span>
673                                                            <button class="btn btn-primary btn-inline ml-2 ml-md-6 mr-md-6 paynow-button" id="buy-day-before-button" data-unique-id="${booking.uniqueId}" data-booking="${booking.id}" data-type="day_before">Buy Now</button>
674                                                        </div>
675                                                    </form>
676                                                ` : ''}
677    
678                                                ${ (dayItem.type === 'day_after') && (!booking.additionalDaysAfter || (booking.additionalDaysAfter < dayItem.maxQuantity)) ? `
679                                                    <h5 class="mt-5">Additional Days After Event</h5>
680                                                    <p>Additional days after the event can be purchased for $${dayItem.price}/day.</p>
681                                                    <form class="form-inline">
682                                                        <div class="w-25 form-row align-items-center">
683                                                            <div class="" style="white-space:nowrap;">
684                                                                <select id="additional-days-after-select" class="custom-select custom-select-sm mr-1">
685                                                                    ${optionsAfterHtml}
686                                                                </select>
687                                                                    days
688                                                            </div>
689                                                        </div>
690                                                        <div class="w-75 pr-lg-2 text-right d-none" id="additional-days-after-subtotal-wrap">
691                                                            <span id="additional-days-after-subtotal">$0</span>
692                                                            <button class="btn btn-primary btn-inline ml-2 ml-md-6 mr-md-6 paynow-button" id="buy-day-after-button" data-unique-id="${booking.uniqueId}" data-booking="${booking.id}" data-type="day_after">Buy Now</button>
693                                                        </div>
694                                                    </form>
695                                                ` : ''}
696                                            
697                                            `).join("\n") : ''}
698
699
700                                            ${ !booking.equipmentRental && booking.retreat.equipmentRental ? `
701                                            <h5 class="mt-8">Equipment Rental</h5>
702                                            <p>Would you like to rent a ${booking.retreat.equipmentRental.title} for $${booking.retreat.equipmentRental.price}?</p>
703                                            <form class="form-inline">
704                                                <div class="w-50 form-row align-items-center">
705                                                    <select id="equipment-rental-select" class="custom-select custom-select-sm" data-name="${booking.retreat.equipmentRental.title}">
706                                                        <option value="0">No</option>
707                                                        <option value="${booking.retreat.equipmentRental.price}">Yes</option>
708                                                    </select>
709                                                </div>
710                                                <div class="w-50 pr-lg-2 text-right d-none" id="equipment-rental-subtotal-wrap">
711                                                    <span id="equipment-rental-subtotal">$0</span>
712                                                    <button class="btn btn-primary btn-inline ml-2 ml-md-6 mr-md-6 paynow-button" id="buy-equipment-button" data-unique-id="${booking.uniqueId}" data-booking="${booking.id}" data-type="equipment">Buy Now</button>
713                                                </div>
714                                            </form>
715                                            ` : ''}
716                                        </div>
717                                    </div>
718                                </div>
719
720                                ${booking.status === 'cancelled' ? '' : `<button class="btn btn-danger my-5 ml-2" data-toggle="modal" data-target="#confirmCancelModal">Cancel Booking</button>`}
721
722                            </div>
723                        </div>
724                    </div>
725                </div>
726                <div class="row justify-content-md-center d-none" id="cart-row">
727                <div class="col-12 col-md-7">
728                    <style>
729                    .list-group-item:first-child {
730                        border:none;
731                    }
732                    </style>
733                    <div id="ticket-list" class="row break tab-content" style="flex-wrap:nowrap;">
734                        
735                        <div class="col-12 mx-auto prefs-list fade active show tab-pane" id="setPrefs" role="tabpanel" aria-labelledby="setPrefs-tab">
736                            <div class="mb-2 font-weight-bold">
737                                <h3>Shopping Cart</h3>
738                            </div>
739                            <!-- List group -->
740                            <ul id="cart-list" class="list-group list-group-lg list-group-flush-x mb-6 item-list">
741                            </ul>
742                            <button class="btn btn-outline-secondary btn-inline btn-sm mt-6" id="cancel-cart">Cancel</button>
743                        </div>
744
745                    </div>
746
747                </div>
748                <div id="checkout-column" class="col-12 col-md-5 col-lg-4 offset-lg-1">
749                
750                    <!-- Total -->
751                    <div class="card mb-7 bg-light">
752                        <div class="card-body">
753                            <ul class="list-group list-group-sm list-group-flush-y list-group-flush-x" id="list-cart-details">
754                                <li class="list-group-item d-flex">
755                                    <span>Total Charge</span> <span class="ml-auto font-size-sm" id="cart-subtotal"> - </span>
756                                </li>
757                                <li class="list-group-item d-none">
758                                    <span>Tax</span> <span class="ml-auto font-size-sm"></span>
759                                </li>
760                                <li class="list-group-item border-0 d-none font-size-lg font-weight-bold">
761                                    <span>Total</span> <span class="ml-auto font-size-sm"></span>
762                                </li>
763                                <li class="list-group-item border-0 d-none font-size-xs text-muted">
764                                    <span>Shipping and taxes will be calculated at checkout.</span>
765                                </li>
766                            </ul>
767                        </div>
768                    
769                    </div>
770
771                    <!-- Button -->
772                    <div id="paypal-button-container" class="paypal-button-container mb-2"></div>
773
774                    <!-- Disclaimer -->
775                    <p class="mb-7 font-size-xs text-gray-500">
776                    Your personal data will be used to process your order, support
777                    your experience throughout this website, and for other purposes
778                    described in our privacy policy.
779                    </p>
780
781                </div>
782                </div>
783            </div>
784        </section>
785        `
786        return view
787    },
788    after_render: async () => {
789
790        let payButtons = document.getElementsByClassName('paynow-button');
791        for (const payButton of payButtons) {
792            payButton.addEventListener('click', function(e){
793                e.preventDefault();
794                let subtotalContainer = document.getElementById('cart-subtotal');
795
796                // set data attributes
797                subtotalContainer.dataset.type = e.target.dataset.type;
798                subtotalContainer.dataset.amount = e.target.dataset.amount;
799                subtotalContainer.dataset.booking = e.target.dataset.booking;
800                subtotalContainer.dataset.uniqueId = e.target.dataset.uniqueId;
801                let typeString = '';
802                if (e.target.dataset.type === 'day_before') {
803                    delete subtotalContainer.dataset.payment;
804                    subtotalContainer.dataset.qty = e.target.dataset.qty;
805                    typeString = `${e.target.dataset.qty} Additional Day${e.target.dataset.qty > 1 ? 's' : ''} Before Event`;
806                } else if (e.target.dataset.type === 'day_after') {
807                    delete subtotalContainer.dataset.payment;
808                    subtotalContainer.dataset.qty = e.target.dataset.qty;
809                    typeString = `${e.target.dataset.qty} Additional Day${e.target.dataset.qty > 1 ? 's' : ''} After Event`;
810                } else if (e.target.dataset.type === 'equipment') {
811                    delete subtotalContainer.dataset.payment;
812                    subtotalContainer.dataset.qty = 1;
813                    typeString = `${e.target.dataset.name} Rental`;
814                } else {  // second and final payments
815                    subtotalContainer.dataset.payment = e.target.dataset.payment;
816                    subtotalContainer.dataset.qty = 1;
817                    typeString = `${Utils.capitalize(e.target.dataset.type)} Payment`;
818                }
819
820                const cartHtml = `<li class="list-group-item"><div class="row align-items-center"><div class="col-10">${typeString}</div><div class="col-2">$${e.target.dataset.amount}</div></div></li>`
821                document.getElementById('cart-list').innerHTML = cartHtml;
822                subtotalContainer.innerHTML = `$${e.target.dataset.amount}`;
823                createPaypalWidget(e.target.dataset.amount);
824
825                document.getElementById('booking-row').classList.add('d-none');
826                document.getElementById('cart-row').classList.remove('d-none');
827                window.scroll(0,0);
828
829            });
830        }
831
832        document.getElementById("additional-days-before-select") && document.getElementById("additional-days-before-select").addEventListener('change', function(e){
833            e.preventDefault();
834            let price = parseFloat(e.target.value);
835            let qty = parseFloat(e.target.selectedOptions[0].firstChild.data);
836            document.getElementById("buy-day-before-button").dataset.amount = price.toFixed(2);
837            document.getElementById("buy-day-before-button").dataset.qty = qty;
838            document.getElementById("additional-days-before-subtotal").innerHTML = '$' + price.toFixed(2);
839            if (qty > 0) {
840                document.getElementById("additional-days-before-subtotal-wrap").classList.remove("d-none");
841            } else {
842                document.getElementById("additional-days-before-subtotal-wrap").classList.add("d-none");
843            }
844        });
845
846        document.getElementById("additional-days-after-select") && document.getElementById("additional-days-after-select").addEventListener('change', function(e){
847            e.preventDefault();
848            let price = parseFloat(e.target.value);
849            let qty = parseFloat(e.target.selectedOptions[0].firstChild.data);
850            document.getElementById("buy-day-after-button").dataset.amount = price.toFixed(2);
851            document.getElementById("buy-day-after-button").dataset.qty = qty;
852            document.getElementById("additional-days-after-subtotal").innerHTML = '$' + price.toFixed(2);
853            if (qty > 0) {
854                document.getElementById("additional-days-after-subtotal-wrap").classList.remove("d-none");
855            } else {
856                document.getElementById("additional-days-after-subtotal-wrap").classList.add("d-none");
857            }
858        });
859
860        document.getElementById("equipment-rental-select") && document.getElementById("equipment-rental-select").addEventListener('change', function(e){
861            e.preventDefault();
862            let price = parseFloat(e.target.value);
863            document.getElementById("buy-equipment-button").dataset.amount = price.toFixed(2);
864            document.getElementById("buy-equipment-button").dataset.name = e.target.dataset.name;
865            document.getElementById("equipment-rental-subtotal").innerHTML = '$' + price.toFixed(2);
866            if (price > 0) {
867                document.getElementById("equipment-rental-subtotal-wrap").classList.remove("d-none");
868            } else {
869                document.getElementById("equipment-rental-subtotal-wrap").classList.add("d-none");
870            }
871        });
872
873        document.getElementById("arrival-timepicker").addEventListener('focus', function(e) {
874            e.target.classList.remove('is-invalid');
875        })
876
877        document.getElementById("departure-timepicker").addEventListener('focus', function(e) {
878            e.target.classList.remove('is-invalid');
879        })
880
881        document.getElementById("cancel-cart").addEventListener('click', function(e) {
882            document.getElementById("cart-row").classList.add('d-none');
883            document.getElementById("booking-row").classList.remove('d-none');
884        })
885
886        let pickerOptions = {
887            display: {
888                icons: {
889                  time: 'fe fe-clock',
890                  date: 'fe fe-calendar',
891                  up: 'fe fe-arrow-up',
892                  down: 'fe fe-arrow-down',
893                  previous: 'fe fe-chevron-left',
894                  next: 'fe fe-chevron-right',
895                  today: 'fe fe-calendar-check',
896                  clear: 'fe fe-trash',
897                  close: 'fe fe-x',
898                },
899                buttons: {
900                  today: true,
901                  clear: true,
902                  close: true,
903                },
904                theme: 'light',
905            },
906            localization: {
907                locale: 'en-US',
908                // State the format rather than leaning on the locale default:
909                // the picker parses the input's value positionally against
910                // this string and re-emits it after every pick, and the same
911                // string is posted back to the api. See RetreatBookingTime.js.
912                format: RetreatBookingTime.PICKER_FORMAT,
913            }
914        }
915
916        new tempusDominus.TempusDominus(document.getElementById('arrival-timepicker'), pickerOptions);
917        new tempusDominus.TempusDominus(document.getElementById('departure-timepicker'), pickerOptions);
918
919        document.getElementById("save-contact-info").addEventListener('click', function(e) {
920            let isValid = validateForm();
921            if (isValid) {
922                saveContactInfo();
923            }
924        });
925
926        document.getElementById("cancel-booking").addEventListener('click', function(e) {
927
928            cancelBooking();
929            
930        });
931          
932    }
933}
934export default QuiltworxAccountRetreatBookingSingle;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.