feat: Dashboard – Donut ohne Überlauf, Achse lesbar, Karten-Optionen
- Budget-Donut: Mittelbetrag passt sich der Länge an (kein Hineinlaufen in den Ring bei 4-stelligen Beträgen) - Vermögensverlauf: Y-Achse ohne Währungssymbol (formatAmount) + etwas mehr linker Rand → „CHF …" wird nicht mehr abgeschnitten - Karten-Optionen: im Anordnen-Modus je Karte einstellbar; Budget-Donut und Budget-Fortschritt können nach Gruppen ODER Kategorien aufschlüsseln (?group_by=), Auswahl wird im Layout (props) mitgespeichert Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -43,12 +43,19 @@ export default function Dashboard() {
|
||||
async function persist(next) {
|
||||
setItems(next);
|
||||
try {
|
||||
await api.saveDashboardLayout(next.map((it) => ({ i: it.i, type: it.type, x: it.x, y: it.y, w: it.w, h: it.h })));
|
||||
await api.saveDashboardLayout(next.map((it) => ({
|
||||
i: it.i, type: it.type, x: it.x, y: it.y, w: it.w, h: it.h, props: it.props,
|
||||
})));
|
||||
} catch (e) {
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Eine Karten-Option (z. B. Aufschlüsselung Gruppen/Kategorien) setzen + sichern.
|
||||
function setCardProp(i, key, value) {
|
||||
persist(items.map((it) => (it.i === i ? { ...it, props: { ...(it.props || {}), [key]: value } } : it)));
|
||||
}
|
||||
|
||||
function addCard(type) {
|
||||
const def = KARTEN[type];
|
||||
const maxY = items.reduce((m, it) => Math.max(m, it.y + it.h), 0);
|
||||
@@ -140,7 +147,30 @@ export default function Dashboard() {
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="dash-card-body">{def ? createElement(def.Comp) : null}</div>
|
||||
<div className="dash-card-body">
|
||||
{edit && def?.options?.length > 0 && (
|
||||
<div className="dash-config-bar">
|
||||
{def.options.map((opt) => {
|
||||
const cur = it.props?.[opt.key] ?? opt.def;
|
||||
return (
|
||||
<div key={opt.key} className="dash-config-opt">
|
||||
<span className="muted small">{t(opt.labelKey)}</span>
|
||||
<div className="seg sm">
|
||||
{opt.choices.map((ch) => (
|
||||
<button key={ch.v} type="button"
|
||||
className={`seg-btn${cur === ch.v ? " active" : ""}`}
|
||||
onClick={() => setCardProp(it.i, opt.key, ch.v)}>
|
||||
{t(ch.labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{def ? createElement(def.Comp, { key: JSON.stringify(it.props || {}), props: it.props }) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user