feat(frontend): v3-Umbau P2 — echtes Routing, Unterzustand in der URL
Dritte Etappe. Die Ansicht lag bisher im URL-Hash (`#models`), gespeist aus einem
useState in App.tsx. Das war fuer Reload und Lesezeichen brauchbar — aber nur auf
View-Ebene; fuer den Zustand DARUNTER war kein Platz.
## Was jetzt in der Adresse steht (Befund B-05)
/modelle?reiter=routing der Reiter war lokaler State, nicht teilbar
/ideen?offen=<id> die aufgeklappte Karte; wer waehrend eines
laufenden Workers neu lud, verlor seinen Platz
/wissen?datei=<pfad> jede Vault-Notiz verlinkbar
<ueberall>?system=wartung|logs die System-Schublade inkl. Reiter
Alle Suchparameter werden validiert (app/router.tsx) — ein Link mit Unsinn darin
faellt auf den Normalfall zurueck, statt die Ansicht in einen halben Zustand zu
bringen.
## Alte Lesezeichen brechen nicht
lib/hashUmleitung.ts biegt `#models` per replaceState auf `/modelle`, EINMAL vor
dem Router-Mount. Fuenf Tests decken das ab (jeder NAV-Eintrag, die Schreibweise
`#/models`, erhaltene Suchparameter, unbekannter Hash, kein Hash) — die Bruecke
sieht sonst wie toter Code aus und waere ein naheliegender Kandidat zum Wegraeumen.
Das Backend brauchte KEINE Zeile: der SPA-Catch-all in app.py:148-168 beantwortet
jede unbekannte Route schon immer mit index.html. Vorher geprueft, nicht gehofft.
## Fehlergrenze pro Route (Befund B-13)
Vorher hing eine einzige Grenze in main.tsx um alles — ein Renderfehler im
Modell-Manager nahm Cockpit, Konsole und Chronik mit. Jetzt faengt jede Route fuer
sich (components/RouteFehler.tsx: was passiert ist, was man tun kann, technische
Einzelheiten aufklappbar). AppErrorBoundary bleibt als letztes Netz fuer Fehler
ausserhalb jeder Route. Dazu eine 404-Seite, die Schiene und Kopfzeile stehen
laesst — im Browser geprueft.
## Budget: der Router kostete 29 kB, sie sind wieder drin
TanStack Router hob den Start-Chunk von 111 auf 141 kB gzip und riss damit das in
P0 gesetzte Ampel-Budget (125 kB). Statt das Budget hochzusetzen — was es als
Signal entwertet haette — den Platz zurueckgeholt: SystemDrawer und CommandPalette
sind beim Start UNSICHTBAR und liegen jetzt hinter lazy(). Die Palette laedt beim
ersten Strg+K und bleibt dann gemountet.
Start-Chunk gzip 111 411 -> 114 978 B (Budget 125 000, weiter scharf)
dist gesamt 1 565 521 B (Budget 3 145 728)
## Nebenbei
· Befund B-09 erledigt: Der "Suchen"-Knopf baute ein GEFAELSCHTES KeyboardEvent
(`new KeyboardEvent("keydown", { metaKey: true })`) und feuerte es aufs
Dokument. Jetzt ein eigener Kanal; in P3 loest der Store auch den ab.
· Der Schliessen-Knopf der System-Schublade hatte kein aria-label — ein reiner
Icon-Knopf ohne Namen. Nachgeholt (beim Pruefen aufgefallen).
· nav.ts fuehrt jetzt den Pfad je Eintrag + navFuerPfad() mit Laengen-Sortierung,
damit /ideen/<id> die Sidebar bei "Ideen" markiert. Mit Tests.
## Verifiziert im Browser (lokales Backend, echtes Produktions-Buendel)
/ -> /cockpit, Sidebar markiert
/#models -> /modelle, Sidebar markiert
/modelle?reiter=routing -> Reiter "Routing & Warm-Set" aktiv
/cockpit?system=logs -> Schublade offen, Reiter "System-Logs" aktiv;
Schliessen entfernt den Parameter wieder
/gibtsnicht -> 404-Seite, Schiene + Kopfzeile intakt
Zurueck-Taste -> voriger Pfad
26/26 Tests gruen · ESLint 0 Fehler · tsc sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae69c5ce31
commit
464b0d20b8
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router"
|
||||
import {
|
||||
Loader2, AlertTriangle, ChevronDown, ChevronRight, Clock, Hammer, Lightbulb, Send,
|
||||
HelpCircle, RotateCcw, Square, ArrowUp, ArrowDown, Code2, FileText, GitFork, X, Layers,
|
||||
@@ -114,7 +115,14 @@ function IdeenSection() {
|
||||
const [ziel, setZiel] = useState<"lxc" | "docker">("lxc")
|
||||
const [sending, setSending] = useState(false)
|
||||
const [busy, setBusy] = useState<Record<string, boolean>>({})
|
||||
const [openLog, setOpenLog] = useState<string | null>(null)
|
||||
// v3-Umbau P2 (Befund B-05): Die aufgeklappte Karte lag in useState. Wer waehrend
|
||||
// eines laufenden Workers neu lud — und genau dann schaut man hin — verlor seinen
|
||||
// Platz in der Liste. Jetzt steht sie in der URL (/ideen?offen=<id>) und ist teilbar.
|
||||
const navigate = useNavigate()
|
||||
const suche = useSearch({ from: "/ideen" })
|
||||
const openLog = suche.offen ?? null
|
||||
const setOpenLog = (id: string | null) =>
|
||||
navigate({ to: "/ideen", search: (alt) => ({ ...alt, offen: id ?? undefined }), replace: true })
|
||||
const [openKonzept, setOpenKonzept] = useState<string | null>(null)
|
||||
const [antworten, setAntworten] = useState<Record<string, string>>({})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user