/* ================================================================ NexaBoard - Main JavaScript ================================================================ */ declare const ApexCharts: { new (el: HTMLElement, options: Record): { render: () => void }; }; // DOM Elements const sidebar = document.getElementById('sidebar') as HTMLElement; const sidebarToggle = document.getElementById('sidebarToggle') as HTMLButtonElement; const sidebarClose = document.getElementById('sidebarClose') as HTMLButtonElement; const sidebarOverlay = document.getElementById('sidebarOverlay') as HTMLElement; const themeToggle = document.getElementById('themeToggle') as HTMLButtonElement; // ================================================================ // Sidebar Functions // ================================================================ function openSidebar() { sidebar?.classList.add('open'); sidebarOverlay?.classList.add('active'); document.body.style.overflow = 'hidden'; } function closeSidebar() { sidebar?.classList.remove('open'); sidebarOverlay?.classList.remove('active'); document.body.style.overflow = ''; } // Sidebar Toggle Events sidebarToggle?.addEventListener('click', openSidebar); sidebarClose?.addEventListener('click', closeSidebar); sidebarOverlay?.addEventListener('click', closeSidebar); // Submenu Toggle const submenuItems = document.querySelectorAll('.nav-item.has-submenu > .nav-link'); for (const item of submenuItems) { item.addEventListener('click', (e) => { e.preventDefault(); const parent = (item as HTMLElement).parentElement; parent?.classList.toggle('open'); }); } // ================================================================ // Theme Toggle // ================================================================ function initTheme() { const savedTheme = localStorage.getItem('theme') || 'light'; document.documentElement.setAttribute('data-theme', savedTheme); updateThemeIcon(savedTheme); } function toggleTheme() { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); updateThemeIcon(newTheme); // Reinitialize charts with new theme initCharts(); } function updateThemeIcon(theme: string) { const icon = themeToggle?.querySelector('i'); if (icon) { icon.className = theme === 'dark' ? 'ri-moon-line' : 'ri-sun-line'; } } themeToggle?.addEventListener('click', toggleTheme); initTheme(); // ================================================================ // Charts Configuration // ================================================================ function getChartColors() { const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; return { primary: '#14b8a6', secondary: '#94a3b8', success: '#22c55e', warning: '#f59e0b', danger: '#ef4444', info: '#3b82f6', text: isDark ? '#f8fafc' : '#0f172a', textSecondary: isDark ? '#94a3b8' : '#64748b', grid: isDark ? '#334155' : '#e2e8f0', background: isDark ? '#1e293b' : '#ffffff' }; } function initCharts() { const colors = getChartColors(); // Revenue Sparkline initSparkline('revenueSparkline', [45, 52, 38, 45, 55, 62, 58, 65, 72, 68, 75, 84], colors.primary); // Orders Sparkline initSparkline('ordersSparkline', [28, 32, 25, 38, 42, 35, 48, 52, 45, 58, 55, 62], colors.success); // Customers Sparkline initSparkline('customersSparkline', [120, 145, 132, 158, 175, 162, 188, 205, 195, 218, 235, 248], colors.warning); // Visitors Sparkline initSparkline('visitorsSparkline', [95, 88, 92, 85, 78, 82, 75, 72, 78, 68, 65, 70], colors.danger); // Revenue Chart initRevenueChart(colors); // Category Chart initCategoryChart(colors); } function initSparkline(elementId: string, data: number[], color: string) { const el = document.getElementById(elementId); if (!el) return; // Clear existing chart el.innerHTML = ''; const options = { series: [{ data: data }], chart: { type: 'area', height: 50, sparkline: { enabled: true }, animations: { enabled: true, easing: 'easeinout', speed: 800 } }, stroke: { width: 2, curve: 'smooth' }, fill: { type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.4, opacityTo: 0.1, stops: [0, 100] } }, colors: [color], tooltip: { enabled: false } }; const chart = new ApexCharts(el, options); chart.render(); } function initRevenueChart(colors: ReturnType) { const el = document.getElementById('revenueChart'); if (!el) return; // Clear existing chart el.innerHTML = ''; const options = { series: [ { name: 'Revenue', data: [44000, 55000, 57000, 56000, 61000, 58000, 63000, 60000, 66000, 72000, 68000, 84000] }, { name: 'Expenses', data: [35000, 41000, 36000, 42000, 38000, 44000, 40000, 46000, 42000, 48000, 44000, 52000] } ], chart: { type: 'area', height: 300, toolbar: { show: false }, fontFamily: 'Plus Jakarta Sans, sans-serif', foreColor: colors.textSecondary, animations: { enabled: true, easing: 'easeinout', speed: 800, animateGradually: { enabled: true, delay: 150 } } }, colors: [colors.primary, colors.secondary], fill: { type: 'gradient', gradient: { shadeIntensity: 1, opacityFrom: 0.35, opacityTo: 0.05, stops: [0, 95, 100] } }, stroke: { width: 2.5, curve: 'smooth' }, grid: { borderColor: colors.grid, strokeDashArray: 4, padding: { left: 0, right: 0 } }, xaxis: { categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], axisBorder: { show: false }, axisTicks: { show: false }, labels: { style: { colors: colors.textSecondary, fontSize: '12px' } } }, yaxis: { labels: { style: { colors: colors.textSecondary, fontSize: '12px' }, formatter: (value: number) => `${value / 1000}k` } }, legend: { show: false }, tooltip: { theme: document.documentElement.getAttribute('data-theme') || 'light', y: { formatter: (value: number) => `${value.toLocaleString()}` } }, dataLabels: { enabled: false } }; const chart = new ApexCharts(el, options); chart.render(); } function initCategoryChart(colors: ReturnType) { const el = document.getElementById('categoryChart'); if (!el) return; // Clear existing chart el.innerHTML = ''; const options = { series: [35, 28, 22, 15], chart: { type: 'donut', height: 200, fontFamily: 'Plus Jakarta Sans, sans-serif', animations: { enabled: true, easing: 'easeinout', speed: 800, animateGradually: { enabled: true, delay: 150 } } }, colors: [colors.primary, colors.success, colors.warning, colors.info], labels: ['Electronics', 'Fashion', 'Home & Living', 'Others'], stroke: { width: 0 }, plotOptions: { pie: { donut: { size: '75%', labels: { show: true, name: { show: true, fontSize: '14px', fontWeight: 500, color: colors.textSecondary }, value: { show: true, fontSize: '24px', fontWeight: 700, color: colors.text, formatter: (val: string) => `${val}%` }, total: { show: true, label: 'Total Sales', fontSize: '12px', color: colors.textSecondary, formatter: () => '$84,245' } } } } }, legend: { show: false }, dataLabels: { enabled: false }, tooltip: { theme: document.documentElement.getAttribute('data-theme') || 'light', y: { formatter: (value: number) => `${value}%` } } }; const chart = new ApexCharts(el, options); chart.render(); } // Initialize charts when DOM is ready document.addEventListener('DOMContentLoaded', () => { // Small delay to ensure ApexCharts is loaded setTimeout(initCharts, 100); }); // ================================================================ // Keyboard Shortcuts // ================================================================ document.addEventListener('keydown', (e) => { // Ctrl/Cmd + K to focus search if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); const searchInput = document.querySelector('.search-input') as HTMLInputElement; searchInput?.focus(); } // Escape to close sidebar if (e.key === 'Escape') { closeSidebar(); } }); // ================================================================ // Window Resize Handler // ================================================================ window.addEventListener('resize', () => { if (window.innerWidth > 1024) { closeSidebar(); } }); // ================================================================ // Task Checkbox Handler // ================================================================ const taskCheckboxes = document.querySelectorAll('.task-checkbox input'); for (const checkbox of taskCheckboxes) { checkbox.addEventListener('change', (e) => { const target = e.target as HTMLInputElement; const taskItem = target.closest('.task-item'); if (target.checked) { taskItem?.classList.add('completed'); } else { taskItem?.classList.remove('completed'); } }); } // ================================================================ // Dropdown Toggle (for mobile) // ================================================================ const headerDropdowns = document.querySelectorAll('.header-dropdown'); for (const dropdown of headerDropdowns) { const btn = dropdown.querySelector('.header-btn, .header-user'); const menu = dropdown.querySelector('.dropdown-menu'); btn?.addEventListener('click', (e) => { if (window.innerWidth <= 768) { e.stopPropagation(); menu?.classList.toggle('show'); } }); } // Close dropdowns when clicking outside document.addEventListener('click', () => { for (const menu of document.querySelectorAll('.dropdown-menu.show')) { menu.classList.remove('show'); } }); console.log('NexaBoard Admin Dashboard initialized');