redesign: modern dark dashboard WOW effect - gradients, animations, new layout
This commit is contained in:
@@ -6,7 +6,7 @@ const TABS = ["Openclaw", "Сервисы"] as const;
|
||||
type TabName = typeof TABS[number];
|
||||
|
||||
const MACHINE_MAP: Record<TabName, string> = {
|
||||
"Openclaw": "openclaw",
|
||||
"Openclaw": "ocplatform",
|
||||
"Сервисы": "services",
|
||||
};
|
||||
|
||||
@@ -29,13 +29,20 @@ function CircularGauge({ value, size = 96, color, label }: { value: number; size
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<div className="relative" style={{ width: size, height: size }}>
|
||||
<svg width={size} height={size} className="-rotate-90">
|
||||
<defs>
|
||||
<linearGradient id={`gauge-gradient-${label}`} x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stopColor={color} stopOpacity="0.9" />
|
||||
<stop offset="100%" stopColor={color} stopOpacity="0.4" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx={size/2} cy={size/2} r={radius} fill="none" stroke="rgba(255,255,255,0.05)" strokeWidth="10" />
|
||||
<circle
|
||||
cx={size/2} cy={size/2} r={radius}
|
||||
fill="none" stroke={color}
|
||||
fill="none"
|
||||
stroke={`url(#gauge-gradient-${label})`}
|
||||
strokeWidth="10" strokeLinecap="round"
|
||||
strokeDasharray={circumference} strokeDashoffset={offset}
|
||||
style={{ transition: "stroke-dashoffset 0.8s ease", filter: `drop-shadow(0 0 6px ${color}60)` }}
|
||||
style={{ transition: "stroke-dashoffset 0.8s ease", filter: `drop-shadow(0 0 8px ${color}60)` }}
|
||||
/>
|
||||
</svg>
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
@@ -54,7 +61,7 @@ function UsageBar({ label, value, color, detail }: { label: string; value: numbe
|
||||
<span className="text-slate-400 font-medium">{label}</span>
|
||||
<span className="text-white font-semibold">{detail || `${value}%`}</span>
|
||||
</div>
|
||||
<div className="h-1.5 rounded-full overflow-hidden" style={{ background: "rgba(255,255,255,0.05)" }}>
|
||||
<div className="h-1.5 rounded-full overflow-hidden bg-white/5">
|
||||
<div
|
||||
className="h-full rounded-full transition-all duration-700"
|
||||
style={{ width: `${value}%`, background: color, boxShadow: `0 0 8px ${color}80` }}
|
||||
@@ -102,19 +109,19 @@ export default function SystemPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6 max-w-7xl mx-auto">
|
||||
<div className="flex items-center justify-between pt-2">
|
||||
<div className="space-y-5 max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between pt-1">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-white">System Monitor</h1>
|
||||
<p className="text-slate-500 text-sm mt-1">
|
||||
<h1 className="text-2xl font-bold text-white">System Monitor</h1>
|
||||
<p className="text-slate-500 text-sm mt-0.5">
|
||||
{lastUpdated ? `Обновлено: ${lastUpdated.toLocaleTimeString("ru-RU")}` : "Загрузка..."}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={fetchMetrics}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-xl border border-white/10 text-slate-300 text-sm transition-all hover:border-white/20 hover:bg-white/5 disabled:opacity-50"
|
||||
style={{ background: "rgba(255,255,255,0.03)" }}
|
||||
className="flex items-center gap-2 px-4 py-2 rounded-xl border border-white/10 text-slate-300 text-sm transition-all hover:border-white/20 hover:bg-white/5 disabled:opacity-50 bg-white/3"
|
||||
>
|
||||
<RefreshCw className={`w-4 h-4 ${loading ? "animate-spin" : ""}`} />
|
||||
Обновить
|
||||
@@ -122,14 +129,14 @@ export default function SystemPage() {
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1 p-1 rounded-xl w-fit border border-white/5" style={{ background: "rgba(255,255,255,0.03)" }}>
|
||||
<div className="flex gap-1 p-1 rounded-2xl w-fit bg-white/5">
|
||||
{TABS.map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`px-5 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
className={`px-5 py-2 rounded-xl text-sm font-medium transition-all ${
|
||||
activeTab === tab
|
||||
? "text-white"
|
||||
? "text-white shadow-lg shadow-indigo-500/20"
|
||||
: "text-slate-500 hover:text-slate-300"
|
||||
}`}
|
||||
style={activeTab === tab ? { background: "linear-gradient(135deg, #6366f1, #8b5cf6)" } : {}}
|
||||
@@ -150,9 +157,11 @@ export default function SystemPage() {
|
||||
) : (
|
||||
<>
|
||||
{/* Circular gauges */}
|
||||
<div className="card card-accent-violet p-6">
|
||||
<div className="card card-violet p-6">
|
||||
<div className="flex items-center gap-2 mb-6">
|
||||
<Activity className="w-4 h-4 text-violet-400" />
|
||||
<div className="w-7 h-7 rounded-lg bg-violet-500/15 flex items-center justify-center">
|
||||
<Activity className="w-3.5 h-3.5 text-violet-400" />
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-white">Загрузка системы</span>
|
||||
</div>
|
||||
{loading ? (
|
||||
@@ -171,10 +180,10 @@ export default function SystemPage() {
|
||||
{/* Stats grid */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{[
|
||||
{ icon: Cpu, label: "CPU", value: loading ? "..." : `${metrics?.cpu ?? "—"}%`, sub: loading ? "" : `Load: ${metrics?.load1?.toFixed(2) ?? "—"} · ${metrics?.cpuCount ?? "?"} cores`, color: "text-indigo-400", accent: "card-accent-blue" },
|
||||
{ icon: MemoryStick, label: "RAM", value: loading ? "..." : `${metrics?.ram?.used ?? "—"} GB`, sub: loading ? "" : `из ${metrics?.ram?.total ?? "?"} GB`, color: "text-violet-400", accent: "card-accent-violet" },
|
||||
{ icon: HardDrive, label: "Disk", value: loading ? "..." : `${metrics?.disk?.used ?? "—"} GB`, sub: loading ? "" : `из ${metrics?.disk?.total ?? "?"} GB`, color: "text-emerald-400", accent: "card-accent-emerald" },
|
||||
{ icon: Clock, label: "Uptime", value: loading ? "..." : (metrics?.uptime ?? "—"), sub: "Время работы", color: "text-amber-400", accent: "card-accent-amber" },
|
||||
{ icon: Cpu, label: "CPU", value: loading ? "..." : `${metrics?.cpu ?? "—"}%`, sub: loading ? "" : `Load: ${metrics?.load1?.toFixed(2) ?? "—"} · ${metrics?.cpuCount ?? "?"} cores`, color: "text-indigo-400", accent: "card-blue" },
|
||||
{ icon: MemoryStick, label: "RAM", value: loading ? "..." : `${metrics?.ram?.used ?? "—"} GB`, sub: loading ? "" : `из ${metrics?.ram?.total ?? "?"} GB`, color: "text-violet-400", accent: "card-violet" },
|
||||
{ icon: HardDrive, label: "Disk", value: loading ? "..." : `${metrics?.disk?.used ?? "—"} GB`, sub: loading ? "" : `из ${metrics?.disk?.total ?? "?"} GB`, color: "text-emerald-400", accent: "card-emerald" },
|
||||
{ icon: Clock, label: "Uptime", value: loading ? "..." : (metrics?.uptime ?? "—"), sub: "Время работы", color: "text-amber-400", accent: "card-amber" },
|
||||
].map((item) => (
|
||||
<div key={item.label} className={`card ${item.accent} p-5`}>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -203,9 +212,11 @@ export default function SystemPage() {
|
||||
|
||||
{/* Network */}
|
||||
{!loading && metrics?.network && (
|
||||
<div className="card card-accent-cyan p-5">
|
||||
<div className="card card-cyan p-5">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Wifi className="w-4 h-4 text-cyan-400" />
|
||||
<div className="w-7 h-7 rounded-lg bg-cyan-500/15 flex items-center justify-center">
|
||||
<Wifi className="w-3.5 h-3.5 text-cyan-400" />
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-white">Сеть (всего)</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
|
||||
Reference in New Issue
Block a user