Files
Dashboard/features/dashboard/components/charts/Trichter.tsx
T
dejanandClaude Opus 5 e209c33916 Drei Stellen im Hover- und Ausrichtungsverhalten nachbessern
Aus dem Sparring, alle drei auch in Analytics wirksam.

1. Vertriebstrichter: Zeilen-Hervorhebung statt Ausgrauen der uebrigen Balken.

   Bisher setzte das Ueberfahren einer Zeile alle anderen Balken auf 45 %
   Deckkraft. Das war aus drei Gruenden falsch: Es liess fuenf von sechs
   Balken verblassen und las sich dadurch wie eine Auswahl statt wie ein
   Ueberfahren; es war in sich widerspruechlich, weil die Balken verblassten
   und die Namen daneben schwarz blieben; und Deckkraft traegt in diesem
   Diagramm bereits Bedeutung — der Balken kodiert einen Anteil, ihn heller zu
   machen heisst dort "weniger" und nicht "gerade nicht beruehrt".

   Jetzt bekommt die beruehrte Zeile einen Untergrund, die uebrigen bleiben
   unveraendert. Derselbe Untergrund wie in der Handlungsbedarf-Liste und in
   den Tabellen. Die Spur des aktiven Balkens wird weiss: neutral-50 und
   neutral-100 liegen nur fuenf Stufen auseinander, grau auf grau haette dem
   Balken den Bezugsrahmen genommen.

2. KPI-Karten einer Reihe auf gleiche Hoehe.

   Die Umsatzkarte war hoeher als ihre drei Nachbarn, weil sie als einzige
   eine Trendzeile mit Verlaufskurve traegt. Auf dem Dashboard steht zwischen
   Rasterzelle und Karte ein Link, und der reichte die Streckung des Rasters
   nicht durch. h-full auf der Karte.

3. Handlungsbedarf: Hover-Flaeche greift ueber den Inhalt hinaus.

   Der Untergrund endete hart am Pfeil — bei zwei Spalten besonders sichtbar,
   weil daneben 32 px Spaltenabstand ungefaerbt blieben. Jetzt acht Pixel
   Ueberstand und runde Ecken.

   Dabei mitgenommen: Der Trennstrich sass unten und brauchte eine Ausnahme
   fuer "die letzte Zeile" — die es bei zwei Spalten zweimal gibt. Die rechte
   Spalte behielt dadurch einen Strich, unter dem nichts mehr kam. Oben
   liegend ist die Ausnahme die erste Zeile jeder Spalte, und die steht immer
   an derselben Stelle, unabhaengig von der Anzahl der Eintraege.

tsc, lint, build und verify:layout laufen sauber; Hover-Zustaende und
Kartenhoehen nachgemessen statt begutachtet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
2026-08-31 17:06:25 +02:00

117 lines
4.9 KiB
TypeScript

