// Dashboard JavaScript let categoryChart, monthlyChart; // Load dashboard data async function loadDashboardData() { try { const stats = await apiCall('/api/dashboard-stats'); // Store user currency globally for use across functions window.userCurrency = stats.currency || 'RON'; // Ensure we have valid data with defaults const totalSpent = parseFloat(stats.total_spent || 0); const activeCategories = parseInt(stats.active_categories || 0); const totalTransactions = parseInt(stats.total_transactions || 0); const categoryBreakdown = stats.category_breakdown || []; const monthlyData = stats.monthly_data || []; // Update KPI cards document.getElementById('total-spent').textContent = formatCurrency(totalSpent, window.userCurrency); document.getElementById('active-categories').textContent = activeCategories; document.getElementById('total-transactions').textContent = totalTransactions; // Update percent change const percentChange = document.getElementById('percent-change'); const percentChangeValue = parseFloat(stats.percent_change || 0); const isPositive = percentChangeValue >= 0; percentChange.className = `${isPositive ? 'bg-red-500/10 text-red-400' : 'bg-green-500/10 text-green-400'} text-xs font-semibold px-2 py-1 rounded-full flex items-center gap-1`; percentChange.innerHTML = ` ${isPositive ? 'trending_up' : 'trending_down'} ${Math.abs(percentChangeValue).toFixed(1)}% `; // Load charts with validated data loadCategoryChart(categoryBreakdown); loadMonthlyChart(monthlyData); // Load category cards loadCategoryCards(categoryBreakdown, totalSpent); // Load recent transactions loadRecentTransactions(); } catch (error) { console.error('Failed to load dashboard data:', error); } } // Category pie chart with CSS conic-gradient (beautiful & lightweight) function loadCategoryChart(data) { const pieChart = document.getElementById('pie-chart'); const pieTotal = document.getElementById('pie-total'); const pieLegend = document.getElementById('pie-legend'); if (!pieChart || !pieTotal || !pieLegend) return; if (!data || data.length === 0) { pieChart.style.background = 'conic-gradient(#233648 0% 100%)'; pieTotal.textContent = '0 lei'; pieLegend.innerHTML = '
' + (window.getTranslation ? window.getTranslation('dashboard.noData', 'No data available') : 'No data available') + '
'; return; } // Calculate total and get user currency from API response (stored globally) const total = data.reduce((sum, cat) => sum + parseFloat(cat.total || 0), 0); const userCurrency = window.userCurrency || 'RON'; pieTotal.textContent = formatCurrency(total, userCurrency); // Generate conic gradient segments let currentPercent = 0; const gradientSegments = data.map(cat => { const percent = total > 0 ? (parseFloat(cat.total || 0) / total) * 100 : 0; const segment = `${cat.color} ${currentPercent}% ${currentPercent + percent}%`; currentPercent += percent; return segment; }); // Apply gradient with smooth transitions pieChart.style.background = `conic-gradient(${gradientSegments.join(', ')})`; // Generate compact legend for 12-14 categories pieLegend.innerHTML = data.map(cat => { const percent = total > 0 ? ((parseFloat(cat.total || 0) / total) * 100).toFixed(1) : 0; return `${noTransText}
`; return; } container.innerHTML = data.transactions.map(tx => `${tx.description}
${tx.category_name} • ${formatDate(tx.date)}
${formatCurrency(tx.amount, window.userCurrency || 'RON')}
${tx.tags.length > 0 ? `${tx.tags.join(', ')}
` : ''}' + (window.getTranslation ? window.getTranslation('dashboard.noCategories', 'No categories yet') : 'No categories yet') + '
'; return; } // Icon mapping const categoryIcons = { 'Food & Dining': 'restaurant', 'Transportation': 'directions_car', 'Shopping': 'shopping_cart', 'Entertainment': 'movie', 'Bills & Utilities': 'receipt', 'Healthcare': 'medical_services', 'Education': 'school', 'Other': 'category' }; // Ensure totalSpent is a valid number const validTotalSpent = parseFloat(totalSpent || 0); container.innerHTML = categoryBreakdown.map(cat => { const total = parseFloat(cat.total || 0); const count = parseInt(cat.count || 0); const percentage = validTotalSpent > 0 ? ((total / validTotalSpent) * 100).toFixed(1) : 0; const icon = categoryIcons[cat.name] || 'category'; return `${count} ${count === 1 ? (window.getTranslation ? window.getTranslation('transactions.transaction', 'transaction') : 'transaction') : (window.getTranslation ? window.getTranslation('transactions.transactions', 'transactions') : 'transactions')}
${formatCurrency(total, window.userCurrency || 'RON')}
${cat.name}
${cat.color} • ${cat.icon}