From 72f4bd066d42a28006331622bda44801abf15ad8 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Jan 2026 11:05:56 +0100 Subject: [PATCH] Enhance MoneyHistoryGraphDialog: Add axis labels for better data visualization, improve graph scaling logic, and implement money and date formatting methods. Update localization files to include "yesterday" translations in German and English, enhancing user experience and clarity in financial data representation. --- .../falukant/MoneyHistoryGraphDialog.vue | 137 ++++++++++++++++-- frontend/src/i18n/locales/de/falukant.json | 1 + frontend/src/i18n/locales/en/falukant.json | 1 + 3 files changed, 129 insertions(+), 10 deletions(-) diff --git a/frontend/src/dialogues/falukant/MoneyHistoryGraphDialog.vue b/frontend/src/dialogues/falukant/MoneyHistoryGraphDialog.vue index c3ce6ba..da805fc 100644 --- a/frontend/src/dialogues/falukant/MoneyHistoryGraphDialog.vue +++ b/frontend/src/dialogues/falukant/MoneyHistoryGraphDialog.vue @@ -32,13 +32,46 @@ {{ $t('falukant.moneyHistory.graph.noData') }}
- + + + + + {{ label.text }} + + + + + + + {{ label.text }} + + + + + + + +
@@ -59,6 +92,10 @@ export default { graphRange: '24h', graphData: [], graphLoading: false, + _minX: null, + _maxX: null, + _minY: null, + _maxY: null, } }, computed: { @@ -70,19 +107,52 @@ export default { if (d.moneyBefore != null) return Number(d.moneyBefore) return 0 }) - const minX = Math.min(...xs) - const maxX = Math.max(...xs) - const minY = Math.min(...ys) - const maxY = Math.max(...ys) - const spanX = maxX - minX || 1 - const spanY = maxY - minY || 1 + this._minX = Math.min(...xs) + this._maxX = Math.max(...xs) + this._minY = Math.min(...ys) + this._maxY = Math.max(...ys) + const spanX = this._maxX - this._minX || 1 + const spanY = this._maxY - this._minY || 1 return xs.map((x, i) => { - const normX = ((x - minX) / spanX) * 100 - const normY = 40 - ((ys[i] - minY) / spanY) * 35 - 2 // etwas Rand oben/unten + const normX = 8 + ((x - this._minX) / spanX) * 90 // 8-98 Bereich für X + const normY = 42 - ((ys[i] - this._minY) / spanY) * 38 // 2-42 Bereich für Y return `${normX.toFixed(2)},${normY.toFixed(2)}` }).join(' ') }, + yAxisLabels() { + if (!this.graphData.length || !this._minY || !this._maxY) return [] + const labels = [] + const numLabels = 5 + const span = this._maxY - this._minY || 1 + + for (let i = 0; i <= numLabels; i++) { + const value = this._minY + (span * i / numLabels) + const y = 42 - (i / numLabels) * 38 + labels.push({ + y: y + 1.5, // Zentrierung + text: this.formatMoney(value) + }) + } + return labels + }, + xAxisLabels() { + if (!this.graphData.length || !this._minX || !this._maxX) return [] + const labels = [] + const numLabels = 5 + const span = this._maxX - this._minX || 1 + + for (let i = 0; i <= numLabels; i++) { + const timestamp = this._minX + (span * i / numLabels) + const x = 8 + (i / numLabels) * 90 + const date = new Date(timestamp) + labels.push({ + x: x, + text: this.formatDate(date) + }) + } + return labels + }, }, methods: { open() { @@ -96,6 +166,11 @@ export default { range: this.graphRange, }) this.graphData = Array.isArray(response.data) ? response.data : [] + // Reset min/max für computed properties + this._minX = null + this._maxX = null + this._minY = null + this._maxY = null } catch (error) { console.error('Error loading money history graph data:', error) this.graphData = [] @@ -103,6 +178,35 @@ export default { this.graphLoading = false } }, + formatMoney(amount) { + return new Intl.NumberFormat(navigator.language, { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + style: 'currency', + currency: 'EUR' + }).format(amount) + }, + formatDate(date) { + const now = new Date() + const diffMs = now - date + const diffHours = diffMs / (1000 * 60 * 60) + + if (diffHours < 24) { + // Heute: nur Uhrzeit + return date.toLocaleTimeString(navigator.language, { hour: '2-digit', minute: '2-digit' }) + } else if (diffHours < 48) { + // Gestern: "Gestern" + Uhrzeit + return this.$t('falukant.moneyHistory.graph.yesterday') + ' ' + date.toLocaleTimeString(navigator.language, { hour: '2-digit', minute: '2-digit' }) + } else { + // Älter: Datum + Uhrzeit + return date.toLocaleString(navigator.language, { + day: '2-digit', + month: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) + } + }, }, } @@ -141,6 +245,19 @@ export default { border-radius: 4px; } +.axis-label { + font-size: 2.5px; + fill: #333; +} + +.y-label { + dominant-baseline: middle; +} + +.x-label { + dominant-baseline: hanging; +} + .graph-loading, .graph-no-data { margin: 1rem 0; diff --git a/frontend/src/i18n/locales/de/falukant.json b/frontend/src/i18n/locales/de/falukant.json index 00bceb1..7262285 100644 --- a/frontend/src/i18n/locales/de/falukant.json +++ b/frontend/src/i18n/locales/de/falukant.json @@ -587,6 +587,7 @@ "close": "Schließen", "loading": "Lade Verlauf...", "noData": "Für den gewählten Zeitraum liegen keine Buchungen vor.", + "yesterday": "Gestern", "range": { "label": "Zeitraum", "today": "Heute", diff --git a/frontend/src/i18n/locales/en/falukant.json b/frontend/src/i18n/locales/en/falukant.json index afe0335..1b3c98d 100644 --- a/frontend/src/i18n/locales/en/falukant.json +++ b/frontend/src/i18n/locales/en/falukant.json @@ -117,6 +117,7 @@ "close": "Close", "loading": "Loading history...", "noData": "No entries for the selected period.", + "yesterday": "Yesterday", "range": { "label": "Range", "today": "Today",