feat: add Vorhanden / In Benutzung filter mode to Model Manager
This commit is contained in:
@@ -139,6 +139,14 @@ function Cockpit() {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [hoveredNode, setHoveredNode] = useState<string | null>(null)
|
||||
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
|
||||
const [dimensions, setDimensions] = useState({ width: 800, height: 360 })
|
||||
@@ -729,38 +737,68 @@ function Cockpit() {
|
||||
|
||||
{/* ZONE C: Library list cards & Upgrade Radar */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<div className="text-xs font-bold uppercase tracking-wider text-muted-foreground">Installierte Modell-Bibliothek</div>
|
||||
<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 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 ({filteredModels.length} von {models.length})
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Filter mode selector */}
|
||||
<div className="flex rounded-lg border border-border/40 bg-card/45 p-0.5">
|
||||
<button
|
||||
onClick={() => setFilterMode("all")}
|
||||
className={cn(
|
||||
"px-2.5 py-1 rounded-md text-[9px] font-bold uppercase transition-all cursor-pointer",
|
||||
filterMode === "all" ? "bg-primary text-primary-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
|
||||
)}
|
||||
>
|
||||
Vorhanden
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilterMode("in_use")}
|
||||
className={cn(
|
||||
"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>
|
||||
|
||||
{viewMode === "grid" ? (
|
||||
<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">
|
||||
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>
|
||||
) : (
|
||||
models.map((m) => {
|
||||
filteredModels.map((m) => {
|
||||
const isRunning = running.includes(m.name)
|
||||
const hasUpgrade = updates?.model_list.find((u) => u.role === m.role)
|
||||
const brand = getBrandInfo(m.name)
|
||||
@@ -884,12 +922,14 @@ function Cockpit() {
|
||||
</div>
|
||||
) : (
|
||||
<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">
|
||||
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>
|
||||
) : (
|
||||
models.map((m) => {
|
||||
filteredModels.map((m) => {
|
||||
const isRunning = running.includes(m.name)
|
||||
const brand = getBrandInfo(m.name)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user