fix(boxwart): unbekannte /api-Pfade sind 404, Waechter nennt die echte Fehlerursache
Ampel / ampel (push) Failing after 21s

- Der SPA-Rueckfall lieferte fuer unbekannte /api-Pfade die Startseite (HTML, 200). Im
  ersten Probelauf stuerzte der Start daran ab (/api/radar gab es noch nicht). Jetzt 404;
  api() behandelt Nicht-JSON als Fehler, der Router zeigt eine deutsche Fehleranzeige.
- Waechter: Skriptmeldungen ("! ...", fehlgeschlagen) gehen vor systemd-Rahmenzeilen wie
  "Failed to start ..." - die sagen nur, dass es scheiterte, nicht warum.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-23 21:44:11 +02:00
co-authored by Claude Opus 5.5
parent bfb07a92c3
commit f3af2adec3
17 changed files with 73 additions and 28 deletions
+18
View File
@@ -0,0 +1,18 @@
import type { ErrorComponentProps } from "@tanstack/react-router"
/** Deutsche Fehleranzeige einer Ansicht statt „Something went wrong!“. */
export function Absturz({ error }: ErrorComponentProps) {
return (
<div role="alert" className="flex flex-col items-start gap-3 rounded-2xl border border-rot-rand bg-rot-grund p-6 text-rot-text">
<p className="schild text-lg text-rot">Diese Ansicht ist abgestürzt</p>
<p className="text-[15px]">{error instanceof Error ? error.message : String(error)}</p>
<button
type="button"
onClick={() => window.location.reload()}
className="schild h-11 rounded-lg border border-rot-rand px-5 text-sm text-foreground hover:bg-[#3a1512]"
>
Neu laden
</button>
</div>
)
}
+2
View File
@@ -1,4 +1,5 @@
import { createRootRoute, createRoute, createRouter, lazyRouteComponent, Navigate } from "@tanstack/react-router"
import { Absturz } from "./Absturz"
import { Shell } from "./Shell"
import { StartSeite } from "@/views/Start"
@@ -25,6 +26,7 @@ const modelle = createRoute({
export const router = createRouter({
routeTree: wurzel.addChildren([start, updates, modelle]),
defaultPreload: "intent",
defaultErrorComponent: Absturz,
})
declare module "@tanstack/react-router" {
+3
View File
@@ -34,6 +34,9 @@ export async function api<T>(pfad: string, init: RequestInit = {}): Promise<T> {
const text = await antwort.text()
const body = text ? safeJson(text) : null
if (!antwort.ok) throw new ApiFehler(antwort.status, detailAus(body, antwort.status))
// Kommt statt JSON eine HTML-Seite (alter Server, falscher Pfad), ist das ein Fehler —
// sonst rechnen die Ansichten mit einem Text, als wären es Daten.
if (typeof body === "string") throw new ApiFehler(antwort.status, "Die Box antwortet nicht mit Daten.")
return body as T
}
+1 -1
View File
@@ -61,7 +61,7 @@ function Instrumente({ box }: { box: Start["box"] }) {
const RADAR_ART = { bestanden: "gruen", durchgefallen: "rot", wartet: "cyan", neu: "cyan" } as const
function RadarKasten({ radar }: { radar: Radar | undefined }) {
if (!radar) return null
if (!radar?.kandidaten) return null
const offen = radar.kandidaten.filter((k) => ["bestanden", "wartet", "neu"].includes(k.status))
const vorn = offen.find((k) => k.status === "bestanden") ?? offen[0]
return (