Refactor: Frontend-Fundament — Format-Utils, Dialog-Ref, Typen (Phase 3a)
- Neuer lib/format.ts: gb/fmtBytes/fmtSize/fmtEta/fmtCtx zentral; aus DashboardView/SystemView/ModelsView entdoppelt und importiert. - CustomDialog: document.getElementById -> useRef (kein DOM-Query, robuster). - api.ts: GitInfo/ComponentVersion/Versions typisiert, SystemStatus.versions ergänzt; App.tsx nutzt SystemStatus statt any. tsc grün, Build grün, Dashboard verifiziert (keine Konsolenfehler, Formatierer rendern identisch). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,10 @@ import { useEffect, useState } from "react"
|
||||
import { Bot, Cpu, ExternalLink, Brain, Layers, Plus, ShieldAlert, X, Power, Shield, Download, RefreshCw, Check, Coins } from "lucide-react"
|
||||
import { api, type SystemStatus, type AgentStatus, type ModelInfo, type Memory, type UpdatesResp, type Job } from "@/lib/api"
|
||||
import { cn, resolveExternalUrl } from "@/lib/utils"
|
||||
import { gb } from "@/lib/format"
|
||||
import { CustomDialog } from "@/components/CustomDialog"
|
||||
|
||||
|
||||
function gb(b: number) {
|
||||
return (b / 1024 ** 3).toFixed(1)
|
||||
}
|
||||
|
||||
function RadialGauge({ value, label, detail }: { value: number; label: string; detail?: string }) {
|
||||
const radius = 24
|
||||
const circ = 2 * Math.PI * radius
|
||||
|
||||
@@ -3,21 +3,10 @@ import { Download, Search, Trash2, Edit3, Star, Layers, Activity, HardDrive, X,
|
||||
import { api, type DiscoverResp, type Fit, type Job, type ModelInfo, type RoutingResp, type UpdatesResp, type ConnectResp } from "@/lib/api"
|
||||
import { CapsChips } from "@/components/CapsChips"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { fmtBytes, fmtEta, fmtSize, fmtCtx } from "@/lib/format"
|
||||
import { CustomDialog } from "@/components/CustomDialog"
|
||||
|
||||
|
||||
function fmtBytes(b?: number) {
|
||||
if (!b) return ""
|
||||
if (b > 1024 ** 3) return `${(b / 1024 ** 3).toFixed(1)} GB`
|
||||
return `${(b / 1024 ** 2).toFixed(0)} MB`
|
||||
}
|
||||
|
||||
function fmtEta(s?: number) {
|
||||
if (!s) return ""
|
||||
const m = Math.floor(s / 60)
|
||||
return m > 0 ? `${m} min` : `${s} s`
|
||||
}
|
||||
|
||||
function JobsBar({ onError }: { onError?: (msg: string) => void }) {
|
||||
const [jobs, setJobs] = useState<Job[]>([])
|
||||
const [errorMsg, setErrorMsg] = useState<string | null>(null)
|
||||
@@ -100,16 +89,6 @@ function JobsBar({ onError }: { onError?: (msg: string) => void }) {
|
||||
)
|
||||
}
|
||||
|
||||
function fmtSize(b?: number | null) {
|
||||
if (!b) return "—"
|
||||
const gb = b / 1024 ** 3
|
||||
return gb >= 1 ? `${gb.toFixed(1)} GB` : `${(b / 1024 ** 2).toFixed(0)} MB`
|
||||
}
|
||||
|
||||
function fmtCtx(c: number | null) {
|
||||
return c ? `${Math.round(c / 1024)}k` : "—"
|
||||
}
|
||||
|
||||
function FitBadge({ fit }: { fit: Fit }) {
|
||||
const tone = {
|
||||
perfect: "bg-emerald-500/15 text-emerald-400 border border-emerald-500/20",
|
||||
|
||||
@@ -2,13 +2,10 @@ import { useEffect, useState } from "react"
|
||||
import { ExternalLink, RefreshCw, Save, Cpu, HardDrive, Cpu as GpuIcon, Activity } from "lucide-react"
|
||||
import { api, type ServicesResp, type SystemStatus } from "@/lib/api"
|
||||
import { cn, resolveExternalUrl } from "@/lib/utils"
|
||||
import { gb } from "@/lib/format"
|
||||
import { CustomDialog } from "@/components/CustomDialog"
|
||||
|
||||
|
||||
function gb(b: number) {
|
||||
return (b / 1024 ** 3).toFixed(1)
|
||||
}
|
||||
|
||||
function DiagnosticBar({ label, percent, detail, icon: Icon }: { label: string; percent: number; detail?: string; icon: any }) {
|
||||
const barColor = percent > 90
|
||||
? "bg-red-500 shadow-md shadow-red-500/20"
|
||||
|
||||
Reference in New Issue
Block a user