Files
dejanandClaude Opus 5 6a0b36ddcc Geviertstriche aus Text, Kommentaren und Doku entfernen
467 Stellen. Nicht durch einen anderen Strich ersetzt, sondern durch das
Satzzeichen, das an der jeweiligen Stelle richtig ist: Doppelpunkt, wo eine
Erlaeuterung folgt; Komma, wo der Satz weiterlaeuft; Punkt, wo zwei
Hauptsaetze aneinanderstiessen. Ein Halbgeviertstrich waere nur derselbe
Griff mit kuerzerem Strich gewesen.

Regelbasiert ersetzt und danach nachgeprueft, weil eine Regel allein nicht
reicht. Gefunden und einzeln korrigiert:

- Eingeschobene Saetze mit Strichpaar. Nur der oeffnende Strich passte ins
  Muster, der schliessende blieb als "—," stehen. Drei Stellen, jetzt mit
  Klammern oder als eigener Satz.

- Doppelte Doppelpunkte, wo beide Striche eines Paars zu Doppelpunkten wurden
  ("Globale Regeln: Schrift, Groessen, Abstaende: kommen aus HiveGrid").
  Sieben Stellen in Doku und Oberflaeche, jetzt in Klammern.

- Pfade, denen ein Komma statt eines Doppelpunkts folgte.

- Ausgerichtete Kommentartabellen, in denen der Strich eine Spalte war.

Nicht angefasst: drei Geviertstriche, die keine Prosa sind, sondern
Tabellenplatzhalter fuer "kein Wert" (Trichter, Auftraege, Kunden). Dort ist
der Strich die Angabe selbst; ihn zu entfernen liesse leere Zellen zurueck.

tsc, lint, build, verify:carbon und verify:layout laufen sauber.

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

137 lines
4.1 KiB
TypeScript

"use client";
import React from "react";
import {
Area,
CartesianGrid,
ComposedChart,
Line,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { ChartTooltip } from "./ChartTooltip";
import { ChartLeer } from "./ChartLeer";
import {
CHART,
MARGIN,
MIN_TICK_ABSTAND,
PRIMAER_FARBE,
VERGLEICH_FARBE,
achseX,
achseY,
achsenIntervall,
nettesRaster,
} from "../../utils/chartTheme";
import type { ZeitreihePunkt } from "../../data/types";
/**
* Zeitverlauf als Fläche mit optionaler Vergleichslinie (Vorjahr).
*
* Bewusst `ComposedChart`, nicht `AreaChart`: Recharts sammelt in einem
* AreaChart ausschließlich `Area`-Kinder ein (generateCategoricalChart mit
* GraphicalChild = Area). Ein `<Line>` darin wird stillschweigend verworfen,
* die Vergleichsreihe stand monatelang in der Legende, ohne je gezeichnet zu
* werden. ComposedChart nimmt beide Typen.
*
* Carbon-Regeln, die hier umgesetzt sind:
* - nur horizontale Gridlines (#e0e0e0), vertikale lenken ab
* - keine Tick-Marks, Achsenbeschriftung 12 px in #525252
* - lineare Interpolation: eine Monotone-Kurve erfindet Zwischenwerte
* zwischen zwei Messpunkten, die es nicht gibt
* - Vergleichsreihe gestrichelt und neutral, damit sie die Hauptreihe
* nicht um Aufmerksamkeit bringt
*/
export function AreaVerlauf({
daten,
formatWert,
/** Kürzere Schreibweise für die Y-Achse; ohne Angabe wie formatWert. */
formatAchse,
hoehe = 260,
zeigeVergleich = false,
farbe = PRIMAER_FARBE,
labelPrefix,
}: {
daten: ZeitreihePunkt[];
formatWert: (wert: number) => string;
formatAchse?: (wert: number) => string;
hoehe?: number;
zeigeVergleich?: boolean;
farbe?: string;
labelPrefix?: string;
}) {
// Zwei Verläufe auf einer Seite dürfen sich keinen Gradienten teilen.
const gradientId = `verlauf-${React.useId().replace(/:/g, "")}`;
const hatWerte = daten.some((d) => d.value > 0 || (d.vergleich ?? 0) > 0);
if (!daten.length || !hatWerte) return <ChartLeer hoehe={hoehe} />;
// Ausdünnen der Zeitachse: siehe achsenIntervall() in chartTheme.ts.
const intervall = achsenIntervall(daten.length);
const maximum = Math.max(
...daten.map((d) => Math.max(d.value, zeigeVergleich ? (d.vergleich ?? 0) : 0))
);
const raster = nettesRaster(maximum);
return (
<div style={{ height: hoehe }}>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart data={daten} margin={MARGIN.standard}>
<defs>
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={farbe} stopOpacity={0.24} />
<stop offset="100%" stopColor={farbe} stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid stroke={CHART.grid} vertical={false} />
<XAxis
dataKey="label"
interval={intervall}
minTickGap={MIN_TICK_ABSTAND}
{...achseX}
/>
<YAxis
domain={raster.domain}
ticks={raster.ticks}
tickFormatter={formatAchse ?? formatWert}
{...achseY}
/>
<Tooltip
cursor={{ stroke: CHART.achse, strokeWidth: 1, strokeDasharray: "3 3" }}
content={(props) => (
<ChartTooltip {...props} formatWert={formatWert} labelPrefix={labelPrefix} />
)}
/>
<Area
type="linear"
dataKey="value"
name="Aktueller Zeitraum"
stroke={farbe}
strokeWidth={2}
fill={`url(#${gradientId})`}
dot={false}
activeDot={{ r: 4, fill: farbe, stroke: "#fff", strokeWidth: 2 }}
/>
{zeigeVergleich ? (
<Line
type="linear"
dataKey="vergleich"
name="Vorjahr"
stroke={VERGLEICH_FARBE}
strokeWidth={1.5}
strokeDasharray="4 4"
dot={false}
activeDot={{ r: 4, fill: VERGLEICH_FARBE, stroke: "#fff", strokeWidth: 2 }}
/>
) : null}
</ComposedChart>
</ResponsiveContainer>
</div>
);
}