/** * WooCommerce Integration Scripts * * Handles booking form interactions for WooCommerce integration. * * @package Magdev\WpBnb */ (function ($) { 'use strict'; /** * WP BnB WooCommerce Integration */ const WpBnbWC = { /** * Settings from localization */ settings: {}, /** * Initialize */ init: function () { this.settings = window.wpBnbWC || {}; this.bindEvents(); this.initBookingForms(); }, /** * Bind global events */ bindEvents: function () { // Admin: Sync all rooms button $(document).on('click', '.bnb-sync-rooms-btn', this.handleSyncRooms.bind(this)); // Admin: Generate invoice button $(document).on('click', '.bnb-generate-invoice-btn', this.handleGenerateInvoice.bind(this)); }, /** * Initialize booking forms */ initBookingForms: function () { $('.bnb-wc-booking-form').each(function () { new BookingForm($(this)); }); }, /** * Handle sync all rooms button */ handleSyncRooms: function (e) { e.preventDefault(); const $btn = $(e.currentTarget); const $status = $btn.siblings('.sync-status'); $btn.prop('disabled', true).addClass('updating'); $status.text(this.settings.i18n?.syncing || 'Syncing...'); $.ajax({ url: this.settings.ajaxUrl, type: 'POST', data: { action: 'wp_bnb_sync_all_rooms', nonce: this.settings.nonce }, success: function (response) { if (response.success) { $status.html('' + response.data.message + ''); } else { $status.html('' + (response.data?.message || 'Error') + ''); } }, error: function () { $status.html('' + (WpBnbWC.settings.i18n?.error || 'Error occurred') + ''); }, complete: function () { $btn.prop('disabled', false).removeClass('updating'); } }); }, /** * Handle generate invoice button */ handleGenerateInvoice: function (e) { e.preventDefault(); const $btn = $(e.currentTarget); const orderId = $btn.data('order-id'); $btn.prop('disabled', true).addClass('updating'); $.ajax({ url: this.settings.ajaxUrl, type: 'POST', data: { action: 'wp_bnb_generate_invoice', nonce: this.settings.nonce, order_id: orderId }, success: function (response) { if (response.success) { location.reload(); } else { alert(response.data?.message || 'Error generating invoice'); } }, error: function () { alert(WpBnbWC.settings.i18n?.error || 'Error occurred'); }, complete: function () { $btn.prop('disabled', false).removeClass('updating'); } }); } }; /** * Booking Form Handler */ class BookingForm { constructor($form) { this.$form = $form; this.roomId = $form.data('room-id'); this.productId = $form.data('product-id'); this.checkAvailabilityTimeout = null; this.bindEvents(); } bindEvents() { this.$form.on('change', '.bnb-date-input', this.onDateChange.bind(this)); this.$form.on('change', '.bnb-guests-input', this.onGuestsChange.bind(this)); this.$form.on('change', '.bnb-service-checkbox', this.onServiceChange.bind(this)); this.$form.on('change', '.bnb-service-qty', this.onServiceQtyChange.bind(this)); this.$form.on('submit', this.onSubmit.bind(this)); } onDateChange() { const checkIn = this.$form.find('[name="bnb_check_in"]').val(); const checkOut = this.$form.find('[name="bnb_check_out"]').val(); // Validate dates if (!checkIn || !checkOut) { this.updateAvailabilityStatus(''); return; } const checkInDate = new Date(checkIn); const checkOutDate = new Date(checkOut); const today = new Date(); today.setHours(0, 0, 0, 0); if (checkInDate < today) { this.updateAvailabilityStatus('unavailable', WpBnbWC.settings.i18n?.pastDate || 'Check-in cannot be in the past'); return; } if (checkOutDate <= checkInDate) { this.updateAvailabilityStatus('unavailable', WpBnbWC.settings.i18n?.invalidDates || 'Check-out must be after check-in'); return; } // Check availability this.checkAvailability(checkIn, checkOut); } onGuestsChange() { // Re-validate if needed const checkIn = this.$form.find('[name="bnb_check_in"]').val(); const checkOut = this.$form.find('[name="bnb_check_out"]').val(); if (checkIn && checkOut) { this.checkAvailability(checkIn, checkOut); } } onServiceChange(e) { const $checkbox = $(e.currentTarget); const $item = $checkbox.closest('.bnb-wc-service-item'); const $qtyInput = $item.find('.bnb-service-qty'); if ($checkbox.is(':checked')) { $item.addClass('selected'); $qtyInput.prop('disabled', false); } else { $item.removeClass('selected'); $qtyInput.prop('disabled', true); } this.updatePriceDisplay(); } onServiceQtyChange() { this.updatePriceDisplay(); } checkAvailability(checkIn, checkOut) { // Debounce clearTimeout(this.checkAvailabilityTimeout); this.updateAvailabilityStatus('checking', WpBnbWC.settings.i18n?.checking || 'Checking availability...'); this.checkAvailabilityTimeout = setTimeout(() => { const guests = this.$form.find('[name="bnb_guests"]').val() || 1; $.ajax({ url: WpBnbWC.settings.ajaxUrl, type: 'POST', data: { action: 'wp_bnb_get_availability', nonce: WpBnbWC.settings.nonce, room_id: this.roomId, check_in: checkIn, check_out: checkOut, guests: guests }, success: (response) => { if (response.success) { const data = response.data; if (data.available) { this.updateAvailabilityStatus('available', WpBnbWC.settings.i18n?.available || 'Available'); this.updatePriceDisplay(data.price, data.breakdown); this.enableSubmit(); } else { this.updateAvailabilityStatus('unavailable', data.message || WpBnbWC.settings.i18n?.unavailable || 'Not available'); this.disableSubmit(); } } else { this.updateAvailabilityStatus('unavailable', response.data?.message || 'Error checking availability'); this.disableSubmit(); } }, error: () => { this.updateAvailabilityStatus('unavailable', WpBnbWC.settings.i18n?.error || 'Error checking availability'); this.disableSubmit(); } }); }, 500); } updateAvailabilityStatus(status, message) { const $statusEl = this.$form.find('.bnb-availability-status'); if (!status) { $statusEl.hide(); return; } $statusEl.removeClass('checking available unavailable').addClass(status); let icon = ''; switch (status) { case 'checking': icon = ''; break; case 'available': icon = ''; break; case 'unavailable': icon = ''; break; } $statusEl.html(icon + ' ' + message).show(); } updatePriceDisplay(roomPrice, breakdown) { const $priceDisplay = this.$form.find('.bnb-wc-price-display'); if (!roomPrice) { $priceDisplay.hide(); return; } // Calculate services total let servicesTotal = 0; const nights = breakdown?.nights || 1; this.$form.find('.bnb-wc-service-item').each(function () { const $item = $(this); const $checkbox = $item.find('.bnb-service-checkbox'); if ($checkbox.is(':checked')) { const price = parseFloat($item.data('price')) || 0; const pricingType = $item.data('pricing-type'); const qty = parseInt($item.find('.bnb-service-qty').val()) || 1; if (pricingType === 'per_night') { servicesTotal += price * qty * nights; } else if (pricingType === 'per_booking') { servicesTotal += price * qty; } } }); const grandTotal = roomPrice + servicesTotal; // Update display let html = '
' + (WpBnbWC.settings.i18n?.roomTotal || 'Room') + '' + WpBnbWC.formatPrice(roomPrice) + '
'; if (servicesTotal > 0) { html += '
' + (WpBnbWC.settings.i18n?.services || 'Services') + '' + WpBnbWC.formatPrice(servicesTotal) + '
'; } html += '
' + (WpBnbWC.settings.i18n?.total || 'Total') + '' + WpBnbWC.formatPrice(grandTotal) + '
'; $priceDisplay.html(html).show(); } enableSubmit() { this.$form.find('.add-to-cart-btn').prop('disabled', false); } disableSubmit() { this.$form.find('.add-to-cart-btn').prop('disabled', true); } onSubmit(e) { // Form will submit normally - WooCommerce handles the add to cart // Just validate one more time const checkIn = this.$form.find('[name="bnb_check_in"]').val(); const checkOut = this.$form.find('[name="bnb_check_out"]').val(); if (!checkIn || !checkOut) { e.preventDefault(); alert(WpBnbWC.settings.i18n?.selectDates || 'Please select check-in and check-out dates'); return false; } } } /** * Format price */ WpBnbWC.formatPrice = function (price) { const currency = this.settings.currency || 'CHF'; const symbol = this.settings.currencySymbol || currency; const decimals = this.settings.priceDecimals || 2; const decimalSep = this.settings.decimalSeparator || '.'; const thousandSep = this.settings.thousandSeparator || "'"; const formatted = price.toFixed(decimals).replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep).replace('.', decimalSep); return symbol + ' ' + formatted; }; // Initialize on DOM ready $(document).ready(function () { WpBnbWC.init(); }); // Export for external use window.WpBnbWC = WpBnbWC; })(jQuery);