Files
care-book-block-ultimate/PRODUCTION-READY/care-booking-block-ultimate/public/js/frontend.min.js
Emanuel Almeida 38bb926742 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>
2025-09-12 01:27:34 +01:00

6 lines
7.3 KiB
JavaScript

/**
* Descomplicar® Crescimento Digital
* https://descomplicar.pt
*/
(function($, config) { 'use strict'; const CareBooking = { config: config || {}, initialized: false, retryCount: 0, observers: [], 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; }, 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) { if (this.hasKiviCareContent(node)) { hasNewContent = true; } } }); } }); if (hasNewContent) { this.enhanceNewContent(); } }); observer.observe(document.body, { childList: true, subtree: true }); this.observers.push(observer); }, 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); }, enhanceExistingElements: function() { this.enhanceLoadingStates(); this.enhanceFormValidation(); this.enhanceFallbackElements(); }, enhanceNewContent: function() { if (this.config.debug) { console.log('Care Booking Block: Enhancing new content'); } setTimeout(() => { this.enhanceExistingElements(); }, 100); }, enhanceLoadingStates: function() { const $forms = $(this.config.selectors.forms); $forms.each((index, form) => { const $form = $(form); if (!$form.find('.care-booking-loading').length) { $form.prepend('<div class="care-booking-loading" style="display: none;">Loading...</div>'); } $form.on('submit', (e) => { this.showLoadingState($form); }); $(document).on('ajaxStart', () => { if (this.isKiviCareAjax()) { this.showLoadingState($form); } }); $(document).on('ajaxComplete', () => { this.hideLoadingState($form); }); }); }, showLoadingState: function($element) { $element.addClass('care-booking-loading'); $element.find('.care-booking-loading').show(); }, hideLoadingState: function($element) { $element.removeClass('care-booking-loading'); $element.find('.care-booking-loading').hide(); }, isKiviCareAjax: function() { return window.location.href.indexOf('kivicare') !== -1 || document.body.className.indexOf('kivicare') !== -1; }, 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; } }); $form.find('select').on('change', (e) => { this.validateSelectField($(e.target)); }); }); }, 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; }, 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); } }, 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(); }, clearFieldError: function($field) { $field.removeClass('error'); $field.siblings('.field-error').hide(); }, enhanceFallbackElements: function() { $(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'); } }); }, setupEventListeners: function() { $(document).on('change', 'select[name="doctor_id"], .doctor-selection', (e) => { this.handleDoctorChange($(e.target)); }); $(document).on('change', 'select[name="service_id"], .service-selection', (e) => { this.handleServiceChange($(e.target)); }); $(document).on('click', '.care-booking-retry', (e) => { e.preventDefault(); this.retryOperation($(e.target)); }); }, handleDoctorChange: function($select) { const doctorId = $select.val(); if (this.config.debug) { console.log('Care Booking Block: Doctor changed to', doctorId); } const $serviceSelect = $select.closest('form').find('select[name="service_id"], .service-selection'); if ($serviceSelect.length) { $serviceSelect.val('').trigger('change'); this.updateServiceOptions($serviceSelect, doctorId); } }, handleServiceChange: function($select) { const serviceId = $select.val(); if (this.config.debug) { console.log('Care Booking Block: Service changed to', serviceId); } }, updateServiceOptions: function($serviceSelect, doctorId) { if (!doctorId || doctorId === '0') { return; } $serviceSelect.trigger('doctor_changed', [doctorId]); }, setupFallbacks: function() { if (!this.config.fallbackEnabled) { return; } this.setupAutoRetry(); this.setupOfflineDetection(); }, 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); } $.ajax(ajaxSettings); }, this.config.retryDelay); } }); }, 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) { this.retryPendingOperations(); } else { this.showOfflineMessage(); } }); }, retryPendingOperations: function() { if (this.config.debug) { console.log('Care Booking Block: Retrying pending operations'); } }, 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); } }, retryOperation: function($button) { const $container = $button.closest('.care-booking-container'); this.showLoadingState($container); setTimeout(() => { this.hideLoadingState($container); $button.closest('.error-message').fadeOut(); }, 1000); }, destroy: function() { this.observers.forEach(observer => observer.disconnect()); this.observers = []; $(document).off('.careBooking'); this.initialized = false; } }; $(document).ready(() => { CareBooking.init(); }); $(window).on('beforeunload', () => { CareBooking.destroy(); }); if (config && config.debug) { window.CareBooking = CareBooking; } })(jQuery, window.careBookingConfig);