first commit
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
// Dashboard Charts JavaScript - Mit echten Daten aus der API
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Dashboard charts initializing...');
|
||||
|
||||
// Prüfe ob Chart.js geladen ist
|
||||
if (typeof Chart === 'undefined') {
|
||||
console.error('Chart.js not loaded!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Chart.js loaded, version:', Chart.version);
|
||||
|
||||
// Dashboard-Daten über API laden
|
||||
fetch('/api/dashboard-data')
|
||||
.then(response => {
|
||||
console.log('API Response status:', response.status);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Dashboard data loaded:', data);
|
||||
initializeCharts(data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading dashboard data:', error);
|
||||
// Fallback mit Demo-Daten
|
||||
console.log('Using fallback demo data...');
|
||||
initializeChartsWithDemoData();
|
||||
});
|
||||
});
|
||||
|
||||
function initializeChartsWithDemoData() {
|
||||
console.log('Initializing charts with demo data...');
|
||||
|
||||
const demoData = {
|
||||
monthly_stats: [
|
||||
{ month: '2024-01', income: 3000, expenses: 2200, net_change: 800, balance: 5000 },
|
||||
{ month: '2024-02', income: 3200, expenses: 2400, net_change: 800, balance: 5800 },
|
||||
{ month: '2024-03', income: 2800, expenses: 2100, net_change: 700, balance: 6500 }
|
||||
],
|
||||
category_stats: [
|
||||
{ category_name: 'Lebensmittel', total_amount: 600, percentage: 27.3 },
|
||||
{ category_name: 'Transport', total_amount: 250, percentage: 11.4 },
|
||||
{ category_name: 'Unterhaltung', total_amount: 180, percentage: 8.2 },
|
||||
{ category_name: 'Sonstiges', total_amount: 170, percentage: 7.7 }
|
||||
],
|
||||
transaction_trend: [
|
||||
{ date: '2024-03-01T00:00:00Z', income: 100, expense: 50, balance: 5000 },
|
||||
{ date: '2024-03-02T00:00:00Z', income: 0, expense: 80, balance: 4920 },
|
||||
{ date: '2024-03-03T00:00:00Z', income: 200, expense: 120, balance: 5000 },
|
||||
{ date: '2024-03-04T00:00:00Z', income: 50, expense: 90, balance: 4960 },
|
||||
{ date: '2024-03-05T00:00:00Z', income: 0, expense: 70, balance: 4890 }
|
||||
],
|
||||
asset_overview: {
|
||||
total_bank_balance: 15000,
|
||||
total_depot_value: 25000
|
||||
}
|
||||
};
|
||||
|
||||
initializeCharts(demoData);
|
||||
}
|
||||
|
||||
function initializeCharts(data) {
|
||||
console.log('Initializing charts with data:', data);
|
||||
|
||||
// Chart 1: Monatliche Statistiken
|
||||
createMonthlyChart(data);
|
||||
|
||||
// Chart 2: Einnahmen vs Ausgaben
|
||||
createIncomeExpenseChart(data);
|
||||
|
||||
// Chart 3: Trend-Chart
|
||||
createTrendChart(data);
|
||||
|
||||
console.log('All charts initialized');
|
||||
}
|
||||
|
||||
function createMonthlyChart(data) {
|
||||
const ctx = document.getElementById('monthlyChart');
|
||||
if (!ctx) {
|
||||
console.warn('Monthly chart canvas not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Echte Datenstruktur aus der API verwenden
|
||||
const monthlyStats = data.monthly_stats || data.monthlyStats || [];
|
||||
console.log('Monthly stats data:', monthlyStats);
|
||||
|
||||
new Chart(ctx.getContext('2d'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: monthlyStats.map(d => {
|
||||
// Month aus "2024-01" Format zu "Januar 2024" konvertieren
|
||||
const [year, month] = d.month.split('-');
|
||||
const monthNames = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
||||
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'];
|
||||
return `${monthNames[parseInt(month) - 1]} ${year}`;
|
||||
}),
|
||||
datasets: [{
|
||||
label: 'Einnahmen',
|
||||
data: monthlyStats.map(d => parseFloat(d.income || 0)),
|
||||
backgroundColor: 'rgba(34, 197, 94, 0.7)',
|
||||
borderColor: 'rgba(34, 197, 94, 1)',
|
||||
borderWidth: 1
|
||||
}, {
|
||||
label: 'Ausgaben',
|
||||
data: monthlyStats.map(d => parseFloat(d.expenses || 0)),
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.7)',
|
||||
borderColor: 'rgba(239, 68, 68, 1)',
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '€' + value.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Monatliche Ein- und Ausgaben'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('Monthly chart created');
|
||||
} catch (error) {
|
||||
console.error('Error creating monthly chart:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function createIncomeExpenseChart(data) {
|
||||
const ctx = document.getElementById('incomeExpenseChart');
|
||||
if (!ctx) {
|
||||
console.warn('Income/Expense chart canvas not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Echte Datenstruktur verwenden
|
||||
const monthlyStats = data.monthly_stats || data.monthlyStats || [];
|
||||
console.log('Income/Expense stats data:', monthlyStats);
|
||||
|
||||
const totalIncome = monthlyStats.reduce((sum, d) => sum + parseFloat(d.income || 0), 0);
|
||||
const totalExpenses = monthlyStats.reduce((sum, d) => sum + parseFloat(d.expenses || 0), 0);
|
||||
|
||||
new Chart(ctx.getContext('2d'), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Einnahmen', 'Ausgaben'],
|
||||
datasets: [{
|
||||
data: [totalIncome, totalExpenses],
|
||||
backgroundColor: ['#22c55e', '#ef4444'],
|
||||
borderWidth: 2,
|
||||
borderColor: '#ffffff'
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Einnahmen vs. Ausgaben'
|
||||
},
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return context.label + ': €' + context.parsed.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('Income/Expense chart created');
|
||||
} catch (error) {
|
||||
console.error('Error creating income/expense chart:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function createTrendChart(data) {
|
||||
const ctx = document.getElementById('trendChart');
|
||||
if (!ctx) {
|
||||
console.warn('Trend chart canvas not found');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Echte Datenstruktur verwenden
|
||||
const trendData = data.transaction_trend || data.transactionTrend || [];
|
||||
console.log('Trend data:', trendData);
|
||||
|
||||
new Chart(ctx.getContext('2d'), {
|
||||
type: 'bar', // Basis-Typ für Balken
|
||||
data: {
|
||||
labels: trendData.map(d => {
|
||||
// Datum als Monat/Jahr formatieren für 12-Monats-Ansicht
|
||||
const date = new Date(d.date);
|
||||
return date.toLocaleDateString('de-DE', { month: 'short', year: 'numeric' });
|
||||
}),
|
||||
datasets: [{
|
||||
label: 'Einnahmen',
|
||||
type: 'bar', // Explizit als Bar definieren
|
||||
data: trendData.map(d => parseFloat(d.income || 0)),
|
||||
backgroundColor: 'rgba(34, 197, 94, 0.7)',
|
||||
borderColor: 'rgba(34, 197, 94, 1)',
|
||||
borderWidth: 1,
|
||||
yAxisID: 'y'
|
||||
}, {
|
||||
label: 'Ausgaben',
|
||||
type: 'bar', // Explizit als Bar definieren
|
||||
data: trendData.map(d => parseFloat(d.expense || 0)),
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.7)',
|
||||
borderColor: 'rgba(239, 68, 68, 1)',
|
||||
borderWidth: 1,
|
||||
yAxisID: 'y'
|
||||
}, {
|
||||
label: 'Saldo',
|
||||
type: 'line', // Explizit als Linie definieren
|
||||
data: trendData.map(d => parseFloat(d.balance || 0)),
|
||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||
borderColor: 'rgba(59, 130, 246, 1)',
|
||||
borderWidth: 3,
|
||||
fill: false,
|
||||
tension: 0.1,
|
||||
yAxisID: 'y1',
|
||||
pointRadius: 3,
|
||||
pointHoverRadius: 5
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Datum'
|
||||
}
|
||||
},
|
||||
y: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'left',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Einnahmen/Ausgaben (€)'
|
||||
},
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '€' + value.toLocaleString();
|
||||
}
|
||||
}
|
||||
},
|
||||
y1: {
|
||||
type: 'linear',
|
||||
display: true,
|
||||
position: 'right',
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Saldo (€)'
|
||||
},
|
||||
grid: {
|
||||
drawOnChartArea: false,
|
||||
},
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return '€' + value.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '90-Tage Trend'
|
||||
},
|
||||
legend: {
|
||||
position: 'top'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
return context.dataset.label + ': €' + context.parsed.y.toLocaleString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('Trend chart created');
|
||||
} catch (error) {
|
||||
console.error('Error creating trend chart:', error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user