/** * Tom's Java Jive - Admin JavaScript */ // Sidebar Toggle document.addEventListener('DOMContentLoaded', function() { const sidebarToggle = document.getElementById('sidebarToggle'); const sidebar = document.querySelector('.admin-sidebar'); if (sidebarToggle && sidebar) { sidebarToggle.addEventListener('click', function() { sidebar.classList.toggle('open'); }); // Close sidebar when clicking outside on mobile document.addEventListener('click', function(e) { if (window.innerWidth <= 1024) { if (!sidebar.contains(e.target) && !sidebarToggle.contains(e.target)) { sidebar.classList.remove('open'); } } }); } // Confirm dialogs document.querySelectorAll('[data-confirm]').forEach(el => { el.addEventListener('click', function(e) { if (!confirm(this.dataset.confirm)) { e.preventDefault(); } }); }); // Auto-hide alerts document.querySelectorAll('.alert').forEach(alert => { setTimeout(() => { alert.style.opacity = '0'; alert.style.transform = 'translateY(-10px)'; setTimeout(() => alert.remove(), 300); }, 5000); }); }); // Toast notifications const AdminToast = { container: null, init() { if (!this.container) { this.container = document.createElement('div'); this.container.style.cssText = 'position:fixed;bottom:20px;right:20px;z-index:1000;display:flex;flex-direction:column;gap:8px;'; document.body.appendChild(this.container); } }, show(message, type = 'success', duration = 3000) { this.init(); const colors = { success: '#10B981', error: '#EF4444', warning: '#F59E0B', info: '#3B82F6' }; const toast = document.createElement('div'); toast.style.cssText = ` background: white; padding: 12px 16px; border-radius: 8px; box-shadow: 0 10px 15px rgba(0,0,0,0.1); border-left: 4px solid ${colors[type] || colors.info}; display: flex; align-items: center; gap: 10px; animation: slideIn 0.3s ease; `; toast.innerHTML = ` ${message} `; this.container.appendChild(toast); setTimeout(() => { toast.style.animation = 'slideIn 0.3s ease reverse'; setTimeout(() => toast.remove(), 300); }, duration); }, success(msg) { this.show(msg, 'success'); }, error(msg) { this.show(msg, 'error'); }, warning(msg) { this.show(msg, 'warning'); }, info(msg) { this.show(msg, 'info'); } }; // API helper async function adminFetch(url, options = {}) { const defaults = { headers: { 'Content-Type': 'application/json', } }; const response = await fetch(url, { ...defaults, ...options }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Request failed'); } return data; } // Loading state function setLoading(button, isLoading) { if (isLoading) { button.dataset.originalHtml = button.innerHTML; button.innerHTML = ' Loading...'; button.disabled = true; } else { button.innerHTML = button.dataset.originalHtml || button.innerHTML; button.disabled = false; } } // Format currency function formatCurrency(amount) { return '$' + parseFloat(amount).toFixed(2); } // Format date function formatDate(dateString) { return new Date(dateString).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } // Debounce function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // Modal const Modal = { open(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.add('active'); document.body.style.overflow = 'hidden'; } }, close(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove('active'); document.body.style.overflow = ''; } } }; // Close modals on overlay click document.addEventListener('click', function(e) { if (e.target.classList.contains('modal-overlay')) { e.target.classList.remove('active'); document.body.style.overflow = ''; } }); // Image preview function previewImage(input, previewId) { const preview = document.getElementById(previewId); if (!preview) return; if (input.files && input.files[0]) { const reader = new FileReader(); reader.onload = function(e) { preview.src = e.target.result; preview.style.display = 'block'; }; reader.readAsDataURL(input.files[0]); } } // Add slideIn animation const style = document.createElement('style'); style.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } `; document.head.appendChild(style);