ShineBridge v1.9.1 — Fix: Finanzen-Chart bei langen Abrechnungsjahren unlesbar
'Kosten je Tag' quetschte alle Tage in eine feste Breite von 600px — bei 112 Tagen wurden die Balken zu ~1px-Haarlinien. Breite skaliert jetzt mit der Tagesanzahl (min. 10px Slot/Tag), Chart scrollt bei vielen Tagen horizontal statt gestaucht zu werden. X-Achsen-Labels richten sich an der tatsächlichen Balkenbreite aus statt an einer festen Label-Anzahl. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
name: ShineBridge
|
name: ShineBridge
|
||||||
version: "1.9.0"
|
version: "1.9.1"
|
||||||
slug: shinebridge
|
slug: shinebridge
|
||||||
description: Energiemanagement für Growatt/Goodwe-Wechselrichter — PV-Überschussladen, Wallbox-EMS, Tarifvergleich, Zigbee2MQTT-Überschussgeräte
|
description: Energiemanagement für Growatt/Goodwe-Wechselrichter — PV-Überschussladen, Wallbox-EMS, Tarifvergleich, Zigbee2MQTT-Überschussgeräte
|
||||||
url: https://gitea.bitfire.work/retr0/shinebridge
|
url: https://gitea.bitfire.work/retr0/shinebridge
|
||||||
|
|||||||
@@ -797,11 +797,16 @@ async function loadFinance() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// ── SVG-Balkendiagramm ────────────────────────────────────────
|
// ── SVG-Balkendiagramm ────────────────────────────────────────
|
||||||
const W = 600, H = 180, PL = 44, PR = 12, PT = 10, PB = 28;
|
// Breite skaliert mit der Anzahl Tage (Mindestplatz pro Tag), statt bei vielen
|
||||||
|
// Tagen alles in eine feste Breite zu quetschen — sonst werden Balken bei
|
||||||
|
// langen Abrechnungsjahren (>60 Tage) zu Haarlinien und unlesbar.
|
||||||
|
const H = 180, PL = 44, PR = 12, PT = 10, PB = 28;
|
||||||
|
const n = days.length;
|
||||||
|
const MIN_SLOT = 10; // px pro Tag (Balkenpaar + Abstand), unterhalb dessen nichts mehr lesbar ist
|
||||||
|
const W = Math.max(600, PL + PR + n * MIN_SLOT);
|
||||||
const cW = W - PL - PR;
|
const cW = W - PL - PR;
|
||||||
const cH = H - PT - PB;
|
const cH = H - PT - PB;
|
||||||
const n = days.length;
|
const barW = Math.max(3, Math.floor(cW / n) - 2);
|
||||||
const barW = Math.max(2, Math.floor(cW / n) - 2);
|
|
||||||
const gap = Math.max(1, Math.floor(cW / n) - barW);
|
const gap = Math.max(1, Math.floor(cW / n) - barW);
|
||||||
|
|
||||||
const allVals = days.flatMap(d => [d.fixed_eur, d.spot_eur].filter(v => v != null));
|
const allVals = days.flatMap(d => [d.fixed_eur, d.spot_eur].filter(v => v != null));
|
||||||
@@ -816,6 +821,9 @@ async function loadFinance() {
|
|||||||
yLabels += `<text x="${PL-4}" y="${(y+4).toFixed(1)}" text-anchor="end" font-size="9" fill="#8b949e">${fEur(v)}</text>`;
|
yLabels += `<text x="${PL-4}" y="${(y+4).toFixed(1)}" text-anchor="end" font-size="9" fill="#8b949e">${fEur(v)}</text>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Label-Abstand an der tatsächlichen Balkenbreite ausrichten (~60px pro Label),
|
||||||
|
// statt pauschal 8 Labels über die ganze — jetzt variable — Breite zu verteilen.
|
||||||
|
const labelStep = Math.max(1, Math.round(60 / (barW + gap)));
|
||||||
let bars = '', xLabels = '';
|
let bars = '', xLabels = '';
|
||||||
days.forEach((d, i) => {
|
days.forEach((d, i) => {
|
||||||
const x0 = PL + i * (barW + gap);
|
const x0 = PL + i * (barW + gap);
|
||||||
@@ -825,7 +833,7 @@ async function loadFinance() {
|
|||||||
const hSpot = cH * d.spot_eur / maxVal;
|
const hSpot = cH * d.spot_eur / maxVal;
|
||||||
bars += `<rect x="${x0 + barW/2}" y="${(PT + cH - hSpot).toFixed(1)}" width="${barW/2}" height="${hSpot.toFixed(1)}" fill="${C.spot}" opacity="0.85"/>`;
|
bars += `<rect x="${x0 + barW/2}" y="${(PT + cH - hSpot).toFixed(1)}" width="${barW/2}" height="${hSpot.toFixed(1)}" fill="${C.spot}" opacity="0.85"/>`;
|
||||||
}
|
}
|
||||||
if (i % Math.max(1, Math.floor(n / 8)) === 0) {
|
if (i % labelStep === 0) {
|
||||||
const label = d.date.slice(5);
|
const label = d.date.slice(5);
|
||||||
xLabels += `<text x="${(x0 + barW/2).toFixed(1)}" y="${H - 4}" text-anchor="middle" font-size="9" fill="#8b949e">${label}</text>`;
|
xLabels += `<text x="${(x0 + barW/2).toFixed(1)}" y="${H - 4}" text-anchor="middle" font-size="9" fill="#8b949e">${label}</text>`;
|
||||||
}
|
}
|
||||||
@@ -838,10 +846,11 @@ async function loadFinance() {
|
|||||||
<div style="border-top:1px solid var(--border);padding-top:24px;margin-top:4px">
|
<div style="border-top:1px solid var(--border);padding-top:24px;margin-top:4px">
|
||||||
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin-bottom:14px">Kosten je Tag</div>
|
<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--text-dim);margin-bottom:14px">Kosten je Tag</div>
|
||||||
<div style="overflow-x:auto">
|
<div style="overflow-x:auto">
|
||||||
<svg viewBox="0 0 ${W} ${H+24}" style="width:100%;max-width:${W}px;display:block">
|
<svg viewBox="0 0 ${W} ${H+24}" width="${W}" height="${H+24}" style="width:${W}px;max-width:none;display:block">
|
||||||
${gridLines}${yLabels}${bars}${xLabels}${legend}
|
${gridLines}${yLabels}${bars}${xLabels}${legend}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
${n > 60 ? `<div style="font-size:11px;color:var(--text-dim);margin-top:6px">← Horizontal scrollbar, ${n} Tage</div>` : ''}
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
el.innerHTML = `<div style="padding:4px 0">${empfehlung}${cards}${chartSection}</div>`;
|
el.innerHTML = `<div style="padding:4px 0">${empfehlung}${cards}${chartSection}</div>`;
|
||||||
|
|||||||
Reference in New Issue
Block a user