UI: Auftragsbuch, Chronik+Zeitmaschine, Wissens-Wiki, Morgenlage, Erinnerungen

Neue Views: Auftragsbuch (Karten mit Diff/Kritiker-Kontext, Annehmen/Ablehnen mit
Fangnetz-Dialog), Chronik (Tages-Timeline aus dem Briefkasten + Zeitmaschine mit
Ein-Klick-Restore), Wissen (Vault als klickbares Wiki, [[Links]] springen).
Cockpit: Auftragsbuch-Kachel + Braucht-dich-Eintrag, Morgenlage-Karte (juengstes
Chef-Verdikt), Anstehendes (Erinnerungen anlegen/loeschen), Wissen/Chronik-Kacheln.
Gedaechtnis: Dubletten-Dialog zeigt jetzt konkret, was bleibt und was wegfaellt.
dist mitgebaut (tsc+vite gruen).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-08 18:42:22 +02:00
parent 4dc5ca7343
commit 1985a692bf
18 changed files with 1919 additions and 706 deletions
+58 -5
View File
@@ -2,17 +2,20 @@ import { useState } from "react"
import {
Boxes, Brain, Server, ShieldAlert, Plug, Bot, TerminalSquare, ScrollText,
HeartPulse, AlertTriangle, Loader2, Wrench, Check, HardDrive, ChevronRight,
Inbox, History, Library,
} from "lucide-react"
import { api } from "@/lib/api"
import {
useModels, useServices, useUpdates, useMemory, useAgentStatus, useConnectHealth,
useQueryClient, qk,
useAuftragsbuch, useQueryClient, qk,
} from "@/lib/queries"
import { SquareTerminal } from "lucide-react"
import { useLucyHealth, type Repair } from "@/lib/useLucyHealth"
import { useDialog } from "@/lib/useDialog"
import { cn } from "@/lib/utils"
import { CockpitTile } from "./CockpitTile"
import { MorgenlageCard } from "./MorgenlageCard"
import { ErinnerungenCard } from "./ErinnerungenCard"
import { SystemStatusCard } from "@/components/dashboard/SystemStatusCard"
import { TokenPerformanceCard } from "@/components/dashboard/TokenPerformanceCard"
import { LatencyCard } from "@/components/dashboard/LatencyCard"
@@ -31,6 +34,7 @@ export function CockpitView({ onNavigate }: { onNavigate: (v: string) => void })
const { data: memory } = useMemory()
const { data: agent } = useAgentStatus()
const { data: connect } = useConnectHealth()
const { data: auftraege } = useAuftragsbuch(20_000)
const health = useLucyHealth()
const { showAlert, dialogElement } = useDialog()
@@ -69,6 +73,7 @@ export function CockpitView({ onNavigate }: { onNavigate: (v: string) => void })
(updates ? (updates.os > 0 ? 1 : 0) + (updates.engine > 0 ? 1 : 0) + (updates.swap > 0 ? 1 : 0) + (updates.models > 0 ? 1 : 0) : 0) +
(updates?.components?.filter((c) => c.update === true).length ?? 0)
const connectOk = connect?.gateway.ok && connect?.memory.ok
const openAuftraege = auftraege?.available ? auftraege.open_count : 0
return (
<div className="space-y-7">
@@ -83,18 +88,30 @@ export function CockpitView({ onNavigate }: { onNavigate: (v: string) => void })
{/* Ehrliche Status-Zeile */}
<StatusLine verdict={health.verdict} memory={health.memory} warns={health.warns} />
{/* Morgenlage — das jüngste Nacht-Gutachten (verschwindet, wenn keines da ist) */}
<MorgenlageCard />
{/* „Braucht dich" — offene Aktionen, sonst Ruhe */}
<NeedsYou
problems={health.problems}
pendingCount={pendingCount}
openAuftraege={openAuftraege}
busy={busy}
onRepair={runRepair}
onNavigate={onNavigate}
/>
{/* Bereichs-Kacheln */}
<section>
<ZoneLabel>Bereiche</ZoneLabel>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
<CockpitTile
icon={Inbox} title="Auftragsbuch" value={auftraege?.available ? openAuftraege : "—"}
unit={openAuftraege === 1 ? "Vorschlag" : "Vorschläge"}
tone={!auftraege ? "loading" : openAuftraege > 0 ? "warn" : "ok"}
hint="Annehmen oder ablehnen"
onClick={() => onNavigate("auftraege")}
/>
<CockpitTile
icon={Boxes} title="Modelle" value={running} unit="warm"
tone={running > 0 ? "ok" : "muted"} hint="Speicher, laden & Rollen"
@@ -129,6 +146,12 @@ export function CockpitView({ onNavigate }: { onNavigate: (v: string) => void })
</div>
</section>
{/* Anstehendes — Erinnerungen & Routinen (GUI-Hälfte der Wecker-API) */}
<section>
<ZoneLabel>Anstehendes</ZoneLabel>
<ErinnerungenCard />
</section>
{/* Leistung — die Live-Graphen zurück auf der Startseite (User-Wunsch 07.07.:
System-Verlauf, tok/s und Turn-Latenz waren nach dem UI-v3-Umbau nur noch in
der alten Zentrale erreichbar). Karten sind autark (eigene Hooks/Polling). */}
@@ -162,6 +185,16 @@ export function CockpitView({ onNavigate }: { onNavigate: (v: string) => void })
tone={!agent ? "loading" : agent.box_console_reachable ? "ok" : "warn"} hint="Direkte Box-Shell (SSH)"
onClick={() => onNavigate("konsole")}
/>
<CockpitTile
icon={Library} title="Wissen" value="öffnen"
tone="muted" hint="Lucys Wissens-Vault (Traum-Notizen)"
onClick={() => onNavigate("wissen")}
/>
<CockpitTile
icon={History} title="Chronik" value="öffnen"
tone="muted" hint="Autonome Taten + Zeitmaschine"
onClick={() => onNavigate("chronik")}
/>
</div>
</section>
@@ -225,18 +258,21 @@ function StatusLine({ verdict, memory, warns }: {
)
}
// „Braucht dich": kritische Probleme (mit 1-Klick-Reparatur) + bereitliegende Updates.
// Nichts offen → ruhige Bestätigung statt leerer Fläche.
// „Braucht dich": kritische Probleme (mit 1-Klick-Reparatur) + bereitliegende Updates
// + offene Vorschläge im Auftragsbuch. Nichts offen → ruhige Bestätigung statt leerer Fläche.
function NeedsYou({
problems, pendingCount, busy, onRepair,
problems, pendingCount, openAuftraege, busy, onRepair, onNavigate,
}: {
problems: ReturnType<typeof useLucyHealth>["problems"]
pendingCount: number
openAuftraege: number
busy: Record<string, boolean>
onRepair: (id: string, r: Repair) => void
onNavigate: (v: string) => void
}) {
const hasUpdates = pendingCount > 0
const nothing = problems.length === 0 && !hasUpdates
const hasAuftraege = openAuftraege > 0
const nothing = problems.length === 0 && !hasUpdates && !hasAuftraege
return (
<section>
@@ -274,6 +310,23 @@ function NeedsYou({
)}
</div>
))}
{hasAuftraege && (
<button
onClick={() => onNavigate("auftraege")}
className="flex w-full items-center justify-between gap-3 rounded-2xl border border-primary/30 bg-primary/5 p-4 text-left transition-all hover:bg-primary/10 cursor-pointer"
>
<div className="flex items-center gap-3">
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-primary/30 bg-background/30 text-primary">
<Inbox className="h-4.5 w-4.5" />
</span>
<div>
<p className="text-sm font-bold text-foreground">{openAuftraege} Vorschl{openAuftraege === 1 ? "ag" : "äge"} im Auftragsbuch</p>
<p className="text-xs text-muted-foreground">Die Box hat etwas gebaut oder sich ausgedacht annehmen oder ablehnen.</p>
</div>
</div>
<ChevronRight className="h-4 w-4 shrink-0 text-primary/70" />
</button>
)}
{hasUpdates && (
<button
onClick={() => openDrawer("maintenance")}