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.

This commit is contained in:
Torsten Schulz (local)
2026-01-29 11:05:56 +01:00
parent b3db65d1b8
commit 72f4bd066d
3 changed files with 129 additions and 10 deletions

View File

@@ -32,13 +32,46 @@
{{ $t('falukant.moneyHistory.graph.noData') }} {{ $t('falukant.moneyHistory.graph.noData') }}
</div> </div>
<div v-else class="graph-container"> <div v-else class="graph-container">
<svg viewBox="0 0 100 40" preserveAspectRatio="none" class="graph-svg"> <svg viewBox="0 0 100 50" preserveAspectRatio="none" class="graph-svg">
<!-- Y-Achse Beschriftungen (Geldbeträge) -->
<g class="y-axis-labels">
<text
v-for="(label, index) in yAxisLabels"
:key="'y-' + index"
:x="2"
:y="label.y"
class="axis-label y-label"
text-anchor="start"
>
{{ label.text }}
</text>
</g>
<!-- X-Achse Beschriftungen (Zeit) -->
<g class="x-axis-labels">
<text
v-for="(label, index) in xAxisLabels"
:key="'x-' + index"
:x="label.x"
:y="47"
class="axis-label x-label"
text-anchor="middle"
>
{{ label.text }}
</text>
</g>
<!-- Graph-Linie -->
<polyline <polyline
:points="graphPolylinePoints" :points="graphPolylinePoints"
fill="none" fill="none"
stroke="#F9A22C" stroke="#F9A22C"
stroke-width="0.7" stroke-width="0.3"
/> />
<!-- Achsenlinien -->
<line x1="8" y1="2" x2="8" y2="42" stroke="#666" stroke-width="0.2" />
<line x1="8" y1="42" x2="98" y2="42" stroke="#666" stroke-width="0.2" />
</svg> </svg>
</div> </div>
</div> </div>
@@ -59,6 +92,10 @@ export default {
graphRange: '24h', graphRange: '24h',
graphData: [], graphData: [],
graphLoading: false, graphLoading: false,
_minX: null,
_maxX: null,
_minY: null,
_maxY: null,
} }
}, },
computed: { computed: {
@@ -70,19 +107,52 @@ export default {
if (d.moneyBefore != null) return Number(d.moneyBefore) if (d.moneyBefore != null) return Number(d.moneyBefore)
return 0 return 0
}) })
const minX = Math.min(...xs) this._minX = Math.min(...xs)
const maxX = Math.max(...xs) this._maxX = Math.max(...xs)
const minY = Math.min(...ys) this._minY = Math.min(...ys)
const maxY = Math.max(...ys) this._maxY = Math.max(...ys)
const spanX = maxX - minX || 1 const spanX = this._maxX - this._minX || 1
const spanY = maxY - minY || 1 const spanY = this._maxY - this._minY || 1
return xs.map((x, i) => { return xs.map((x, i) => {
const normX = ((x - minX) / spanX) * 100 const normX = 8 + ((x - this._minX) / spanX) * 90 // 8-98 Bereich für X
const normY = 40 - ((ys[i] - minY) / spanY) * 35 - 2 // etwas Rand oben/unten const normY = 42 - ((ys[i] - this._minY) / spanY) * 38 // 2-42 Bereich für Y
return `${normX.toFixed(2)},${normY.toFixed(2)}` return `${normX.toFixed(2)},${normY.toFixed(2)}`
}).join(' ') }).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: { methods: {
open() { open() {
@@ -96,6 +166,11 @@ export default {
range: this.graphRange, range: this.graphRange,
}) })
this.graphData = Array.isArray(response.data) ? response.data : [] 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) { } catch (error) {
console.error('Error loading money history graph data:', error) console.error('Error loading money history graph data:', error)
this.graphData = [] this.graphData = []
@@ -103,6 +178,35 @@ export default {
this.graphLoading = false 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'
})
}
},
}, },
} }
</script> </script>
@@ -141,6 +245,19 @@ export default {
border-radius: 4px; border-radius: 4px;
} }
.axis-label {
font-size: 2.5px;
fill: #333;
}
.y-label {
dominant-baseline: middle;
}
.x-label {
dominant-baseline: hanging;
}
.graph-loading, .graph-loading,
.graph-no-data { .graph-no-data {
margin: 1rem 0; margin: 1rem 0;

View File

@@ -587,6 +587,7 @@
"close": "Schließen", "close": "Schließen",
"loading": "Lade Verlauf...", "loading": "Lade Verlauf...",
"noData": "Für den gewählten Zeitraum liegen keine Buchungen vor.", "noData": "Für den gewählten Zeitraum liegen keine Buchungen vor.",
"yesterday": "Gestern",
"range": { "range": {
"label": "Zeitraum", "label": "Zeitraum",
"today": "Heute", "today": "Heute",

View File

@@ -117,6 +117,7 @@
"close": "Close", "close": "Close",
"loading": "Loading history...", "loading": "Loading history...",
"noData": "No entries for the selected period.", "noData": "No entries for the selected period.",
"yesterday": "Yesterday",
"range": { "range": {
"label": "Range", "label": "Range",
"today": "Today", "today": "Today",