diff --git a/index.html b/index.html
index 69649ae..6d94de6 100644
--- a/index.html
+++ b/index.html
@@ -3,14 +3,14 @@
diff --git a/script.js b/script.js
index 5214495..0349d21 100644
--- a/script.js
+++ b/script.js
@@ -1,4 +1,4 @@
-class ETFTradeTracker {
+class PersonalFinanceTracker {
constructor() {
this.trades = [];
this.currentPrices = new Map(); // Store current market prices
@@ -793,6 +793,10 @@ class ETFTradeTracker {
// Load and display top accounts for each segment
this.loadDashboardTopAccounts();
+
+ // Initialize and update growth charts
+ this.initializeGrowthCharts();
+ this.updateGrowthCharts();
}
updateDashboardColors(totalGains) {
@@ -3702,6 +3706,191 @@ class ETFTradeTracker {
subscriptionsValue.textContent = '€0.00';
}
}
+
+ // Growth Charts Functionality
+ initializeGrowthCharts() {
+ // Initialize chart data storage
+ this.chartData = {
+ portfolio: [],
+ cash: [],
+ total: [],
+ currentPeriod: '7d'
+ };
+
+ // Bind period selector events
+ const periodButtons = document.querySelectorAll('.period-btn');
+ periodButtons.forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ // Update active button
+ periodButtons.forEach(b => b.classList.remove('active'));
+ e.target.classList.add('active');
+
+ // Update period and refresh charts
+ this.chartData.currentPeriod = e.target.dataset.period;
+ this.updateGrowthCharts();
+ });
+ });
+
+ // Generate initial mock data
+ this.generateMockChartData();
+ }
+
+ generateMockChartData() {
+ // Generate mock historical data for different time periods
+ const periods = {
+ '7d': 7,
+ '1m': 30,
+ '3m': 90,
+ '6m': 180,
+ '1y': 365
+ };
+
+ Object.keys(periods).forEach(period => {
+ const days = periods[period];
+ const portfolioData = [];
+ const cashData = [];
+ const totalData = [];
+
+ // Get current values as base
+ const currentPortfolio = this.calculatePortfolioTotals().currentValue;
+ const currentCash = 5000; // Mock current cash value
+ const currentTotal = currentPortfolio + currentCash;
+
+ // Generate historical values with some growth trend
+ for (let i = days; i >= 0; i--) {
+ const date = new Date();
+ date.setDate(date.getDate() - i);
+
+ // Add some randomness but overall upward trend
+ const growthFactor = Math.pow(1.0008, days - i); // 0.08% daily growth on average
+ const randomFactor = 0.95 + Math.random() * 0.1; // ±5% random variation
+
+ const portfolioValue = currentPortfolio / growthFactor * randomFactor;
+ const cashValue = currentCash + (Math.random() - 0.5) * 1000; // Cash fluctuates more
+
+ portfolioData.push({ date, value: portfolioValue });
+ cashData.push({ date, value: Math.max(0, cashValue) });
+ totalData.push({ date, value: portfolioValue + Math.max(0, cashValue) });
+ }
+
+ this.chartData[`portfolio_${period}`] = portfolioData;
+ this.chartData[`cash_${period}`] = cashData;
+ this.chartData[`total_${period}`] = totalData;
+ });
+ }
+
+ updateGrowthCharts() {
+ const period = this.chartData.currentPeriod;
+
+ // Update portfolio chart
+ this.renderChart('portfolio-chart', this.chartData[`portfolio_${period}`], '#28a745');
+ this.updateChartStats('portfolio', this.chartData[`portfolio_${period}`]);
+
+ // Update cash chart
+ this.renderChart('cash-chart', this.chartData[`cash_${period}`], '#17a2b8');
+ this.updateChartStats('cash', this.chartData[`cash_${period}`]);
+
+ // Update total chart
+ this.renderChart('total-chart', this.chartData[`total_${period}`], '#667eea');
+ this.updateChartStats('total', this.chartData[`total_${period}`]);
+ }
+
+ renderChart(canvasId, data, color) {
+ const canvas = document.getElementById(canvasId);
+ if (!canvas) return;
+
+ const ctx = canvas.getContext('2d');
+ const rect = canvas.getBoundingClientRect();
+
+ // Set canvas size
+ canvas.width = rect.width * window.devicePixelRatio;
+ canvas.height = rect.height * window.devicePixelRatio;
+ ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
+
+ // Clear canvas
+ ctx.clearRect(0, 0, rect.width, rect.height);
+
+ if (!data || data.length < 2) return;
+
+ // Calculate bounds
+ const values = data.map(d => d.value);
+ const minValue = Math.min(...values);
+ const maxValue = Math.max(...values);
+ const valueRange = maxValue - minValue;
+ const padding = 20;
+
+ // Draw grid lines
+ ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--border-light');
+ ctx.lineWidth = 1;
+ ctx.setLineDash([2, 2]);
+
+ for (let i = 0; i <= 4; i++) {
+ const y = padding + (i * (rect.height - 2 * padding)) / 4;
+ ctx.beginPath();
+ ctx.moveTo(padding, y);
+ ctx.lineTo(rect.width - padding, y);
+ ctx.stroke();
+ }
+ ctx.setLineDash([]);
+
+ // Draw line chart
+ ctx.strokeStyle = color;
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+
+ data.forEach((point, index) => {
+ const x = padding + (index * (rect.width - 2 * padding)) / (data.length - 1);
+ const y = rect.height - padding - ((point.value - minValue) / valueRange) * (rect.height - 2 * padding);
+
+ if (index === 0) {
+ ctx.moveTo(x, y);
+ } else {
+ ctx.lineTo(x, y);
+ }
+ });
+
+ ctx.stroke();
+
+ // Fill area under curve
+ ctx.globalAlpha = 0.1;
+ ctx.fillStyle = color;
+ ctx.lineTo(rect.width - padding, rect.height - padding);
+ ctx.lineTo(padding, rect.height - padding);
+ ctx.closePath();
+ ctx.fill();
+ ctx.globalAlpha = 1;
+ }
+
+ updateChartStats(type, data) {
+ if (!data || data.length < 2) return;
+
+ const latest = data[data.length - 1];
+ const previous = data[0];
+ const change = ((latest.value - previous.value) / previous.value) * 100;
+
+ // Update value display
+ const valueElement = document.getElementById(`${type}-graph-value`);
+ const changeElement = document.getElementById(`${type}-graph-change`);
+
+ if (valueElement) {
+ valueElement.textContent = this.formatCurrency(latest.value, 'EUR');
+ }
+
+ if (changeElement) {
+ const changeText = `${change >= 0 ? '+' : ''}${change.toFixed(1)}%`;
+ changeElement.textContent = changeText;
+
+ // Update change indicator classes
+ changeElement.className = 'change-indicator';
+ if (change > 0) {
+ changeElement.classList.add('positive');
+ } else if (change < 0) {
+ changeElement.classList.add('negative');
+ } else {
+ changeElement.classList.add('neutral');
+ }
+ }
+ }
}
-const app = new ETFTradeTracker();
\ No newline at end of file
+const app = new PersonalFinanceTracker();
\ No newline at end of file
diff --git a/styles.css b/styles.css
index 263de84..04ff9bf 100644
--- a/styles.css
+++ b/styles.css
@@ -942,6 +942,258 @@ body {
border-left-color: var(--info);
}
+/* Growth Graphs Section */
+.growth-graphs-section {
+ margin-top: 40px;
+ max-width: 1200px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.graphs-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 25px;
+}
+
+.graphs-header h2 {
+ margin: 0;
+ color: var(--text-primary);
+ font-size: 1.5rem;
+ font-weight: 600;
+}
+
+.time-period-selector {
+ display: flex;
+ gap: 8px;
+ background: var(--bg-secondary);
+ padding: 4px;
+ border-radius: 8px;
+ box-shadow: var(--shadow-light);
+}
+
+.period-btn {
+ background: transparent;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.period-btn:hover {
+ background: var(--bg-tertiary);
+ color: var(--text-primary);
+}
+
+.period-btn.active {
+ background: var(--accent-primary);
+ color: white;
+ box-shadow: 0 2px 4px rgba(102, 126, 234, 0.2);
+}
+
+.graphs-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
+ gap: 20px;
+}
+
+.graph-card {
+ background: var(--bg-secondary);
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: var(--shadow-light);
+ border-left: 4px solid var(--accent-primary);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.graph-card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-medium);
+}
+
+.graph-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 20px;
+}
+
+.graph-header h3 {
+ margin: 0;
+ font-size: 1rem;
+ color: var(--text-secondary);
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ font-weight: 600;
+}
+
+.graph-stats {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 2px;
+}
+
+.current-value {
+ font-size: 1.2rem;
+ font-weight: 700;
+ color: var(--text-primary);
+}
+
+.change-indicator {
+ font-size: 0.8rem;
+ font-weight: 600;
+ padding: 2px 6px;
+ border-radius: 4px;
+}
+
+.change-indicator.positive {
+ color: var(--success);
+ background: var(--success-bg);
+}
+
+.change-indicator.negative {
+ color: var(--danger);
+ background: var(--danger-bg);
+}
+
+.change-indicator.neutral {
+ color: var(--text-muted);
+ background: var(--bg-tertiary);
+}
+
+.graph-container {
+ height: 200px;
+ position: relative;
+ background: var(--bg-primary);
+ border-radius: 8px;
+ padding: 15px;
+ overflow: hidden;
+}
+
+.chart-canvas {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+/* Specific graph card styling */
+.portfolio-graph {
+ border-left-color: var(--success);
+}
+
+.cash-graph {
+ border-left-color: var(--info);
+}
+
+.combined-graph {
+ border-left-color: var(--accent-primary);
+}
+
+/* Mock chart styling for visual representation */
+.mock-chart {
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(45deg, transparent 0%, var(--accent-primary) 20%, transparent 40%);
+ opacity: 0.1;
+ border-radius: 4px;
+ position: relative;
+ overflow: hidden;
+}
+
+.mock-chart::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: var(--accent-primary);
+ opacity: 0.6;
+ transform: translateY(-50%) rotate(-10deg);
+}
+
+.mock-chart::after {
+ content: 'Chart data loading...';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ color: var(--text-muted);
+ font-size: 0.9rem;
+ font-style: italic;
+}
+
+/* Responsive design for graphs */
+@media (max-width: 768px) {
+ .graphs-header {
+ flex-direction: column;
+ gap: 15px;
+ align-items: stretch;
+ }
+
+ .graphs-header h2 {
+ text-align: center;
+ }
+
+ .time-period-selector {
+ justify-content: center;
+ }
+
+ .graphs-grid {
+ grid-template-columns: 1fr;
+ gap: 15px;
+ }
+
+ .graph-card {
+ padding: 15px;
+ }
+
+ .graph-container {
+ height: 180px;
+ padding: 10px;
+ }
+
+ .period-btn {
+ padding: 6px 12px;
+ font-size: 0.8rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .growth-graphs-section {
+ margin-top: 30px;
+ }
+
+ .graphs-header h2 {
+ font-size: 1.3rem;
+ }
+
+ .graph-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ margin-bottom: 15px;
+ }
+
+ .graph-stats {
+ align-items: flex-start;
+ }
+
+ .current-value {
+ font-size: 1.1rem;
+ }
+
+ .graph-container {
+ height: 160px;
+ }
+}
+
.dashboard-card.yearly-investment {
border-left-color: var(--accent-purple);
}