homelab: eigener Live-Strom (keine Getrennt-Anzeige mehr), Hinweise des Homelab-Waechters auf der Seite, Verbindungsanzeige je Instanz
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
83c33584e4
commit
160381d8b3
@@ -2,6 +2,7 @@ import { lazy, Suspense, useEffect, useState } from "react"
|
||||
import { Link, Outlet } from "@tanstack/react-router"
|
||||
import { Cpu, Ellipsis, ExternalLink, House, RefreshCw, ScrollText, Server, Settings } from "lucide-react"
|
||||
import { cn } from "cn"
|
||||
import { useInstanz } from "@/lib/abfragen"
|
||||
import { useStrom, useStromLage } from "@/lib/strom"
|
||||
import { Meldungen } from "./Meldungen"
|
||||
|
||||
@@ -40,6 +41,8 @@ function Uhr() {
|
||||
function Verbindung() {
|
||||
const lage = useStromLage()
|
||||
const live = lage === "live"
|
||||
// Die Anzeige gilt der Instanz, die die Seite ausliefert: der KI-Box oder dem Homelab-Teil.
|
||||
const wer = useInstanz().data?.rolle === "homelab" ? "Homelab" : "Box"
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
@@ -52,7 +55,7 @@ function Verbindung() {
|
||||
aria-hidden
|
||||
className={cn("size-2 shrink-0 rounded-full", live ? "bg-gruen shadow-[0_0_8px_rgba(61,220,138,0.7)]" : "bg-bernstein")}
|
||||
/>
|
||||
<span className="whitespace-nowrap">{live ? "Box online" : lage === "verbinde" ? "Verbinde" : "Getrennt"}</span>
|
||||
<span className="whitespace-nowrap">{live ? `${wer} online` : lage === "verbinde" ? "Verbinde" : "Getrennt"}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import { fehlerText, laeuftNoch } from "./anzeige"
|
||||
import { melden } from "./meldungen"
|
||||
import { stromVerbunden } from "./strom"
|
||||
import type {
|
||||
AktionsErgebnis, AufraeumKandidat, HomelabLauf, HomelabStand, Instanz, Job, Modell, Nutzung, PartnerStatus, Radar,
|
||||
AktionsErgebnis, AufraeumKandidat, Hinweis, HomelabLauf, HomelabStand, Instanz, Job, Modell, Nutzung, PartnerStatus,
|
||||
Radar,
|
||||
Sicherung, Start, UpdateDetails, ZielAktion, ZielStand,
|
||||
} from "./typen"
|
||||
import { gbText } from "./zeit"
|
||||
@@ -291,3 +292,12 @@ export const useZielAktion = () =>
|
||||
erfolg: (a) => `${a.label}: gestartet. Das Ergebnis kommt als Meldung.`,
|
||||
neuLaden: ["homelab", "homelab-laeufe", "ziele-box", "jobs", "start"],
|
||||
})
|
||||
|
||||
/** Was der Wächter des Homelab-Teils gerade sieht (Ausführer schweigt, ein Gast antwortet nicht …). */
|
||||
export const useHomelabHinweise = (aktiv: boolean) =>
|
||||
useQuery({
|
||||
queryKey: ["homelab-hinweise"],
|
||||
queryFn: () => api<{ hinweise: Hinweis[]; aktiv: boolean }>("/api/homelab/hinweise"),
|
||||
enabled: aktiv,
|
||||
refetchInterval: takt(60_000),
|
||||
})
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Chip, type ChipArt, Zustandsfeld } from "@/components/cockpit/Chip"
|
||||
import { Panel } from "@/components/cockpit/Panel"
|
||||
import { ZielKarte } from "@/components/cockpit/ZielKarte"
|
||||
import { useBoxZiele, useHomelab, useHomelabLaeufe, useInstanz, usePartner, useZielAktion } from "@/lib/abfragen"
|
||||
import type { HomelabLauf, PartnerStatus, ZielStand } from "@/lib/typen"
|
||||
import {
|
||||
useBoxZiele, useHomelab, useHomelabHinweise, useHomelabLaeufe, useInstanz, usePartner, useZielAktion,
|
||||
} from "@/lib/abfragen"
|
||||
import type { Hinweis, HomelabLauf, PartnerStatus, ZielStand } from "@/lib/typen"
|
||||
import { flugplanZeit } from "@/lib/zeit"
|
||||
|
||||
/** Was der Homelab-Teil übernimmt (Bestandsaufnahme vom 24.09.2026). */
|
||||
@@ -60,6 +62,25 @@ function HomelabFehlt({ partner }: { partner: PartnerStatus }) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Hinweise des Homelab-Wächters (die der KI-Box stehen auf dem Start). Rot zuerst. */
|
||||
function HomelabHinweise({ hinweise }: { hinweise: Hinweis[] }) {
|
||||
if (hinweise.length === 0) return null
|
||||
return (
|
||||
<Panel titel="Hinweise im Homelab" rechts={<Chip art={hinweise.some((h) => h.stufe === "rot") ? "rot" : "bernstein"}>{hinweise.length}</Chip>}>
|
||||
<ul className="flex flex-col gap-3">
|
||||
{hinweise.map((h) => (
|
||||
<li key={h.id} className="flex flex-col gap-1 border-t border-linie pt-3 first:border-t-0 first:pt-0">
|
||||
<span className={h.stufe === "rot" ? "text-[15px] font-medium text-rot-text" : "text-[15px] font-medium text-bernstein"}>
|
||||
{h.titel}
|
||||
</span>
|
||||
<span className="text-sm text-text-2">{h.text}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Panel>
|
||||
)
|
||||
}
|
||||
|
||||
function LetzteLaeufe({ laeufe }: { laeufe: HomelabLauf[] }) {
|
||||
const fertig = laeufe.filter((l) => l.status !== "laeuft").slice(0, 6)
|
||||
if (fertig.length === 0) return null
|
||||
@@ -94,6 +115,7 @@ export function HomelabSeite() {
|
||||
const aktiv = homelabHier || !!(partner.data?.eingerichtet && partner.data.erreichbar)
|
||||
const homelab = useHomelab(aktiv)
|
||||
const laeufe = useHomelabLaeufe(aktiv)
|
||||
const hinweise = useHomelabHinweise(aktiv)
|
||||
const aktion = useZielAktion()
|
||||
|
||||
if (instanz.isPending || partner.isPending) return <Zustandsfeld text="Wird gelesen …" />
|
||||
@@ -131,6 +153,8 @@ export function HomelabSeite() {
|
||||
{homelab.isError && <p role="alert" className="text-sm text-rot-text">Der Homelab-Teil antwortet gerade nicht: {homelab.error.message}</p>}
|
||||
</Panel>
|
||||
|
||||
{aktiv && <HomelabHinweise hinweise={hinweise.data?.hinweise ?? []} />}
|
||||
|
||||
{ziele.length > 0 && (
|
||||
<div className="grid gap-5 md:gap-6 xl:grid-cols-2">
|
||||
{ziele.map((z) => (
|
||||
|
||||
Reference in New Issue
Block a user