umbau(boxwart): neue Oberflaeche im Cockpit-Stil (Richtung A), Werkzeuge angehoben
Drei Seiten statt zehn: Start, Updates, Modelle — am PC mit Kopfnavigation, am Handy mit Leiste unten. Umsetzung von Mockup A („Cockpit“), vom User am 23.09. gewaehlt. - Start: Hauptwarnleuchte + 8 Warnlampen, Rundinstrumente mit Live-Werten aus dem Strom (Speicher, Temperatur, Platte) und Laufzeit-Zaehlwerk, Checkliste der Waechter-Hinweise mit ihren Knoepfen, Flugplan (heute gelaufen / geplant), Radar-Kasten. - Updates: Bausteine mit „Laeuft → Neu“ und Zusammenfassung, laufende Auftraege, Verlauf der Sonntagslaeufe (neu: GET /api/updates/verlauf), Sicherungen samt Zurueckspielen mit Rueckfrage. - Modelle: Speicherbalken, Rollen Hirn/Coder/Dritte Rolle, wer die Modelle nutzt (7 Tage + 24 h je Stunde), Modell-Radar, weitere Eintraege, Modelle selbst suchen. - Schubladen: Dienste mit Protokoll und Neustart, Einstellungen (HF-Zugang), Hermes-Link. - Werkzeuge: Vite 8, React 19.3 mit React Compiler 1.0 (Babel), vitest 5, Tailwind 4.3, shadcn 4 (Radix) fuer Dialog/Schublade/Knopf, Schriften Barlow/Barlow Condensed/ JetBrains Mono. Entfernt: recharts, cmdk, Kraftgraph, zustand, Inter, Space Grotesk. - Startbuendel 115 KB gzip (Budget 140); Updates/Modelle/Schubladen laden bei Bedarf. - 16 Oberflaechen-Tests (Instrument-Bogen, Hauptleuchte, Zeitformate, Versionen). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3d2c9549fb
commit
907289d7dc
@@ -0,0 +1,68 @@
|
||||
import { useState } from "react"
|
||||
import { useQueryClient } from "@tanstack/react-query"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet"
|
||||
import { useDienste, useProtokoll } from "@/lib/abfragen"
|
||||
import { post } from "@/lib/api"
|
||||
import { melden } from "@/lib/meldungen"
|
||||
import { cn } from "cn"
|
||||
|
||||
/** Dienste der Box mit Protokoll und Neustart — die „Log-Ansicht der Dienste“. */
|
||||
export function Dienste({ offen, onSchliessen }: { offen: boolean; onSchliessen: () => void }) {
|
||||
const qc = useQueryClient()
|
||||
const dienste = useDienste()
|
||||
const [gewaehlt, setGewaehlt] = useState<string | null>(null)
|
||||
const [bestaetigen, setBestaetigen] = useState<string | null>(null)
|
||||
const protokoll = useProtokoll(gewaehlt)
|
||||
|
||||
async function neustarten(unit: string) {
|
||||
setBestaetigen(null)
|
||||
try {
|
||||
const e = await post<{ ok: boolean; err?: string }>("/api/maintenance/restart", { service: unit })
|
||||
if (e.ok) melden("erfolg", `${unit} startet neu.`)
|
||||
else melden("fehler", e.err || `${unit} ließ sich nicht neu starten.`)
|
||||
qc.invalidateQueries({ queryKey: ["dienste"] })
|
||||
} catch (f) {
|
||||
melden("fehler", (f as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Sheet open={offen} onOpenChange={(o) => !o && onSchliessen()}>
|
||||
<SheetContent side="right" className="w-full gap-3 overflow-y-auto border-linie bg-panel sm:max-w-2xl">
|
||||
<SheetHeader>
|
||||
<SheetTitle className="schild text-lg">Dienste und Protokolle</SheetTitle>
|
||||
<SheetDescription className="text-text-2">Welcher Dienst läuft, was er zuletzt geschrieben hat.</SheetDescription>
|
||||
</SheetHeader>
|
||||
<ul className="m-0 flex list-none flex-col px-4 pb-2">
|
||||
{(dienste.data?.services ?? []).map((d) => (
|
||||
<li key={d.unit} className="flex flex-wrap items-center justify-between gap-2 border-t border-linie py-2.5">
|
||||
<span className="flex min-w-0 items-center gap-2.5">
|
||||
<span className={cn("size-2.5 shrink-0 rounded-full", d.ok ? "bg-gruen" : "bg-rot")} aria-hidden />
|
||||
<span className="min-w-0">
|
||||
<span className="block text-[15px]">{d.name}</span>
|
||||
<span className="ziffern text-xs text-text-3">{d.unit} · {d.ok ? "läuft" : "antwortet nicht"}</span>
|
||||
</span>
|
||||
</span>
|
||||
<span className="flex gap-2">
|
||||
<Button variant={gewaehlt === d.unit ? "info" : "ghost"} size="sm" onClick={() => setGewaehlt(d.unit)}>
|
||||
Protokoll
|
||||
</Button>
|
||||
{bestaetigen === d.unit ? (
|
||||
<Button variant="gefahr" size="sm" onClick={() => neustarten(d.unit)}>Wirklich neu starten?</Button>
|
||||
) : (
|
||||
<Button variant="outline" size="sm" onClick={() => setBestaetigen(d.unit)}>Neu starten</Button>
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
{gewaehlt && (
|
||||
<pre className="ziffern mx-4 mb-4 max-h-[50vh] overflow-auto rounded-lg border border-linie bg-[#0b0d0f] p-3 text-xs leading-relaxed whitespace-pre-wrap text-text-2">
|
||||
{protokoll.isFetching ? "Wird gelesen …" : protokoll.data?.text || protokoll.data?.err || "Kein Protokoll."}
|
||||
</pre>
|
||||
)}
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user