chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
301
care-booking-block/public/css/frontend.css
Normal file
301
care-booking-block/public/css/frontend.css
Normal file
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Care Booking Block - Frontend CSS
|
||||
*
|
||||
* Base styles for enhanced KiviCare integration and graceful degradation
|
||||
*
|
||||
* @package CareBookingBlock
|
||||
*/
|
||||
|
||||
/* === LOADING STATES === */
|
||||
.care-booking-loading {
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.care-booking-loading::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0 -10px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: care-booking-spin 1s linear infinite;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.care-booking-loading::after {
|
||||
content: "Loading...";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 20px);
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
@keyframes care-booking-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* === FALLBACK STATES === */
|
||||
.care-booking-fallback {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.care-booking-fallback::after {
|
||||
content: "Service temporarily unavailable";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
border: 1px dashed #ccc;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* === ENHANCED KIVICARE SELECTORS === */
|
||||
.care-booking-enhanced {
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.care-booking-enhanced:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* KiviCare 3.0+ compatibility */
|
||||
.kc-doctor-item,
|
||||
.kc-service-item,
|
||||
.kivicare-doctor,
|
||||
.kivicare-service {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.kc-doctor-item[data-blocked="true"],
|
||||
.kc-service-item[data-blocked="true"],
|
||||
.kivicare-doctor[data-blocked="true"],
|
||||
.kivicare-service[data-blocked="true"] {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* === FORM ENHANCEMENTS === */
|
||||
.care-booking-form-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.care-booking-form-container .field-error {
|
||||
color: #dc3545;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.care-booking-form-container input.error,
|
||||
.care-booking-form-container select.error {
|
||||
border-color: #dc3545;
|
||||
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
|
||||
}
|
||||
|
||||
.care-booking-form-container .success-message {
|
||||
color: #28a745;
|
||||
background-color: #d4edda;
|
||||
border: 1px solid #c3e6cb;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.care-booking-form-container .error-message {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border: 1px solid #f5c6cb;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.care-booking-retry {
|
||||
background-color: #007cba;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.care-booking-retry:hover {
|
||||
background-color: #005a87;
|
||||
}
|
||||
|
||||
/* === OFFLINE STATES === */
|
||||
.care-booking-offline-message {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #ff6b6b;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
z-index: 10000;
|
||||
animation: care-booking-slide-down 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes care-booking-slide-down {
|
||||
from { transform: translateY(-100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* === ACCESSIBILITY === */
|
||||
.care-booking-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* === RESPONSIVE DESIGN === */
|
||||
@media (max-width: 768px) {
|
||||
.care-booking-loading::after {
|
||||
font-size: 11px;
|
||||
transform: translate(-50%, 15px);
|
||||
}
|
||||
|
||||
.care-booking-fallback::after {
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.care-booking-offline-message {
|
||||
font-size: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.care-booking-loading::before {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: -8px 0 0 -8px;
|
||||
}
|
||||
|
||||
.care-booking-loading::after {
|
||||
font-size: 10px;
|
||||
transform: translate(-50%, 12px);
|
||||
}
|
||||
}
|
||||
|
||||
/* === HIGH CONTRAST MODE === */
|
||||
@media (prefers-contrast: high) {
|
||||
.care-booking-fallback::after {
|
||||
background: #000;
|
||||
color: #fff;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
.care-booking-offline-message {
|
||||
background-color: #000;
|
||||
border-bottom: 2px solid #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* === REDUCED MOTION === */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.care-booking-enhanced,
|
||||
.kc-doctor-item,
|
||||
.kc-service-item,
|
||||
.kivicare-doctor,
|
||||
.kivicare-service {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@keyframes care-booking-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(0deg); }
|
||||
}
|
||||
|
||||
.care-booking-offline-message {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* === PRINT STYLES === */
|
||||
@media print {
|
||||
.care-booking-loading,
|
||||
.care-booking-loading::before,
|
||||
.care-booking-loading::after,
|
||||
.care-booking-offline-message,
|
||||
.care-booking-retry {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.care-booking-fallback::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.care-booking-fallback {
|
||||
opacity: 1;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
/* === DARK MODE SUPPORT === */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.care-booking-loading::after {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.care-booking-fallback::after {
|
||||
background: rgba(40, 40, 40, 0.95);
|
||||
color: #ccc;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
.care-booking-form-container .field-error {
|
||||
color: #ff6b6b;
|
||||
}
|
||||
|
||||
.care-booking-form-container .success-message {
|
||||
background-color: #1e4d2b;
|
||||
border-color: #2d5a35;
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
.care-booking-form-container .error-message {
|
||||
background-color: #4d1e24;
|
||||
border-color: #5a2d35;
|
||||
color: #fca5a5;
|
||||
}
|
||||
}
|
||||
6
care-booking-block/public/css/frontend.min.css
vendored
Normal file
6
care-booking-block/public/css/frontend.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
.care-booking-loading{position:relative;opacity:0.7;pointer-events:none;}.care-booking-loading::before{content:"";position:absolute;top:50%;left:50%;width:20px;height:20px;margin:-10px 0 0 -10px;border:2px solid #f3f3f3;border-top:2px solid #3498db;border-radius:50%;animation:care-booking-spin 1s linear infinite;z-index:1000;}.care-booking-loading::after{content:"Loading...";position:absolute;top:50%;left:50%;transform:translate(-50%,20px);font-size:12px;color:#666;z-index:1001;}@keyframes care-booking-spin{0%{transform:rotate(0deg);}100%{transform:rotate(360deg);}}.care-booking-fallback{opacity:0.7;pointer-events:none;position:relative;}.care-booking-fallback::after{content:"Service temporarily unavailable";position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(255,255,255,0.9);display:flex;align-items:center;justify-content:center;font-size:14px;color:#666;border:1px dashed #ccc;z-index:100;}.care-booking-enhanced{transition:opacity 0.3s ease,transform 0.3s ease;}.care-booking-enhanced:hover{opacity:0.9;transform:translateY(-1px);}.kc-doctor-item,.kc-service-item,.kivicare-doctor,.kivicare-service{transition:all 0.2s ease;}.kc-doctor-item[data-blocked="true"],.kc-service-item[data-blocked="true"],.kivicare-doctor[data-blocked="true"],.kivicare-service[data-blocked="true"]{opacity:0;height:0;overflow:hidden;margin:0;padding:0;border:none;}.care-booking-form-container{position:relative;}.care-booking-form-container .field-error{color:#dc3545;font-size:12px;margin-top:4px;display:block;}.care-booking-form-container input.error,.care-booking-form-container select.error{border-color:#dc3545;box-shadow:0 0 0 0.2rem rgba(220,53,69,0.25);}.care-booking-form-container .success-message{color:#28a745;background-color:#d4edda;border:1px solid #c3e6cb;padding:8px 12px;border-radius:4px;margin:10px 0;}.care-booking-form-container .error-message{color:#721c24;background-color:#f8d7da;border:1px solid #f5c6cb;padding:8px 12px;border-radius:4px;margin:10px 0;}.care-booking-retry{background-color:#007cba;color:white;border:none;padding:6px 12px;border-radius:3px;cursor:pointer;font-size:12px;margin-left:8px;}.care-booking-retry:hover{background-color:#005a87;}.care-booking-offline-message{position:fixed;top:0;left:0;right:0;background-color:#ff6b6b;color:white;padding:10px;text-align:center;z-index:10000;animation:care-booking-slide-down 0.3s ease;}@keyframes care-booking-slide-down{from{transform:translateY(-100%);}to{transform:translateY(0);}}.care-booking-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;}@media (max-width:768px){.care-booking-loading::after{font-size:11px;transform:translate(-50%,15px);}.care-booking-fallback::after{font-size:12px;padding:10px;}.care-booking-offline-message{font-size:14px;padding:8px;}}@media (max-width:480px){.care-booking-loading::before{width:16px;height:16px;margin:-8px 0 0 -8px;}.care-booking-loading::after{font-size:10px;transform:translate(-50%,12px);}}@media (prefers-contrast:high){.care-booking-fallback::after{background:#000;color:#fff;border:2px solid #fff;}.care-booking-offline-message{background-color:#000;border-bottom:2px solid #fff;}}@media (prefers-reduced-motion:reduce){.care-booking-enhanced,.kc-doctor-item,.kc-service-item,.kivicare-doctor,.kivicare-service{transition:none;}@keyframes care-booking-spin{0%{transform:rotate(0deg);}100%{transform:rotate(0deg);}}.care-booking-offline-message{animation:none;}}@media print{.care-booking-loading,.care-booking-loading::before,.care-booking-loading::after,.care-booking-offline-message,.care-booking-retry{display:none !important;}.care-booking-fallback::after{display:none;}.care-booking-fallback{opacity:1;pointer-events:all;}}@media (prefers-color-scheme:dark){.care-booking-loading::after{color:#ccc;}.care-booking-fallback::after{background:rgba(40,40,40,0.95);color:#ccc;border-color:#666;}.care-booking-form-container .field-error{color:#ff6b6b;}.care-booking-form-container .success-message{background-color:#1e4d2b;border-color:#2d5a35;color:#86efac;}.care-booking-form-container .error-message{background-color:#4d1e24;border-color:#5a2d35;color:#fca5a5;}}
|
||||
482
care-booking-block/public/js/frontend.js
Normal file
482
care-booking-block/public/js/frontend.js
Normal file
@@ -0,0 +1,482 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Care Booking Block - Frontend JavaScript
|
||||
*
|
||||
* Provides graceful degradation and enhanced interaction for KiviCare integration
|
||||
*
|
||||
* @package CareBookingBlock
|
||||
*/
|
||||
|
||||
(function($, config) {
|
||||
'use strict';
|
||||
|
||||
// Global configuration
|
||||
const CareBooking = {
|
||||
config: config || {},
|
||||
initialized: false,
|
||||
retryCount: 0,
|
||||
observers: [],
|
||||
|
||||
/**
|
||||
* Initialize the Care Booking frontend functionality
|
||||
*/
|
||||
init: function() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Initializing frontend scripts');
|
||||
}
|
||||
|
||||
this.setupObservers();
|
||||
this.enhanceExistingElements();
|
||||
this.setupEventListeners();
|
||||
this.setupFallbacks();
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup MutationObserver to watch for dynamically added content
|
||||
*/
|
||||
setupObservers: function() {
|
||||
if (!window.MutationObserver) {
|
||||
if (this.config.debug) {
|
||||
console.warn('Care Booking Block: MutationObserver not supported');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
let hasNewContent = false;
|
||||
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
|
||||
mutation.addedNodes.forEach((node) => {
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
// Check if new node contains KiviCare content
|
||||
if (this.hasKiviCareContent(node)) {
|
||||
hasNewContent = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (hasNewContent) {
|
||||
this.enhanceNewContent();
|
||||
}
|
||||
});
|
||||
|
||||
// Start observing
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
this.observers.push(observer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if element contains KiviCare content
|
||||
* @param {Element} element
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasKiviCareContent: function(element) {
|
||||
const selectors = [
|
||||
this.config.selectors.doctors,
|
||||
this.config.selectors.services,
|
||||
this.config.selectors.forms
|
||||
].join(', ');
|
||||
|
||||
return $(element).find(selectors).length > 0 || $(element).is(selectors);
|
||||
},
|
||||
|
||||
/**
|
||||
* Enhance existing KiviCare elements on page load
|
||||
*/
|
||||
enhanceExistingElements: function() {
|
||||
this.enhanceLoadingStates();
|
||||
this.enhanceFormValidation();
|
||||
this.enhanceFallbackElements();
|
||||
},
|
||||
|
||||
/**
|
||||
* Enhance newly added content
|
||||
*/
|
||||
enhanceNewContent: function() {
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Enhancing new content');
|
||||
}
|
||||
|
||||
// Add a small delay to ensure DOM is stable
|
||||
setTimeout(() => {
|
||||
this.enhanceExistingElements();
|
||||
}, 100);
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup loading states for better UX
|
||||
*/
|
||||
enhanceLoadingStates: function() {
|
||||
const $forms = $(this.config.selectors.forms);
|
||||
|
||||
$forms.each((index, form) => {
|
||||
const $form = $(form);
|
||||
|
||||
// Add loading indicator
|
||||
if (!$form.find('.care-booking-loading').length) {
|
||||
$form.prepend('<div class="care-booking-loading" style="display: none;">Loading...</div>');
|
||||
}
|
||||
|
||||
// Handle form submissions
|
||||
$form.on('submit', (e) => {
|
||||
this.showLoadingState($form);
|
||||
});
|
||||
|
||||
// Handle AJAX requests
|
||||
$(document).on('ajaxStart', () => {
|
||||
if (this.isKiviCareAjax()) {
|
||||
this.showLoadingState($form);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('ajaxComplete', () => {
|
||||
this.hideLoadingState($form);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show loading state
|
||||
* @param {jQuery} $element
|
||||
*/
|
||||
showLoadingState: function($element) {
|
||||
$element.addClass('care-booking-loading');
|
||||
$element.find('.care-booking-loading').show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide loading state
|
||||
* @param {jQuery} $element
|
||||
*/
|
||||
hideLoadingState: function($element) {
|
||||
$element.removeClass('care-booking-loading');
|
||||
$element.find('.care-booking-loading').hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if current AJAX request is KiviCare related
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isKiviCareAjax: function() {
|
||||
// This is a simplified check - could be enhanced based on KiviCare's AJAX patterns
|
||||
return window.location.href.indexOf('kivicare') !== -1 ||
|
||||
document.body.className.indexOf('kivicare') !== -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Enhance form validation
|
||||
*/
|
||||
enhanceFormValidation: function() {
|
||||
const $forms = $(this.config.selectors.forms);
|
||||
|
||||
$forms.each((index, form) => {
|
||||
const $form = $(form);
|
||||
|
||||
$form.on('submit', (e) => {
|
||||
if (!this.validateBookingForm($form)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Real-time validation for select fields
|
||||
$form.find('select').on('change', (e) => {
|
||||
this.validateSelectField($(e.target));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate booking form
|
||||
* @param {jQuery} $form
|
||||
* @returns {boolean}
|
||||
*/
|
||||
validateBookingForm: function($form) {
|
||||
let isValid = true;
|
||||
const requiredFields = $form.find('select[required], input[required]');
|
||||
|
||||
requiredFields.each((index, field) => {
|
||||
const $field = $(field);
|
||||
if (!$field.val() || $field.val() === '0' || $field.val() === '') {
|
||||
isValid = false;
|
||||
this.showFieldError($field, 'This field is required');
|
||||
} else {
|
||||
this.clearFieldError($field);
|
||||
}
|
||||
});
|
||||
|
||||
return isValid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Validate individual select field
|
||||
* @param {jQuery} $field
|
||||
*/
|
||||
validateSelectField: function($field) {
|
||||
const value = $field.val();
|
||||
|
||||
if ($field.attr('required') && (!value || value === '0' || value === '')) {
|
||||
this.showFieldError($field, 'Please make a selection');
|
||||
} else {
|
||||
this.clearFieldError($field);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show field error
|
||||
* @param {jQuery} $field
|
||||
* @param {string} message
|
||||
*/
|
||||
showFieldError: function($field, message) {
|
||||
$field.addClass('error');
|
||||
|
||||
let $error = $field.siblings('.field-error');
|
||||
if (!$error.length) {
|
||||
$error = $('<div class="field-error"></div>');
|
||||
$field.after($error);
|
||||
}
|
||||
|
||||
$error.text(message).show();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear field error
|
||||
* @param {jQuery} $field
|
||||
*/
|
||||
clearFieldError: function($field) {
|
||||
$field.removeClass('error');
|
||||
$field.siblings('.field-error').hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup fallback elements for graceful degradation
|
||||
*/
|
||||
enhanceFallbackElements: function() {
|
||||
// Add fallback classes to elements that might be blocked
|
||||
$(this.config.selectors.doctors).each((index, element) => {
|
||||
const $element = $(element);
|
||||
if (!$element.hasClass('care-booking-fallback')) {
|
||||
$element.addClass('care-booking-enhanced');
|
||||
}
|
||||
});
|
||||
|
||||
$(this.config.selectors.services).each((index, element) => {
|
||||
const $element = $(element);
|
||||
if (!$element.hasClass('care-booking-fallback')) {
|
||||
$element.addClass('care-booking-enhanced');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup event listeners
|
||||
*/
|
||||
setupEventListeners: function() {
|
||||
// Handle dynamic doctor selection
|
||||
$(document).on('change', 'select[name="doctor_id"], .doctor-selection', (e) => {
|
||||
this.handleDoctorChange($(e.target));
|
||||
});
|
||||
|
||||
// Handle service selection
|
||||
$(document).on('change', 'select[name="service_id"], .service-selection', (e) => {
|
||||
this.handleServiceChange($(e.target));
|
||||
});
|
||||
|
||||
// Handle retry buttons
|
||||
$(document).on('click', '.care-booking-retry', (e) => {
|
||||
e.preventDefault();
|
||||
this.retryOperation($(e.target));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle doctor selection change
|
||||
* @param {jQuery} $select
|
||||
*/
|
||||
handleDoctorChange: function($select) {
|
||||
const doctorId = $select.val();
|
||||
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Doctor changed to', doctorId);
|
||||
}
|
||||
|
||||
// Clear service selection if doctor changed
|
||||
const $serviceSelect = $select.closest('form').find('select[name="service_id"], .service-selection');
|
||||
if ($serviceSelect.length) {
|
||||
$serviceSelect.val('').trigger('change');
|
||||
this.updateServiceOptions($serviceSelect, doctorId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle service selection change
|
||||
* @param {jQuery} $select
|
||||
*/
|
||||
handleServiceChange: function($select) {
|
||||
const serviceId = $select.val();
|
||||
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Service changed to', serviceId);
|
||||
}
|
||||
|
||||
// Additional service-specific logic can be added here
|
||||
},
|
||||
|
||||
/**
|
||||
* Update service options based on selected doctor
|
||||
* @param {jQuery} $serviceSelect
|
||||
* @param {string} doctorId
|
||||
*/
|
||||
updateServiceOptions: function($serviceSelect, doctorId) {
|
||||
if (!doctorId || doctorId === '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
// This would typically make an AJAX request to get services
|
||||
// For now, we'll rely on KiviCare's existing functionality
|
||||
$serviceSelect.trigger('doctor_changed', [doctorId]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup fallback mechanisms
|
||||
*/
|
||||
setupFallbacks: function() {
|
||||
if (!this.config.fallbackEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup automatic retry for failed operations
|
||||
this.setupAutoRetry();
|
||||
|
||||
// Setup offline detection
|
||||
this.setupOfflineDetection();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup automatic retry for failed operations
|
||||
*/
|
||||
setupAutoRetry: function() {
|
||||
$(document).on('ajaxError', (event, jqXHR, ajaxSettings, thrownError) => {
|
||||
if (this.isKiviCareAjax() && this.retryCount < this.config.retryAttempts) {
|
||||
setTimeout(() => {
|
||||
this.retryCount++;
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Retrying operation, attempt', this.retryCount);
|
||||
}
|
||||
|
||||
// Retry the failed request
|
||||
$.ajax(ajaxSettings);
|
||||
}, this.config.retryDelay);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup offline detection
|
||||
*/
|
||||
setupOfflineDetection: function() {
|
||||
$(window).on('online offline', (e) => {
|
||||
const isOnline = e.type === 'online';
|
||||
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Connection status changed to', isOnline ? 'online' : 'offline');
|
||||
}
|
||||
|
||||
if (isOnline) {
|
||||
// Retry any pending operations
|
||||
this.retryPendingOperations();
|
||||
} else {
|
||||
// Show offline message
|
||||
this.showOfflineMessage();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retry pending operations when back online
|
||||
*/
|
||||
retryPendingOperations: function() {
|
||||
// Implementation would depend on what operations need to be retried
|
||||
if (this.config.debug) {
|
||||
console.log('Care Booking Block: Retrying pending operations');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show offline message
|
||||
*/
|
||||
showOfflineMessage: function() {
|
||||
const message = '<div class="care-booking-offline-message">You appear to be offline. Some features may not work properly.</div>';
|
||||
|
||||
if (!$('.care-booking-offline-message').length) {
|
||||
$('body').prepend(message);
|
||||
|
||||
setTimeout(() => {
|
||||
$('.care-booking-offline-message').fadeOut();
|
||||
}, 5000);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Retry a specific operation
|
||||
* @param {jQuery} $button
|
||||
*/
|
||||
retryOperation: function($button) {
|
||||
const $container = $button.closest('.care-booking-container');
|
||||
this.showLoadingState($container);
|
||||
|
||||
// Simulate retry - in practice, this would repeat the failed operation
|
||||
setTimeout(() => {
|
||||
this.hideLoadingState($container);
|
||||
$button.closest('.error-message').fadeOut();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanup resources
|
||||
*/
|
||||
destroy: function() {
|
||||
// Remove observers
|
||||
this.observers.forEach(observer => observer.disconnect());
|
||||
this.observers = [];
|
||||
|
||||
// Remove event listeners
|
||||
$(document).off('.careBooking');
|
||||
|
||||
this.initialized = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
$(document).ready(() => {
|
||||
CareBooking.init();
|
||||
});
|
||||
|
||||
// Handle page unload
|
||||
$(window).on('beforeunload', () => {
|
||||
CareBooking.destroy();
|
||||
});
|
||||
|
||||
// Expose to global scope for debugging
|
||||
if (config && config.debug) {
|
||||
window.CareBooking = CareBooking;
|
||||
}
|
||||
|
||||
})(jQuery, window.careBookingConfig);
|
||||
6
care-booking-block/public/js/frontend.min.js
vendored
Normal file
6
care-booking-block/public/js/frontend.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user