feat: Ausgaben-Anteile wahlweise als Kreisdiagramm

Neue Karten-Option „Darstellung": Liste (Standard) oder Kreis. Dieselben Zahlen,
zwei Lesarten – die Liste nennt jeden Anteil genau, der Kreis zeigt die
Verteilung auf einen Blick. Nutzt den vorhandenen Donut, dessen Legende Betrag
und Anteil ohnehin schon ausweist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-21 09:46:48 +02:00
co-authored by Claude Opus 4.8
parent e10ccdf185
commit 7eed5ee6c4
3 changed files with 29 additions and 3 deletions
+21 -1
View File
@@ -155,6 +155,16 @@ function ExpenseShare({ props }) {
const data = useData(() => api.cardExpenseShare(groupBy));
if (!data) return <Loading />;
if (!data.rows?.length) return <div className="dash-empty muted">{t("cards.noExpenses")}</div>;
// Kreis: der Donut beschriftet seine Legende selbst mit Betrag und Anteil.
if ((props?.darstellung || "liste") === "kreis") {
return (
<Donut
segmente={data.rows.map((r) => ({ name: r.name, value: r.amount, color: r.color }))}
gesamtLabel={t("cards.spent")}
fmt={(v) => formatMoney(v, data.currency)}
/>
);
}
return (
<div className="dash-list">
{data.rows.map((r) => {
@@ -192,6 +202,16 @@ const AUFSCHLUESSELUNG = {
],
};
// Liste oder Kreis – dieselben Zahlen, zwei Lesarten: die Liste nennt jeden
// Anteil genau, der Kreis zeigt die Verteilung auf einen Blick.
const DARSTELLUNG = {
key: "darstellung", labelKey: "cards.display", def: "liste",
choices: [
{ v: "liste", labelKey: "cards.asList" },
{ v: "kreis", labelKey: "cards.asPie" },
],
};
// Registry: Kartentyp -> { Titel-Schlüssel, Komponente, Standardgröße/Mindestgröße,
// optional `options` (im Anordnen-Modus einstellbare Karten-Optionen) }.
export const KARTEN = {
@@ -200,7 +220,7 @@ export const KARTEN = {
"net-worth": { titleKey: "cards.netWorth", Comp: NetWorth, w: 8, h: 4, minW: 4, minH: 3 },
"budget-progress": { titleKey: "cards.budgetProgress", Comp: BudgetProgress, w: 4, h: 3, minW: 3, minH: 2, options: [AUFSCHLUESSELUNG] },
"expense-share": { titleKey: "cards.expenseShare", Comp: ExpenseShare, w: 4, h: 5, minW: 3, minH: 3,
options: [{ ...AUFSCHLUESSELUNG, def: "category" }] },
options: [{ ...AUFSCHLUESSELUNG, def: "category" }, DARSTELLUNG] },
"income-expense": { titleKey: "cards.incomeExpense", Comp: IncomeExpense, w: 4, h: 2, minW: 2, minH: 2 },
"recent-transactions": { titleKey: "cards.recent", Comp: RecentTransactions, w: 4, h: 4, minW: 3, minH: 3 },
};
+4 -1
View File
@@ -134,7 +134,10 @@
"byGroup": "Gruppen",
"byCategory": "Kategorien",
"expenseShare": "Ausgaben-Anteile",
"noExpenses": "Noch keine Ausgaben in diesem Monat."
"noExpenses": "Noch keine Ausgaben in diesem Monat.",
"display": "Darstellung",
"asList": "Liste",
"asPie": "Kreis"
},
"server": {
"unreachable": "Server nicht erreichbar.",
+4 -1
View File
@@ -134,7 +134,10 @@
"byGroup": "Groups",
"byCategory": "Categories",
"expenseShare": "Expense share",
"noExpenses": "No expenses this month yet."
"noExpenses": "No expenses this month yet.",
"display": "Display",
"asList": "List",
"asPie": "Pie"
},
"server": {
"unreachable": "Server unreachable.",