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

133 lines
4.3 KiB
TypeScript

import type { ZeitraumPreset } from "../data/types";
/**
* Fester "Heute"-Bezugspunkt des Prototyps.
*
* Wichtig: Die Dummy-Daten sind deterministisch relativ zu diesem Datum erzeugt.
* Dadurch bleiben alle Zahlen ueber Reloads und Screenshots hinweg identisch.
* In der echten Software wird das durch `new Date()` ersetzt.
*/
export const HEUTE = new Date("2026-07-30T12:00:00");
export interface Zeitfenster {
von: Date;
bis: Date;
}
export function addTage(datum: Date, tage: number): Date {
const d = new Date(datum);
d.setDate(d.getDate() + tage);
return d;
}
export function addMonate(datum: Date, monate: number): Date {
const d = new Date(datum);
d.setMonth(d.getMonth() + monate);
return d;
}
export function toISO(datum: Date): string {
return datum.toISOString().slice(0, 10);
}
/** Zeitfenster eines Presets, bezogen auf HEUTE. */
export function zeitfenster(preset: ZeitraumPreset): Zeitfenster {
const bis = new Date(HEUTE);
bis.setHours(23, 59, 59, 999);
switch (preset) {
case "heute": {
const von = new Date(HEUTE);
von.setHours(0, 0, 0, 0);
return { von, bis };
}
case "7_tage":
return { von: addTage(bis, -7), bis };
case "30_tage":
return { von: addTage(bis, -30), bis };
case "quartal":
return { von: addMonate(bis, -3), bis };
case "jahr":
return { von: addMonate(bis, -12), bis };
case "alles":
default:
return { von: new Date("2000-01-01"), bis };
}
}
/** Gleich langes Fenster direkt davor: Basis fuer Trend-Vergleiche. */
export function vorperiode(preset: ZeitraumPreset): Zeitfenster {
const { von, bis } = zeitfenster(preset);
const dauer = bis.getTime() - von.getTime();
return {
von: new Date(von.getTime() - dauer),
bis: new Date(von.getTime() - 1),
};
}
export function imFenster(iso: string | null, fenster: Zeitfenster): boolean {
if (!iso) return false;
const t = new Date(iso).getTime();
return t >= fenster.von.getTime() && t <= fenster.bis.getTime();
}
/** ISO-Kalenderwoche als sortierbarer Schluessel: "2026-W31". */
export function isoWoche(datum: Date): string {
const d = new Date(Date.UTC(datum.getFullYear(), datum.getMonth(), datum.getDate()));
// Donnerstag derselben Woche bestimmt nach ISO 8601 Jahr und Wochennummer.
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
const jahresstart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const woche = Math.ceil(((d.getTime() - jahresstart.getTime()) / 86_400_000 + 1) / 7);
return `${d.getUTCFullYear()}-W${String(woche).padStart(2, "0")}`;
}
/** "2026-W31" -> "KW 31" */
export function formatWoche(schluessel: string): string {
return `KW ${schluessel.slice(6)}`;
}
/**
* Passende Buckets fuer eine Zeitreihe.
*
* Die Aufloesung folgt der Frage, nicht dem Kalender: Bei rund zwei Vorgaengen
* pro Tag ist eine Tagesreihe ueber 30 Tage Rauschen, kein Verlauf. Deshalb
* Tage nur bis eine Woche, danach Kalenderwochen, ab einem Jahr Monate.
*/
export function bucketsFuer(preset: ZeitraumPreset): {
art: "tag" | "woche" | "monat";
schluessel: string[];
} {
const { von, bis } = zeitfenster(preset);
if (preset === "heute" || preset === "7_tage") {
const schluessel: string[] = [];
for (let d = new Date(von); d <= bis; d = addTage(d, 1)) schluessel.push(toISO(d));
return { art: "tag", schluessel };
}
if (preset === "30_tage" || preset === "quartal") {
const schluessel: string[] = [];
for (let d = new Date(von); d <= bis; d = addTage(d, 7)) {
const key = isoWoche(d);
if (!schluessel.includes(key)) schluessel.push(key);
}
const letzte = isoWoche(bis);
if (!schluessel.includes(letzte)) schluessel.push(letzte);
return { art: "woche", schluessel };
}
const monate: string[] = [];
const start = preset === "alles" ? addMonate(bis, -23) : von;
for (let d = new Date(start.getFullYear(), start.getMonth(), 1); d <= bis; d = addMonate(d, 1)) {
monate.push(`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`);
}
return { art: "monat", schluessel: monate };
}
/** Bucket-Schluessel eines Datums fuer die gewaehlte Aufloesung. */
export function bucketKey(iso: string, art: "tag" | "woche" | "monat"): string {
if (art === "monat") return iso.slice(0, 7);
if (art === "woche") return isoWoche(new Date(iso));
return iso.slice(0, 10);
}