// ECM Control Frontend JavaScript document.addEventListener('DOMContentLoaded', function() { // Auto-hide Bootstrap modals after successful HTMX requests document.body.addEventListener('htmx:afterRequest', function(event) { console.log('HTMX Request completed:', event.detail); if (event.detail.successful) { console.log('HTMX Request was successful'); // Find any open modals and hide them const openModals = document.querySelectorAll('.modal.show'); console.log('Found open modals:', openModals.length); openModals.forEach(modal => { console.log('Closing modal:', modal.id); const modalInstance = bootstrap.Modal.getInstance(modal); if (modalInstance) { modalInstance.hide(); } else { // Fallback: hide manually modal.style.display = 'none'; modal.classList.remove('show'); const backdrop = document.querySelector('.modal-backdrop'); if (backdrop) { backdrop.remove(); } } }); // Show success notification showNotification('Operation completed successfully', 'success'); } else { console.log('HTMX Request failed:', event.detail); // Show error notification showNotification('Operation failed. Please try again.', 'error'); } }); // Clear form fields when modal is hidden document.querySelectorAll('.modal').forEach(modal => { modal.addEventListener('hidden.bs.modal', function() { const form = this.querySelector('form'); if (form) { form.reset(); } }); }); }); // Show notification function function showNotification(message, type = 'info') { const alertClass = type === 'success' ? 'alert-success' : type === 'error' ? 'alert-danger' : 'alert-info'; const notification = document.createElement('div'); notification.className = `alert ${alertClass} alert-dismissible fade show position-fixed`; notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;'; notification.innerHTML = ` ${message} `; document.body.appendChild(notification); // Auto-remove after 5 seconds setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); } // Confirm deletion dialogs document.body.addEventListener('click', function(event) { const deleteBtn = event.target.closest('[hx-delete]'); if (deleteBtn && deleteBtn.hasAttribute('hx-confirm')) { const confirmMessage = deleteBtn.getAttribute('hx-confirm'); if (!confirm(confirmMessage)) { event.preventDefault(); event.stopPropagation(); } } }); // Auto-refresh dashboard every 30 seconds if (window.location.pathname === '/') { setInterval(() => { // Only refresh if page is visible if (!document.hidden) { window.location.reload(); } }, 30000); } // Form validation helpers function validateRecipeForm(form) { const name = form.querySelector('[name="name"]').value.trim(); const gramsOut = parseFloat(form.querySelector('[name="grams_out"]').value); const timeoutSeconds = parseInt(form.querySelector('[name="timeout_seconds"]').value); if (!name) { showNotification('Recipe name is required', 'error'); return false; } if (gramsOut <= 0 || gramsOut > 100) { showNotification('Grams out must be between 0 and 100', 'error'); return false; } if (timeoutSeconds <= 0 || timeoutSeconds > 300) { showNotification('Timeout must be between 1 and 300 seconds', 'error'); return false; } return true; } function validateButtonForm(form) { const name = form.querySelector('[name="name"]').value.trim(); const gpioPin = parseInt(form.querySelector('[name="gpio_pin"]').value); if (!name) { showNotification('Button name is required', 'error'); return false; } if (gpioPin < 0 || gpioPin > 40) { showNotification('GPIO pin must be between 0 and 40', 'error'); return false; } return true; } // Add form validation to modals document.addEventListener('submit', function(event) { const form = event.target; if (form.matches('#addRecipeModal form')) { if (!validateRecipeForm(form)) { event.preventDefault(); } } if (form.matches('#addButtonModal form')) { if (!validateButtonForm(form)) { event.preventDefault(); } } }); // Keyboard shortcuts document.addEventListener('keydown', function(event) { // Ctrl/Cmd + N to add new recipe on recipes page if ((event.ctrlKey || event.metaKey) && event.key === 'n' && window.location.pathname === '/recipes') { event.preventDefault(); const modal = new bootstrap.Modal(document.getElementById('addRecipeModal')); modal.show(); } // Ctrl/Cmd + B to add new button on buttons page if ((event.ctrlKey || event.metaKey) && event.key === 'b' && window.location.pathname === '/buttons') { event.preventDefault(); const modal = new bootstrap.Modal(document.getElementById('addButtonModal')); modal.show(); } });