From 1cddb4432ce2c4556c77803f92af8aaed7c1f07b Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Thu, 30 Jul 2026 17:34:27 +0200 Subject: [PATCH] Web: Kategorien-Anteile-Karte umschaltbar auf Detailansicht (keine Gruppe Andere) Neue Karten-Konfig-Option Detailansicht: mitFarben(..., { alle:true }) faerbt jede Kategorie einzeln (Festpalette vorne, danach erzeugte Farbtoene) statt den Rest zu Andere zu buendeln. Umschaltbar ueber das Zahnrad der Karte. Co-Authored-By: Claude Opus 4.8 --- web/src/dashboard/cards.jsx | 5 ++++- web/src/dashboard/palette.js | 31 +++++++++++++++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/web/src/dashboard/cards.jsx b/web/src/dashboard/cards.jsx index 58ef4ba..df25077 100644 --- a/web/src/dashboard/cards.jsx +++ b/web/src/dashboard/cards.jsx @@ -462,14 +462,16 @@ function KarteAblaufRing() { ); } -function KarteKategorienRing() { +function KarteKategorienRing({ props: karteProps }) { const { daten, fehler } = useDaten(() => api.dashboardByCategory()); if (fehler) return
{fehler}
; if (!daten) return
Lädt…
; + // Detailansicht: jede Kategorie einzeln statt der Bündelung zu „Andere“. const segmente = mitFarben( daten.map((k) => ({ name: k.name, value: k.article_units })), istDunkel(), + { alle: !!karteProps?.detail }, ); return ; } @@ -687,6 +689,7 @@ export const KARTEN = { titel: "Kategorien-Anteile", icon: "tag", vorschau: "ring", beschreibung: "Ring: welche Kategorie wie viel ausmacht", standard: { w: 4, h: 6 }, min: { w: 3, h: 5 }, + config: [{ key: "detail", type: "boolean", label: "Detailansicht (alle Kategorien einzeln, keine Gruppe „Andere“)" }], komponente: KarteKategorienRing, }, "category-bars": { diff --git a/web/src/dashboard/palette.js b/web/src/dashboard/palette.js index 10c5c92..24ccff7 100644 --- a/web/src/dashboard/palette.js +++ b/web/src/dashboard/palette.js @@ -58,13 +58,36 @@ export function andereFarbe(dunkel) { } /** - * Bündelt eine sortierte Liste auf SERIES_MAX Einträge plus „Andere“ und - * vergibt die Farben in fester Reihenfolge. - * Erwartet Objekte mit { name, value }. + * Zusätzliche Farben für die Detailansicht (mehr Kategorien als die geprüfte + * Palette hat). Bewusst nur für den Detail-Fall: gleichmäßig verteilte Farbtöne + * (Goldener Winkel) statt der farbfehlsicht-geprüften Festfarben. */ -export function mitFarben(eintraege, dunkel) { +function extraFarben(anzahl, dunkel) { + const s = dunkel ? 62 : 66; + const l = dunkel ? 58 : 46; + return Array.from({ length: anzahl }, (_, j) => { + const ton = Math.round((28 + j * 137.508) % 360); + return `hsl(${ton}, ${s}%, ${l}%)`; + }); +} + +/** + * Vergibt Farben für eine sortierte Anteilsliste (Objekte mit { name, value }). + * + * Standard: höchstens SERIES_MAX Einträge in fester Reihenfolge, der Rest zu + * „Andere“ gebündelt. Mit `alle` (Detailansicht) bekommt JEDE Kategorie eine + * eigene Farbe (Festpalette vorne, danach erzeugte Töne) – ohne „Andere“. + */ +export function mitFarben(eintraege, dunkel, { alle = false } = {}) { const farben = kategorieFarben(dunkel); const sortiert = [...eintraege].sort((a, b) => b.value - a.value); + if (alle) { + const extra = extraFarben(Math.max(0, sortiert.length - farben.length), dunkel); + return sortiert.map((e, i) => ({ + ...e, + color: i < farben.length ? farben[i] : extra[i - farben.length], + })); + } const vorne = sortiert.slice(0, SERIES_MAX).map((e, i) => ({ ...e, color: farben[i] })); const rest = sortiert.slice(SERIES_MAX); if (rest.length === 0) return vorne;