feat: add Vorhanden / In Benutzung filter mode to Model Manager

This commit is contained in:
Hitonabi
2026-06-26 08:23:50 +02:00
parent f32e4baf5b
commit 563e7837b9
6 changed files with 420 additions and 380 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,8 +7,8 @@
<link rel="manifest" href="/manifest.webmanifest" /> <link rel="manifest" href="/manifest.webmanifest" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>Mission Control 2.0</title> <title>Mission Control 2.0</title>
<script type="module" crossorigin src="/assets/index-BUUZ5n0A.js"></script> <script type="module" crossorigin src="/assets/index-Dk6KzQ7B.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cs_NgMMZ.css"> <link rel="stylesheet" crossorigin href="/assets/index-nR6_L3dg.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+67 -27
View File
@@ -139,6 +139,14 @@ function Cockpit() {
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
const [hoveredNode, setHoveredNode] = useState<string | null>(null) const [hoveredNode, setHoveredNode] = useState<string | null>(null)
const [viewMode, setViewMode] = useState<"grid" | "list">("grid") const [viewMode, setViewMode] = useState<"grid" | "list">("grid")
const [filterMode, setFilterMode] = useState<"all" | "in_use">("all")
const filteredModels = models.filter((m) => {
if (filterMode === "in_use") {
return !!m.role || running.includes(m.name)
}
return true
})
// Canvas pixel tracking for pixel-perfect connection graph without non-uniform scaling // Canvas pixel tracking for pixel-perfect connection graph without non-uniform scaling
const [dimensions, setDimensions] = useState({ width: 800, height: 360 }) const [dimensions, setDimensions] = useState({ width: 800, height: 360 })
@@ -729,38 +737,68 @@ function Cockpit() {
{/* ZONE C: Library list cards & Upgrade Radar */} {/* ZONE C: Library list cards & Upgrade Radar */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between px-1"> <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 px-1">
<div className="text-xs font-bold uppercase tracking-wider text-muted-foreground">Installierte Modell-Bibliothek</div> <div className="text-xs font-bold uppercase tracking-wider text-muted-foreground">
<div className="flex rounded-lg border border-border/40 bg-card/45 p-0.5"> Installierte Modell-Bibliothek ({filteredModels.length} von {models.length})
<button </div>
onClick={() => setViewMode("grid")}
className={cn( <div className="flex items-center gap-3">
"px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer", {/* Filter mode selector */}
viewMode === "grid" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground" <div className="flex rounded-lg border border-border/40 bg-card/45 p-0.5">
)} <button
> onClick={() => setFilterMode("all")}
Grid className={cn(
</button> "px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer",
<button filterMode === "all" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
onClick={() => setViewMode("list")} )}
className={cn( >
"px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer", Vorhanden
viewMode === "list" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground" </button>
)} <button
> onClick={() => setFilterMode("in_use")}
List className={cn(
</button> "px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer",
filterMode === "in_use" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
)}
>
In Benutzung
</button>
</div>
{/* Grid/List layout selector */}
<div className="flex rounded-lg border border-border/40 bg-card/45 p-0.5">
<button
onClick={() => setViewMode("grid")}
className={cn(
"px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer",
viewMode === "grid" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
)}
>
Grid
</button>
<button
onClick={() => setViewMode("list")}
className={cn(
"px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer",
viewMode === "list" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
)}
>
List
</button>
</div>
</div> </div>
</div> </div>
{viewMode === "grid" ? ( {viewMode === "grid" ? (
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3"> <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{models.length === 0 ? ( {filteredModels.length === 0 ? (
<div className="col-span-full text-xs text-muted-foreground border border-dashed border-border/60 rounded-2xl p-12 text-center select-none"> <div className="col-span-full text-xs text-muted-foreground border border-dashed border-border/60 rounded-2xl p-12 text-center select-none">
Keine Modelle konfiguriert. Verwende den Tab "Modelle finden" zum Herunterladen. {filterMode === "in_use"
? "Keine Modelle in Benutzung (keine Zuweisung oder warm geladene Modelle)."
: "Keine Modelle konfiguriert. Verwende den Tab \"Modelle finden\" zum Herunterladen."}
</div> </div>
) : ( ) : (
models.map((m) => { filteredModels.map((m) => {
const isRunning = running.includes(m.name) const isRunning = running.includes(m.name)
const hasUpgrade = updates?.model_list.find((u) => u.role === m.role) const hasUpgrade = updates?.model_list.find((u) => u.role === m.role)
const brand = getBrandInfo(m.name) const brand = getBrandInfo(m.name)
@@ -884,12 +922,14 @@ function Cockpit() {
</div> </div>
) : ( ) : (
<div className="space-y-2"> <div className="space-y-2">
{models.length === 0 ? ( {filteredModels.length === 0 ? (
<div className="text-xs text-muted-foreground border border-dashed border-border/60 rounded-2xl p-12 text-center select-none"> <div className="text-xs text-muted-foreground border border-dashed border-border/60 rounded-2xl p-12 text-center select-none">
Keine Modelle konfiguriert. Verwende den Tab "Modelle finden" zum Herunterladen. {filterMode === "in_use"
? "Keine Modelle in Benutzung (keine Zuweisung oder warm geladene Modelle)."
: "Keine Modelle konfiguriert. Verwende den Tab \"Modelle finden\" zum Herunterladen."}
</div> </div>
) : ( ) : (
models.map((m) => { filteredModels.map((m) => {
const isRunning = running.includes(m.name) const isRunning = running.includes(m.name)
const brand = getBrandInfo(m.name) const brand = getBrandInfo(m.name)