diff --git a/BACKLOG.md b/BACKLOG.md index 6133be4..1ca7fca 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -23,7 +23,7 @@ Das Datenmodell speichert `recurrence_rule` (iCal RRULE) für Kalender-Events. D ### BL-02 — Budget: Monatsvergleich (aktuell vs. Vormonat) -**Status:** Offen +**Status:** Erledigt (v0.3.0) **Aufwand:** S (1–2 Tage) SPEC: „Monatsvergleich (aktuell vs. Vormonat)". Derzeit zeigt die Budget-Seite nur den aktuellen Monat. Es fehlen API-Endpunkt und UI-Komponente für den Vergleich. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e2bf7e..ce8ed2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Budget: month-over-month comparison in summary cards — each card (Einnahmen, Ausgaben, Saldo) shows a trend line (▲/▼ + delta amount vs. previous month); previous month summary is fetched in parallel with current month + ## [0.2.1] - 2026-03-30 ### Fixed diff --git a/public/pages/budget.js b/public/pages/budget.js index 86af7ae..0d677ee 100644 --- a/public/pages/budget.js +++ b/public/pages/budget.js @@ -26,9 +26,10 @@ const MONTH_NAMES = ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', // -------------------------------------------------------- let state = { - month: '', // YYYY-MM - entries: [], - summary: null, + month: '', // YYYY-MM + entries: [], + summary: null, + prevSummary: null, // Vormonat für Monatsvergleich }; let _container = null; @@ -56,19 +57,23 @@ function addMonths(ym, n) { // -------------------------------------------------------- async function loadMonth(month) { + const prevMonth = addMonths(month, -1); try { - const [entriesRes, summaryRes] = await Promise.all([ + const [entriesRes, summaryRes, prevSummaryRes] = await Promise.all([ api.get(`/budget?month=${month}`), api.get(`/budget/summary?month=${month}`), + api.get(`/budget/summary?month=${prevMonth}`), ]); - state.month = month; - state.entries = entriesRes.data; - state.summary = summaryRes.data; + state.month = month; + state.entries = entriesRes.data; + state.summary = summaryRes.data; + state.prevSummary = prevSummaryRes.data; } catch (err) { console.error('[Budget] loadMonth Fehler:', err); - state.month = month; - state.entries = []; - state.summary = { income: 0, expenses: 0, balance: 0, by_category: [] }; + state.month = month; + state.entries = []; + state.summary = { income: 0, expenses: 0, balance: 0, byCategory: [] }; + state.prevSummary = null; window.oikos?.showToast('Budget konnte nicht geladen werden.', 'danger'); } } @@ -157,8 +162,10 @@ function renderBody() { if (!body) return; updateLabel(); - const s = state.summary; + const s = state.summary; + const p = state.prevSummary; const balanceClass = s.balance >= 0 ? 'budget-summary-card--balance-positive' : 'budget-summary-card--balance-negative'; + const prevLabel = p ? formatMonthLabel(p.month).split(' ')[0].slice(0, 3) : ''; body.innerHTML = ` @@ -166,14 +173,17 @@ function renderBody() {