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

125 lines
4.9 KiB
TypeScript

"use client";
import React from "react";
import Link from "next/link";
import { SidebarInside } from "@/ui/components/SidebarInside";
import { Tabs } from "@/ui/components/Tabs";
import { FeatherIcon } from "./FeatherIcon";
import { ANALYSE_SEITEN } from "./navigation";
import type { SeitenId } from "../../data/filtering";
/**
* Innere Navigation des Analytics-Bereichs.
*
* Nachbau von `SidebarIconsLayout` aus dem Zielsystem, der Leiste, die dort
* jeder Arbeitsbereich mit Geschwisterseiten benutzt (Leads, Termine, HR,
* Einstellungen; 17 Aufrufstellen). Analytics mit fünf Analyse-Seiten ist
* derselbe Fall.
*
* Verhalten wie dort: eine schmale Icon-Leiste, die bei Zeigerkontakt oder
* Tastaturfokus auf Beschriftungen aufklappt: verzögert, damit sie beim
* bloßen Überstreichen nicht aufspringt. Unterhalb `sm` tritt an ihre Stelle
* eine Reiterleiste; auch das ist aus dem Original übernommen.
*
* ────────────────────────────────────────────────────────────────────────────
* BEIM EINBAU: Dieser Nachbau wird NICHT portiert.
*
* Verbindlich ist die Verabredung: Analytics bekommt eine innere Leiste mit
* den fünf Analyse-Seiten. Gebaut wird sie in HiveGrid mit dem dortigen
* `SidebarIconsLayout`, das genau diesen Zweck hat (Leads, Termine, HR,
* Einstellungen nutzen es bereits). Diese Datei beschreibt das Ziel, sie ist
* nicht das Bauteil. Rangfolge: docs/ZIELSYSTEM.md Abschnitt 0.
* ────────────────────────────────────────────────────────────────────────────
*/
const OEFFNEN_MS = 300;
const SCHLIESSEN_MS = 350;
export function AnalyticsInnerNav({
aktiv,
children,
}: {
aktiv: SeitenId;
children: React.ReactNode;
}) {
const [zeigerDrauf, setZeigerDrauf] = React.useState(false);
const [fokusInnen, setFokusInnen] = React.useState(false);
const oeffnen = React.useRef<ReturnType<typeof setTimeout> | null>(null);
const schliessen = React.useRef<ReturnType<typeof setTimeout> | null>(null);
React.useEffect(
() => () => {
if (oeffnen.current) clearTimeout(oeffnen.current);
if (schliessen.current) clearTimeout(schliessen.current);
},
[]
);
const ausgeklappt = zeigerDrauf || fokusInnen;
return (
<div className="sm:flex sm:flex-row">
<div
className="relative hidden sm:sticky sm:top-14 sm:flex sm:self-start"
onMouseEnter={() => {
if (schliessen.current) clearTimeout(schliessen.current);
oeffnen.current = setTimeout(() => setZeigerDrauf(true), OEFFNEN_MS);
}}
onMouseLeave={() => {
if (oeffnen.current) clearTimeout(oeffnen.current);
schliessen.current = setTimeout(() => setZeigerDrauf(false), SCHLIESSEN_MS);
}}
onFocusCapture={() => setFokusInnen(true)}
onBlurCapture={(event) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
setFokusInnen(false);
}
}}
>
<SidebarInside
visible
variant={ausgeklappt ? "open" : "closed"}
className="min-h-[calc(100svh-3.5rem)]"
>
{ANALYSE_SEITEN.map((seite) => (
<Link
key={seite.id}
href={seite.href}
title={seite.label}
aria-current={seite.id === aktiv ? "page" : undefined}
className="flex w-full justify-start no-underline"
>
<SidebarInside.NavItem
icon={<FeatherIcon name={seite.icon} />}
selected={seite.id === aktiv}
variant={ausgeklappt ? "icon-and-text" : "icon-only"}
>
{seite.label}
</SidebarInside.NavItem>
</Link>
))}
</SidebarInside>
</div>
{/* Schmale Fenster: Reiter statt Leiste, wie im Original.
Waagerecht scrollend statt umbrechend: Fünf Reiter brauchen mindestens
425 px, bei 375 px lief die ganze Seite dadurch seitlich über. Über
zwei Zeilen gebrochen liest sich ein Reiterband als zwei Steuerelemente
dasselbe Mittel wie beim Zeitraum-Segment in der Filterleiste. */}
<div className="dv-scroll-x w-full min-w-0 overflow-x-auto border-b border-solid border-border-default bg-white px-4 py-2 sm:hidden">
<Tabs className="w-max min-w-full">
{ANALYSE_SEITEN.map((seite) => (
<Link key={seite.id} href={seite.href} className="contents no-underline">
<Tabs.Item active={seite.id === aktiv} className="whitespace-nowrap">
{seite.label}
</Tabs.Item>
</Link>
))}
</Tabs>
</div>
<div className="w-full min-w-0 overflow-x-clip">{children}</div>
</div>
);
}