- Gemeinsamer Lagekopf für beide Cockpits (Statusblock mit Schein, Kacheln mit Logo/Symbol und Zustandspunkt); KI-Box-Lampen als Dienst-Kacheln, Texte in normaler Schreibung. Alte Lampen-Komponente und Leuchtenfarben entfernt. - Instrumente, Zählwerk, Checkliste, Flugplan, Radar-Kasten im neuen Stil; Instrumente ruhig in Indigo, Farbe für Zustände erst ab der Grenze. - Übrige Seiten (Updates, Modelle, Nutzung, Suche, Monitoring, Dienste, Einstellungen, Protokoll, Dialoge): Glasflächen, feine Linien, Etiketten statt Instrumenten-Schild, Glas-Eingabefelder, deckende Dialoge. - Dienste der KI-Box und Geräte im Monitoring mit Symbol bzw. Logo. - Einstellungen: Karten mit Bereichs-Schein; Lizenzhinweis der Logos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
4.3 KiB
TypeScript
98 lines
4.3 KiB
TypeScript
import type { ReactNode } from "react"
|
|
import { cn } from "cn"
|
|
import { Geraetesymbol } from "@/components/cockpit/Geraetesymbol"
|
|
import type { Geraetebild, Punktzustand } from "@/lib/geraetebild"
|
|
import type { LampenZustand } from "@/lib/typen"
|
|
|
|
// Der Kopf beider Cockpits (Design v5, 25.09.2026): links der Statusblock mit Schein in der Farbe der Lage — drücken
|
|
// führt dorthin, wo etwas zu tun ist —, rechts die Kacheln der Geräte bzw. Dienste. Zustand immer auch in Worten.
|
|
|
|
export type Lageart = "ok" | "info" | "gelb" | "rot" | "stumm"
|
|
|
|
const ART: Record<Lageart, { block: string; glut: string; text: string }> = {
|
|
ok: { block: "bg-gruen/[0.07] ring-gruen/30", glut: "var(--gruen)", text: "text-gruen" },
|
|
info: { block: "bg-cyan/[0.07] ring-cyan/30", glut: "var(--cyan)", text: "text-cyan-text" },
|
|
gelb: { block: "bg-bernstein/[0.08] ring-bernstein/35", glut: "var(--bernstein)", text: "text-bernstein" },
|
|
rot: { block: "bg-rot/10 ring-rot/40", glut: "var(--rot)", text: "text-rot-text" },
|
|
stumm: { block: "bg-white/[0.04] ring-white/15", glut: "var(--text-3)", text: "text-text-2" },
|
|
}
|
|
|
|
export function Lagekopf({ art, oben, mitte, unten, aktiv, onAnsehen, label, children }: {
|
|
art: Lageart
|
|
oben: string
|
|
mitte: string
|
|
unten: string
|
|
aktiv: boolean
|
|
onAnsehen: () => void
|
|
/** Name des Abschnitts für Screenreader („Lage im Homelab“). */
|
|
label: string
|
|
/** Die Kacheln rechts (eine Liste von <li>). */
|
|
children: ReactNode
|
|
}) {
|
|
const a = ART[art]
|
|
return (
|
|
<section aria-label={label} className="auftauchen flaeche relative overflow-hidden rounded-3xl">
|
|
<div aria-hidden className="glut-atmet pointer-events-none absolute -top-40 -left-32 size-[460px] rounded-full"
|
|
style={{ background: `radial-gradient(closest-side, color-mix(in srgb, ${a.glut} 20%, transparent), transparent)` }} />
|
|
<div className="relative grid gap-4 p-4 sm:p-5 lg:grid-cols-[280px_minmax(0,1fr)] lg:gap-5">
|
|
<button
|
|
type="button"
|
|
onClick={onAnsehen}
|
|
disabled={!aktiv}
|
|
aria-label={`${oben}: ${mitte}`}
|
|
className={cn(
|
|
"flex min-h-[132px] flex-col items-start justify-center gap-1.5 rounded-2xl px-5 py-4 text-left ring-1 ring-inset transition-[filter]",
|
|
a.block, aktiv ? "cursor-pointer hover:brightness-125" : "cursor-default",
|
|
art === "gelb" && "lampe-atmet",
|
|
)}
|
|
>
|
|
<span className="etikett text-[11px] tracking-[0.18em] text-text-3">{oben}</span>
|
|
<span className={cn("text-[30px] leading-tight font-semibold tracking-[-0.01em] sm:text-[34px]", a.text)}>{mitte}</span>
|
|
<span className="text-[13.5px] text-text-2">{unten}</span>
|
|
</button>
|
|
<ul className="m-0 grid list-none grid-cols-1 gap-2 p-0 min-[480px]:grid-cols-2 xl:grid-cols-4">{children}</ul>
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|
|
|
|
const LAMPE_PUNKT: Record<LampenZustand, Punktzustand> = { ok: "ok", aus: "aus", warn: "warn", info: "info", fehler: "rot" }
|
|
const WERT: Record<LampenZustand, string> = {
|
|
ok: "text-text-3", aus: "text-text-3", warn: "text-bernstein", info: "text-cyan-text", fehler: "text-rot-text",
|
|
}
|
|
const ZUSTAND_TEXT: Record<LampenZustand, string> = {
|
|
ok: "in Ordnung", aus: "aus", warn: "braucht Aufmerksamkeit", info: "Info", fehler: "Störung",
|
|
}
|
|
|
|
/** Eine Kachel im Lagekopf: Bild mit Zustandspunkt, Name, darunter der Wert in der Farbe des Zustands. */
|
|
export function Lagekachel({ bild, name, wert, zustand, onClick }: {
|
|
bild: Geraetebild
|
|
name: string
|
|
wert: string
|
|
zustand: LampenZustand
|
|
onClick?: () => void
|
|
}) {
|
|
const inhalt = (
|
|
<>
|
|
<Geraetesymbol bild={bild} punkt={LAMPE_PUNKT[zustand]} />
|
|
<span className="flex min-w-0 flex-col gap-0.5">
|
|
<span className="truncate text-[14.5px] leading-snug font-medium">{name}</span>
|
|
<span className={cn("truncate text-[12.5px] leading-snug", WERT[zustand])}>{wert}</span>
|
|
<span className="sr-only">{ZUSTAND_TEXT[zustand]}</span>
|
|
</span>
|
|
</>
|
|
)
|
|
const stil = "flaeche-innen flex h-full w-full min-w-0 items-center gap-3 rounded-xl px-3 py-2.5 text-left"
|
|
return (
|
|
<li>
|
|
{onClick ? (
|
|
<button type="button" onClick={onClick} className={cn(stil, "transition-colors hover:border-white/20 hover:bg-white/[0.05]")}>
|
|
{inhalt}
|
|
</button>
|
|
) : (
|
|
<div className={stil}>{inhalt}</div>
|
|
)}
|
|
</li>
|
|
)
|
|
}
|