"use client";
import React from "react";
import { KATEGORIE_FARBEN } from "../../utils/chartTheme";
import { ChartLeer } from "./ChartLeer";
/**
* Vertriebstrichter.
*
* Zeigt je Stufe die absolute Zahl, die Balkenlänge als Anteil an der ersten
* Stufe und die Übergangsquote von der vorherigen Stufe. Genau die
* Übergangsquote ist die Zahl, nach der Betriebe ihren Vertrieb steuern:
* Sie zeigt, wo Vorgänge verloren gehen.
*
* Einfarbig: Die Stufen sind eine Reihenfolge, keine unabhängigen Kategorien —
* DESIGN-SYSTEM.md §1 „Rangfolgen einfarbig". Eine Farbabstufung würde nur
* kodieren, was die Reihenfolge ohnehin sagt.
*
* Die Balken sind zentriert, nicht linksbündig: So liest man die Verjüngung als
* Trichterform, ohne die gemeinsame Skala aufzugeben. Ein echtes Funnel-Chart
* mit breitenkodierten Segmenten kennt Carbon bewusst nicht — es verzerrt.
*
* **Zum Verhalten beim Überfahren:** Die Zeile unter dem Zeiger bekommt einen
* Untergrund, die übrigen bleiben unverändert. Das war vorher andersherum
* gelöst — die nicht berührten Balken wurden auf 45 % Deckkraft gesetzt — und
* das war aus drei Gründen falsch:
*
* 1. Es hat mehr bewegt als gemeint war. Ein Zeiger, der über eine Zeile
* streift, ließ fünf von sechs Balken verblassen; das liest sich wie eine
* Auswahl, nicht wie ein Überfahren.
* 2. Es war in sich widersprüchlich. Die Balken verblassten, die Namen und
* Zahlen daneben blieben schwarz — dieselbe Zeile sagte gleichzeitig
* „unwichtig" und „unverändert".
* 3. Deckkraft trägt in diesem Diagramm bereits eine Bedeutung: Der Balken
* kodiert einen Anteil. Ihn heller zu machen, heißt in der Sprache des
* Diagramms „weniger" — und nicht „gerade nicht berührt".
*
* Der Untergrund ist derselbe wie in der Handlungsbedarf-Liste und in den
* Tabellen (`bg-neutral-50`): Überfahren sieht überall gleich aus.
*/
export function Trichter({
stufen,
formatWert,
}: {
stufen: { name: string; value: number }[];
formatWert: (wert: number) => string;
}) {
const [aktiv, setAktiv] = React.useState<number | null>(null);
if (!stufen.length || stufen[0].value === 0) return <ChartLeer hoehe={220} />;
const start = stufen[0].value;
return (
<ol className="flex flex-col gap-2">
{stufen.map((stufe, index) => {
const anteilGesamt = (stufe.value / start) * 100;
const vorher = index > 0 ? stufen[index - 1].value : null;
const uebergang = vorher && vorher > 0 ? (stufe.value / vorher) * 100 : null;
const hervorgehoben = aktiv === index;
return (
<li
key={stufe.name}
onMouseEnter={() => setAktiv(index)}
onMouseLeave={() => setAktiv(null)}
/* -mx-2 px-2: Der Untergrund greift über den Inhalt hinaus, sonst
endet er hart an der Zahl am rechten Rand. */
className={`-mx-2 grid grid-cols-[96px_minmax(0,1fr)_52px_44px] items-center gap-2 rounded-md px-2 py-0.5 transition-colors duration-150 ${
hervorgehoben ? "bg-neutral-50" : ""
}`}
>
<span
className={`truncate text-[12px] ${
hervorgehoben ? "text-text-neutral-primary" : "text-text-neutral-secondary"
}`}
>
{stufe.name}
</span>
{/* Die Spur ist im hervorgehobenen Zustand weiß statt grau: Auf dem
grauen Untergrund der Zeile wäre eine graue Spur nicht mehr von
ihm zu unterscheiden, und der Balken verlöre seinen Bezugsrahmen. */}
<span
className={`flex h-6 w-full items-center justify-center rounded-sm transition-colors duration-150 ${
hervorgehoben ? "bg-white" : "bg-neutral-100"
}`}
>
<span
className="block h-full rounded-sm transition-[width] duration-300"
style={{ width: `${Math.max(3, anteilGesamt)}%`, background: KATEGORIE_FARBEN[0] }}
/>
</span>
<span className="text-right text-[12px] font-semibold tabular-nums text-text-neutral-primary">
{formatWert(stufe.value)}
</span>
<span
className={`text-right text-[12px] tabular-nums ${
hervorgehoben ? "font-semibold text-text-neutral-primary" : "text-text-neutral-tertiary"
}`}
title={uebergang !== null ? "Übergang von der vorherigen Stufe" : "Startwert des Trichters"}
>
{uebergang !== null ? `${uebergang.toFixed(0)} %` : "—"}
</span>
</li>
);
})}
<li className="mt-2 flex items-center justify-end gap-1 border-t border-solid border-border-default pt-2 text-[11px] text-text-neutral-tertiary">
Rechte Spalte: Übergang von der vorherigen Stufe
</li>
</ol>
);
}