Implement Phase 7: Contact Form 7 Integration (v0.7.0)
All checks were successful
Create Release Package / build-release (push) Successful in 1m1s
All checks were successful
Create Release Package / build-release (push) Successful in 1m1s
Add custom CF7 form tags for booking requests: - [bnb_building_select] - Building filter dropdown - [bnb_room_select] - Room selection with capacity data - [bnb_date_checkin/checkout] - Date pickers with validation - [bnb_guests] - Guest count with capacity limits Features: - Server-side validation for all fields - Real-time AJAX availability checking - Automatic price calculation display - Booking creation on form submission - Guest record creation/linking - Custom mail tags for CF7 templates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
344
assets/css/cf7-integration.css
Normal file
344
assets/css/cf7-integration.css
Normal file
@@ -0,0 +1,344 @@
|
||||
/**
|
||||
* WP BnB Contact Form 7 Integration Styles
|
||||
*
|
||||
* Styling for CF7 booking forms.
|
||||
*
|
||||
* @package Magdev\WpBnb
|
||||
*/
|
||||
|
||||
/* Custom Properties */
|
||||
:root {
|
||||
--wp-bnb-cf7-primary: #2271b1;
|
||||
--wp-bnb-cf7-success: #00a32a;
|
||||
--wp-bnb-cf7-warning: #dba617;
|
||||
--wp-bnb-cf7-error: #d63638;
|
||||
--wp-bnb-cf7-text: #1d2327;
|
||||
--wp-bnb-cf7-text-light: #646970;
|
||||
--wp-bnb-cf7-border: #c3c4c7;
|
||||
--wp-bnb-cf7-bg: #f0f0f1;
|
||||
--wp-bnb-cf7-radius: 4px;
|
||||
--wp-bnb-cf7-spacing: 1rem;
|
||||
}
|
||||
|
||||
/* Form Layout */
|
||||
.wp-bnb-booking-form,
|
||||
.wp-bnb-inquiry-form {
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.wp-bnb-booking-form h3,
|
||||
.wp-bnb-booking-form h4,
|
||||
.wp-bnb-inquiry-form h3,
|
||||
.wp-bnb-inquiry-form h4 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.75em;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px solid var(--wp-bnb-cf7-border);
|
||||
}
|
||||
|
||||
.wp-bnb-booking-form h3:first-child,
|
||||
.wp-bnb-inquiry-form h3:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Form Rows */
|
||||
.wp-bnb-form-row {
|
||||
margin-bottom: var(--wp-bnb-cf7-spacing);
|
||||
}
|
||||
|
||||
.wp-bnb-form-row-2col {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--wp-bnb-cf7-spacing);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.wp-bnb-form-row-2col {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Form Fields */
|
||||
.wp-bnb-form-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wp-bnb-form-field label {
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
color: var(--wp-bnb-cf7-text);
|
||||
}
|
||||
|
||||
/* Custom CF7 Tags Styling */
|
||||
.wp-bnb-building-select,
|
||||
.wp-bnb-room-select,
|
||||
.wp-bnb-date-checkin,
|
||||
.wp-bnb-date-checkout,
|
||||
.wp-bnb-guests {
|
||||
width: 100%;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
border: 1px solid var(--wp-bnb-cf7-border);
|
||||
border-radius: var(--wp-bnb-cf7-radius);
|
||||
background-color: #fff;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.wp-bnb-building-select:focus,
|
||||
.wp-bnb-room-select:focus,
|
||||
.wp-bnb-date-checkin:focus,
|
||||
.wp-bnb-date-checkout:focus,
|
||||
.wp-bnb-guests:focus {
|
||||
outline: none;
|
||||
border-color: var(--wp-bnb-cf7-primary);
|
||||
box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.25);
|
||||
}
|
||||
|
||||
/* Select dropdown */
|
||||
.wp-bnb-room-select optgroup {
|
||||
font-weight: 600;
|
||||
font-style: normal;
|
||||
color: var(--wp-bnb-cf7-text);
|
||||
}
|
||||
|
||||
/* Date inputs */
|
||||
.wp-bnb-date-checkin,
|
||||
.wp-bnb-date-checkout {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Number input */
|
||||
.wp-bnb-guests {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
/* Availability Status */
|
||||
.wp-bnb-availability-status {
|
||||
padding: var(--wp-bnb-cf7-spacing);
|
||||
margin: var(--wp-bnb-cf7-spacing) 0;
|
||||
background-color: var(--wp-bnb-cf7-bg);
|
||||
border-radius: var(--wp-bnb-cf7-radius);
|
||||
text-align: center;
|
||||
min-height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wp-bnb-availability-status:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wp-bnb-checking {
|
||||
color: var(--wp-bnb-cf7-text-light);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.wp-bnb-checking::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
border: 2px solid var(--wp-bnb-cf7-border);
|
||||
border-top-color: var(--wp-bnb-cf7-primary);
|
||||
border-radius: 50%;
|
||||
animation: wp-bnb-spin 0.8s linear infinite;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes wp-bnb-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.wp-bnb-available {
|
||||
color: var(--wp-bnb-cf7-success);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wp-bnb-available::before {
|
||||
content: "\2713";
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.wp-bnb-unavailable {
|
||||
color: var(--wp-bnb-cf7-error);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wp-bnb-unavailable::before {
|
||||
content: "\2717";
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
/* Price Display */
|
||||
.wp-bnb-price-display {
|
||||
padding: var(--wp-bnb-cf7-spacing);
|
||||
margin: var(--wp-bnb-cf7-spacing) 0;
|
||||
background-color: #e7f5ea;
|
||||
border: 1px solid var(--wp-bnb-cf7-success);
|
||||
border-radius: var(--wp-bnb-cf7-radius);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.wp-bnb-price-display:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wp-bnb-price-label {
|
||||
color: var(--wp-bnb-cf7-text-light);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.wp-bnb-price-amount {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--wp-bnb-cf7-success);
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.wp-bnb-nights {
|
||||
color: var(--wp-bnb-cf7-text-light);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Capacity Warning */
|
||||
.wp-bnb-capacity-warning {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
color: var(--wp-bnb-cf7-error);
|
||||
background-color: #fcf0f1;
|
||||
border-radius: var(--wp-bnb-cf7-radius);
|
||||
}
|
||||
|
||||
/* Validation Errors */
|
||||
.wpcf7-form-control-wrap .wpcf7-not-valid-tip {
|
||||
color: var(--wp-bnb-cf7-error);
|
||||
font-size: 0.8125rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.wpcf7-form-control.wpcf7-not-valid {
|
||||
border-color: var(--wp-bnb-cf7-error);
|
||||
}
|
||||
|
||||
/* Response Messages */
|
||||
.wpcf7 form.sent .wpcf7-response-output {
|
||||
border-color: var(--wp-bnb-cf7-success);
|
||||
background-color: #e7f5ea;
|
||||
color: var(--wp-bnb-cf7-success);
|
||||
}
|
||||
|
||||
.wpcf7 form.failed .wpcf7-response-output,
|
||||
.wpcf7 form.aborted .wpcf7-response-output,
|
||||
.wpcf7 form.spam .wpcf7-response-output,
|
||||
.wpcf7 form.invalid .wpcf7-response-output {
|
||||
border-color: var(--wp-bnb-cf7-error);
|
||||
background-color: #fcf0f1;
|
||||
color: var(--wp-bnb-cf7-error);
|
||||
}
|
||||
|
||||
/* Submit Button */
|
||||
.wp-bnb-booking-form .wpcf7-submit,
|
||||
.wp-bnb-inquiry-form .wpcf7-submit {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background-color: var(--wp-bnb-cf7-primary);
|
||||
border: none;
|
||||
border-radius: var(--wp-bnb-cf7-radius);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.wp-bnb-booking-form .wpcf7-submit:hover,
|
||||
.wp-bnb-inquiry-form .wpcf7-submit:hover {
|
||||
background-color: #135e96;
|
||||
}
|
||||
|
||||
.wp-bnb-booking-form .wpcf7-submit:disabled,
|
||||
.wp-bnb-inquiry-form .wpcf7-submit:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Spinner */
|
||||
.wpcf7 .wpcf7-spinner {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
/* Hidden Room Field (for inquiry forms) */
|
||||
.wp-bnb-inquiry-form input[type="hidden"] + .wpcf7-form-control-wrap {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Form Section Headers */
|
||||
.wp-bnb-booking-form hr,
|
||||
.wp-bnb-inquiry-form hr {
|
||||
margin: 1.5rem 0;
|
||||
border: none;
|
||||
border-top: 1px solid var(--wp-bnb-cf7-border);
|
||||
}
|
||||
|
||||
/* Dark Mode Support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--wp-bnb-cf7-text: #f0f0f1;
|
||||
--wp-bnb-cf7-text-light: #a7aaad;
|
||||
--wp-bnb-cf7-border: #50575e;
|
||||
--wp-bnb-cf7-bg: #2c3338;
|
||||
}
|
||||
|
||||
.wp-bnb-building-select,
|
||||
.wp-bnb-room-select,
|
||||
.wp-bnb-date-checkin,
|
||||
.wp-bnb-date-checkout,
|
||||
.wp-bnb-guests {
|
||||
background-color: #3c434a;
|
||||
color: var(--wp-bnb-cf7-text);
|
||||
}
|
||||
|
||||
.wp-bnb-price-display {
|
||||
background-color: #1a3320;
|
||||
border-color: var(--wp-bnb-cf7-success);
|
||||
}
|
||||
|
||||
.wp-bnb-capacity-warning {
|
||||
background-color: #3c1618;
|
||||
}
|
||||
|
||||
.wpcf7 form.sent .wpcf7-response-output {
|
||||
background-color: #1a3320;
|
||||
}
|
||||
|
||||
.wpcf7 form.failed .wpcf7-response-output,
|
||||
.wpcf7 form.aborted .wpcf7-response-output,
|
||||
.wpcf7 form.spam .wpcf7-response-output,
|
||||
.wpcf7 form.invalid .wpcf7-response-output {
|
||||
background-color: #3c1618;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.wp-bnb-availability-status,
|
||||
.wp-bnb-price-display,
|
||||
.wpcf7-submit {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
375
assets/js/cf7-integration.js
Normal file
375
assets/js/cf7-integration.js
Normal file
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* WP BnB Contact Form 7 Integration
|
||||
*
|
||||
* Handles dynamic form behavior for booking forms.
|
||||
*
|
||||
* @package Magdev\WpBnb
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const WpBnbCF7 = {
|
||||
config: window.wpBnbCF7 || {},
|
||||
|
||||
/**
|
||||
* Initialize all CF7 integration features.
|
||||
*/
|
||||
init: function() {
|
||||
this.initBuildingRoomFilter();
|
||||
this.initDateValidation();
|
||||
this.initCapacityValidation();
|
||||
this.initAvailabilityCheck();
|
||||
this.initPriceDisplay();
|
||||
},
|
||||
|
||||
/**
|
||||
* Filter rooms dropdown when building is selected.
|
||||
*/
|
||||
initBuildingRoomFilter: function() {
|
||||
document.querySelectorAll('[data-bnb-building-select]').forEach(function(buildingSelect) {
|
||||
const form = buildingSelect.closest('form');
|
||||
const roomSelect = form ? form.querySelector('[data-bnb-room-select]') : null;
|
||||
|
||||
if (!roomSelect) return;
|
||||
|
||||
// Store all options for filtering
|
||||
const allOptions = Array.from(roomSelect.querySelectorAll('option, optgroup'));
|
||||
const originalHTML = roomSelect.innerHTML;
|
||||
|
||||
buildingSelect.addEventListener('change', function() {
|
||||
const selectedBuilding = buildingSelect.value;
|
||||
|
||||
// Show all options if no building selected
|
||||
if (!selectedBuilding) {
|
||||
roomSelect.innerHTML = originalHTML;
|
||||
roomSelect.dispatchEvent(new Event('change'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Filter options by building
|
||||
roomSelect.innerHTML = '';
|
||||
|
||||
// Add placeholder option
|
||||
const placeholder = document.createElement('option');
|
||||
placeholder.value = '';
|
||||
placeholder.textContent = WpBnbCF7.config.i18n?.selectRoom || '-- Select Room --';
|
||||
roomSelect.appendChild(placeholder);
|
||||
|
||||
allOptions.forEach(function(el) {
|
||||
if (el.tagName === 'OPTGROUP') {
|
||||
// Check if any options in this optgroup match
|
||||
const matchingOptions = Array.from(el.querySelectorAll('option')).filter(function(opt) {
|
||||
return opt.dataset.building === selectedBuilding;
|
||||
});
|
||||
|
||||
if (matchingOptions.length > 0) {
|
||||
const clonedGroup = el.cloneNode(false);
|
||||
matchingOptions.forEach(function(opt) {
|
||||
clonedGroup.appendChild(opt.cloneNode(true));
|
||||
});
|
||||
roomSelect.appendChild(clonedGroup);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger change to update dependent fields
|
||||
roomSelect.dispatchEvent(new Event('change'));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate and link check-in/check-out dates.
|
||||
*/
|
||||
initDateValidation: function() {
|
||||
document.querySelectorAll('[data-bnb-checkin]').forEach(function(checkinInput) {
|
||||
const form = checkinInput.closest('form');
|
||||
const checkoutInput = form ? form.querySelector('[data-bnb-checkout]') : null;
|
||||
|
||||
if (!checkoutInput) return;
|
||||
|
||||
// Set minimum check-in to today
|
||||
const today = WpBnbCF7.formatDate(new Date());
|
||||
if (!checkinInput.getAttribute('min') || checkinInput.getAttribute('min') < today) {
|
||||
checkinInput.setAttribute('min', today);
|
||||
}
|
||||
|
||||
checkinInput.addEventListener('change', function() {
|
||||
if (checkinInput.value) {
|
||||
// Set checkout minimum to checkin + 1 day
|
||||
const minCheckout = new Date(checkinInput.value);
|
||||
minCheckout.setDate(minCheckout.getDate() + 1);
|
||||
checkoutInput.setAttribute('min', WpBnbCF7.formatDate(minCheckout));
|
||||
|
||||
// Clear checkout if it's now invalid
|
||||
if (checkoutInput.value && checkoutInput.value <= checkinInput.value) {
|
||||
checkoutInput.value = '';
|
||||
}
|
||||
|
||||
// Trigger availability check
|
||||
WpBnbCF7.triggerAvailabilityCheck(form);
|
||||
}
|
||||
});
|
||||
|
||||
checkoutInput.addEventListener('change', function() {
|
||||
if (checkoutInput.value && checkinInput.value) {
|
||||
if (checkoutInput.value <= checkinInput.value) {
|
||||
alert(WpBnbCF7.config.i18n?.invalidDateRange || 'Check-out must be after check-in');
|
||||
checkoutInput.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger availability check
|
||||
WpBnbCF7.triggerAvailabilityCheck(form);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate guest count against room capacity.
|
||||
*/
|
||||
initCapacityValidation: function() {
|
||||
document.querySelectorAll('[data-bnb-guests]').forEach(function(guestsInput) {
|
||||
const form = guestsInput.closest('form');
|
||||
const roomSelect = form ? form.querySelector('[data-bnb-room-select]') : null;
|
||||
|
||||
if (!roomSelect) return;
|
||||
|
||||
const validateCapacity = function() {
|
||||
const selectedOption = roomSelect.selectedOptions[0];
|
||||
const capacity = parseInt(selectedOption?.dataset.capacity || 99, 10);
|
||||
const guests = parseInt(guestsInput.value || 0, 10);
|
||||
|
||||
// Update max attribute
|
||||
guestsInput.setAttribute('max', capacity);
|
||||
|
||||
// Show warning if over capacity
|
||||
const wrapper = guestsInput.closest('.wpcf7-form-control-wrap');
|
||||
let warning = wrapper ? wrapper.querySelector('.wp-bnb-capacity-warning') : null;
|
||||
|
||||
if (guests > capacity) {
|
||||
if (!warning && wrapper) {
|
||||
warning = document.createElement('span');
|
||||
warning.className = 'wp-bnb-capacity-warning';
|
||||
wrapper.appendChild(warning);
|
||||
}
|
||||
if (warning) {
|
||||
warning.textContent = (WpBnbCF7.config.i18n?.capacityExceeded || 'Maximum %d guests for this room').replace('%d', capacity);
|
||||
}
|
||||
} else if (warning) {
|
||||
warning.remove();
|
||||
}
|
||||
};
|
||||
|
||||
roomSelect.addEventListener('change', validateCapacity);
|
||||
guestsInput.addEventListener('change', validateCapacity);
|
||||
guestsInput.addEventListener('input', validateCapacity);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize AJAX availability checking.
|
||||
*/
|
||||
initAvailabilityCheck: function() {
|
||||
// Find forms with availability display
|
||||
document.querySelectorAll('.wp-bnb-availability-status').forEach(function(statusEl) {
|
||||
const form = statusEl.closest('form');
|
||||
if (form) {
|
||||
form._availabilityStatus = statusEl;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger availability check for a form.
|
||||
*
|
||||
* @param {HTMLFormElement} form Form element.
|
||||
*/
|
||||
triggerAvailabilityCheck: function(form) {
|
||||
const roomSelect = form.querySelector('[data-bnb-room-select]');
|
||||
const checkinInput = form.querySelector('[data-bnb-checkin]');
|
||||
const checkoutInput = form.querySelector('[data-bnb-checkout]');
|
||||
const statusEl = form._availabilityStatus || form.querySelector('.wp-bnb-availability-status');
|
||||
const priceEl = form.querySelector('.wp-bnb-price-display');
|
||||
|
||||
if (!roomSelect || !checkinInput || !checkoutInput) return;
|
||||
|
||||
const roomId = roomSelect.value;
|
||||
const checkIn = checkinInput.value;
|
||||
const checkOut = checkoutInput.value;
|
||||
|
||||
if (!roomId || !checkIn || !checkOut) {
|
||||
if (statusEl) statusEl.innerHTML = '';
|
||||
if (priceEl) priceEl.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '<span class="wp-bnb-checking">' + (WpBnbCF7.config.i18n?.checking || 'Checking availability...') + '</span>';
|
||||
}
|
||||
|
||||
// Make AJAX request
|
||||
WpBnbCF7.ajax('wp_bnb_get_availability', {
|
||||
room_id: roomId,
|
||||
check_in: checkIn,
|
||||
check_out: checkOut
|
||||
})
|
||||
.then(function(response) {
|
||||
if (statusEl) {
|
||||
if (response.available) {
|
||||
let html = '<span class="wp-bnb-available">' + (WpBnbCF7.config.i18n?.available || 'Room is available!') + '</span>';
|
||||
statusEl.innerHTML = html;
|
||||
} else {
|
||||
statusEl.innerHTML = '<span class="wp-bnb-unavailable">' + (WpBnbCF7.config.i18n?.unavailable || 'Room is not available for these dates') + '</span>';
|
||||
}
|
||||
}
|
||||
|
||||
// Update price display
|
||||
if (priceEl && response.available && response.price_formatted) {
|
||||
priceEl.innerHTML = '<span class="wp-bnb-price-label">' + (WpBnbCF7.config.i18n?.estimatedTotal || 'Estimated Total') + ':</span> ' +
|
||||
'<span class="wp-bnb-price-amount">' + response.price_formatted + '</span> ' +
|
||||
'<span class="wp-bnb-nights">(' + response.nights + ' ' + (WpBnbCF7.config.i18n?.nights || 'nights') + ')</span>';
|
||||
} else if (priceEl) {
|
||||
priceEl.innerHTML = '';
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('Availability check failed:', error);
|
||||
if (statusEl) {
|
||||
statusEl.innerHTML = '';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize price display updates.
|
||||
*/
|
||||
initPriceDisplay: function() {
|
||||
const self = this;
|
||||
|
||||
document.querySelectorAll('.wp-bnb-price-display').forEach(function(priceEl) {
|
||||
const form = priceEl.closest('form');
|
||||
if (!form) return;
|
||||
|
||||
const updatePrice = self.debounce(function() {
|
||||
const roomSelect = form.querySelector('[data-bnb-room-select]');
|
||||
const checkinInput = form.querySelector('[data-bnb-checkin]');
|
||||
const checkoutInput = form.querySelector('[data-bnb-checkout]');
|
||||
|
||||
if (!roomSelect?.value || !checkinInput?.value || !checkoutInput?.value) {
|
||||
priceEl.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
self.ajax('wp_bnb_calculate_price', {
|
||||
room_id: roomSelect.value,
|
||||
check_in: checkinInput.value,
|
||||
check_out: checkoutInput.value
|
||||
})
|
||||
.then(function(response) {
|
||||
priceEl.innerHTML = '<span class="wp-bnb-price-label">' + (self.config.i18n?.estimatedTotal || 'Estimated Total') + ':</span> ' +
|
||||
'<span class="wp-bnb-price-amount">' + response.price_formatted + '</span> ' +
|
||||
'<span class="wp-bnb-nights">(' + response.nights + ' ' + (self.config.i18n?.nights || 'nights') + ')</span>';
|
||||
})
|
||||
.catch(function() {
|
||||
priceEl.innerHTML = '';
|
||||
});
|
||||
}, 500);
|
||||
|
||||
// Bind to relevant field changes
|
||||
form.querySelectorAll('[data-bnb-room-select], [data-bnb-checkin], [data-bnb-checkout]')
|
||||
.forEach(function(input) {
|
||||
input.addEventListener('change', updatePrice);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Make AJAX request.
|
||||
*
|
||||
* @param {string} action AJAX action name.
|
||||
* @param {object} data Request data.
|
||||
* @return {Promise}
|
||||
*/
|
||||
ajax: function(action, data) {
|
||||
data = data || {};
|
||||
const formData = new FormData();
|
||||
formData.append('action', action);
|
||||
formData.append('nonce', this.config.nonce || '');
|
||||
|
||||
Object.keys(data).forEach(function(key) {
|
||||
if (data[key] !== null && data[key] !== undefined) {
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
});
|
||||
|
||||
return fetch(this.config.ajaxUrl || '/wp-admin/admin-ajax.php', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin'
|
||||
})
|
||||
.then(function(response) {
|
||||
return response.json();
|
||||
})
|
||||
.then(function(responseData) {
|
||||
if (!responseData.success) {
|
||||
throw new Error(responseData.data?.message || 'Request failed');
|
||||
}
|
||||
return responseData.data;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Format date as YYYY-MM-DD.
|
||||
*
|
||||
* @param {Date} date Date object.
|
||||
* @return {string}
|
||||
*/
|
||||
formatDate: function(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return year + '-' + month + '-' + day;
|
||||
},
|
||||
|
||||
/**
|
||||
* Debounce function.
|
||||
*
|
||||
* @param {function} func Function to debounce.
|
||||
* @param {number} wait Milliseconds to wait.
|
||||
* @return {function}
|
||||
*/
|
||||
debounce: function(func, wait) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
func.apply(context, args);
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize on DOM ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
WpBnbCF7.init();
|
||||
});
|
||||
} else {
|
||||
WpBnbCF7.init();
|
||||
}
|
||||
|
||||
// Re-initialize on CF7 form reset
|
||||
document.addEventListener('wpcf7reset', function(event) {
|
||||
setTimeout(function() {
|
||||
WpBnbCF7.init();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Export to window for external access
|
||||
window.WpBnbCF7 = WpBnbCF7;
|
||||
})();
|
||||
Reference in New Issue
Block a user