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 <noreply@anthropic.com>
This commit is contained in:
@@ -462,14 +462,16 @@ function KarteAblaufRing() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function KarteKategorienRing() {
|
function KarteKategorienRing({ props: karteProps }) {
|
||||||
const { daten, fehler } = useDaten(() => api.dashboardByCategory());
|
const { daten, fehler } = useDaten(() => api.dashboardByCategory());
|
||||||
if (fehler) return <div className="alert error"><Icon name="alert" size={16} />{fehler}</div>;
|
if (fehler) return <div className="alert error"><Icon name="alert" size={16} />{fehler}</div>;
|
||||||
if (!daten) return <div className="empty">Lädt…</div>;
|
if (!daten) return <div className="empty">Lädt…</div>;
|
||||||
|
|
||||||
|
// Detailansicht: jede Kategorie einzeln statt der Bündelung zu „Andere“.
|
||||||
const segmente = mitFarben(
|
const segmente = mitFarben(
|
||||||
daten.map((k) => ({ name: k.name, value: k.article_units })),
|
daten.map((k) => ({ name: k.name, value: k.article_units })),
|
||||||
istDunkel(),
|
istDunkel(),
|
||||||
|
{ alle: !!karteProps?.detail },
|
||||||
);
|
);
|
||||||
return <Donut segmente={segmente} gesamtLabel="Einheiten" />;
|
return <Donut segmente={segmente} gesamtLabel="Einheiten" />;
|
||||||
}
|
}
|
||||||
@@ -687,6 +689,7 @@ export const KARTEN = {
|
|||||||
titel: "Kategorien-Anteile", icon: "tag", vorschau: "ring",
|
titel: "Kategorien-Anteile", icon: "tag", vorschau: "ring",
|
||||||
beschreibung: "Ring: welche Kategorie wie viel ausmacht",
|
beschreibung: "Ring: welche Kategorie wie viel ausmacht",
|
||||||
standard: { w: 4, h: 6 }, min: { w: 3, h: 5 },
|
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,
|
komponente: KarteKategorienRing,
|
||||||
},
|
},
|
||||||
"category-bars": {
|
"category-bars": {
|
||||||
|
|||||||
@@ -58,13 +58,36 @@ export function andereFarbe(dunkel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bündelt eine sortierte Liste auf SERIES_MAX Einträge plus „Andere“ und
|
* Zusätzliche Farben für die Detailansicht (mehr Kategorien als die geprüfte
|
||||||
* vergibt die Farben in fester Reihenfolge.
|
* Palette hat). Bewusst nur für den Detail-Fall: gleichmäßig verteilte Farbtöne
|
||||||
* Erwartet Objekte mit { name, value }.
|
* (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 farben = kategorieFarben(dunkel);
|
||||||
const sortiert = [...eintraege].sort((a, b) => b.value - a.value);
|
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 vorne = sortiert.slice(0, SERIES_MAX).map((e, i) => ({ ...e, color: farben[i] }));
|
||||||
const rest = sortiert.slice(SERIES_MAX);
|
const rest = sortiert.slice(SERIES_MAX);
|
||||||
if (rest.length === 0) return vorne;
|
if (rest.length === 0) return vorne;
|
||||||
|
|||||||
Reference in New Issue
Block a user