UI: красивая AgentCard + улучшенный Dashboard
This commit is contained in:
parent
a73f1b143b
commit
a07da3b4bd
4 changed files with 81 additions and 56 deletions
47
frontend/src/components/agents/AgentCard.tsx
Normal file
47
frontend/src/components/agents/AgentCard.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import { Cpu, Activity } from 'lucide-react'
|
||||||
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
|
export function AgentCard({ agent }: { agent: any }) {
|
||||||
|
const statusColor = {
|
||||||
|
online: 'bg-emerald-500',
|
||||||
|
busy: 'bg-amber-500',
|
||||||
|
offline: 'bg-zinc-500',
|
||||||
|
}[agent.status] || 'bg-zinc-500'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
whileHover={{ scale: 1.02, y: -4 }}
|
||||||
|
className="group bg-card/80 border border-border hover:border-primary/50 rounded-3xl p-6 transition-all duration-300 backdrop-blur-xl"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className={`w-5 h-5 rounded-2xl ${statusColor} flex-shrink-0 ring-4 ring-background`} />
|
||||||
|
<div>
|
||||||
|
<div className="font-semibold text-xl tracking-tight">{agent.name}</div>
|
||||||
|
<div className="text-muted-foreground text-sm">{agent.runtime} · {agent.model}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Badge variant="outline">{agent.status}</Badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 flex gap-8">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Cpu className="w-6 h-6 text-muted-foreground" />
|
||||||
|
<div>
|
||||||
|
<div className="text-3xl font-mono font-semibold tabular-nums">{agent.cpu}</div>
|
||||||
|
<div className="text-[10px] uppercase tracking-widest text-muted-foreground -mt-1">CPU %</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Activity className="w-6 h-6 text-muted-foreground" />
|
||||||
|
<div>
|
||||||
|
<div className="text-3xl font-mono font-semibold tabular-nums">{agent.activeRuns}</div>
|
||||||
|
<div className="text-[10px] uppercase tracking-widest text-muted-foreground -mt-1">RUNS</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { motion } from 'framer-motion'
|
import { AgentCard } from './AgentCard'
|
||||||
import { Cpu, Server } from 'lucide-react'
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
|
||||||
import { useAppStore } from '@/store/useAppStore'
|
import { useAppStore } from '@/store/useAppStore'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
|
@ -33,57 +31,17 @@ export function AgentGrid() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex justify-between items-center mb-6">
|
<div className="flex justify-between items-center mb-6">
|
||||||
<h2 className="text-2xl font-semibold">Live Agents</h2>
|
<h2 className="text-xl font-semibold tracking-tight">Live Agents</h2>
|
||||||
<div className="text-sm text-emerald-500">{online} online</div>
|
<div className="text-sm text-emerald-500 font-medium">{online} online</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoading && !agents.length ? (
|
{isLoading && !agents.length ? (
|
||||||
<div className="text-muted-foreground text-sm">Loading agents...</div>
|
<div className="text-muted-foreground text-sm">Loading agents...</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
{agents.map((agent) => {
|
{agents.map((agent) => (
|
||||||
const config = {
|
<AgentCard key={agent.id} agent={agent} />
|
||||||
online: { color: 'bg-emerald-500', label: 'Online' },
|
))}
|
||||||
busy: { color: 'bg-amber-500', label: 'Busy' },
|
|
||||||
offline: { color: 'bg-zinc-500', label: 'Offline' },
|
|
||||||
}[agent.status]
|
|
||||||
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
key={agent.id}
|
|
||||||
whileHover={{ y: -2 }}
|
|
||||||
className="bg-card border border-border rounded-3xl p-6 hover:border-primary/30 transition-all group cursor-pointer"
|
|
||||||
>
|
|
||||||
<div className="flex justify-between items-start">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div className={`w-4 h-4 rounded-full ${config.color} ring-2 ring-offset-2 ring-offset-card ring-current animate-pulse`} />
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold text-lg">{agent.name}</h3>
|
|
||||||
<p className="text-sm text-muted-foreground">{agent.runtime} · {agent.model}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Badge variant="outline">{config.label}</Badge>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-6 grid grid-cols-2 gap-6">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Cpu className="w-5 h-5 text-muted-foreground" />
|
|
||||||
<div>
|
|
||||||
<div className="text-2xl font-mono">{agent.cpu}%</div>
|
|
||||||
<div className="text-xs text-muted-foreground">CPU</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Server className="w-5 h-5 text-muted-foreground" />
|
|
||||||
<div>
|
|
||||||
<div className="text-2xl font-mono">{agent.activeRuns}</div>
|
|
||||||
<div className="text-xs text-muted-foreground">Runs</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,17 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
body { background: var(--color-background); color: var(--color-foreground); font-family: 'Inter', system-ui, sans-serif; }
|
body {
|
||||||
|
background: var(--color-background);
|
||||||
|
color: var(--color-foreground);
|
||||||
|
font-family: 'Inter', system-ui, sans-serif;
|
||||||
|
font-feature-settings: "tnum";
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: rgba(24, 24, 27, 0.8);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
}
|
||||||
::-webkit-scrollbar { width: 6px; }
|
::-webkit-scrollbar { width: 6px; }
|
||||||
::-webkit-scrollbar-track { background: transparent; }
|
::-webkit-scrollbar-track { background: transparent; }
|
||||||
::-webkit-scrollbar-thumb { background: hsl(240 3.7% 25%); border-radius: 3px; }
|
::-webkit-scrollbar-thumb { background: hsl(240 3.7% 25%); border-radius: 3px; }
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,33 @@
|
||||||
import { AgentGrid } from '@/components/agents/AgentGrid'
|
import { AgentGrid } from '@/components/agents/AgentGrid'
|
||||||
import { Kanban } from '@/components/tasks/Kanban'
|
import { Kanban } from '@/components/tasks/Kanban'
|
||||||
import { ActivityFeed } from '@/components/ActivityFeed'
|
import { ActivityFeed } from '@/components/ActivityFeed'
|
||||||
|
import { useAppStore } from '@/store/useAppStore'
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
|
const { agents } = useAppStore()
|
||||||
|
const online = agents.filter(a => a.healthy).length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-8 space-y-8">
|
<div className="p-8 space-y-10">
|
||||||
<div>
|
<div className="flex items-center justify-between">
|
||||||
<h1 className="text-2xl font-bold">Agent Control Center</h1>
|
<div>
|
||||||
<p className="text-muted-foreground mt-1">Управление агентами, задачами и проектами</p>
|
<h1 className="text-4xl font-bold tracking-tighter">Agent Control Center</h1>
|
||||||
|
<p className="text-muted-foreground mt-1">Управление агентами, задачами и проектами</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<div className="text-emerald-500 text-sm font-medium">{online} online</div>
|
||||||
|
<div className="text-muted-foreground text-xs">v0.2.0</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||||
<div className="lg:col-span-5 bg-card border border-border rounded-2xl p-6">
|
<div className="lg:col-span-5 bg-card/80 border border-border rounded-3xl p-6 backdrop-blur-xl">
|
||||||
<AgentGrid />
|
<AgentGrid />
|
||||||
</div>
|
</div>
|
||||||
<div className="lg:col-span-7 bg-card border border-border rounded-2xl p-6">
|
<div className="lg:col-span-7 bg-card/80 border border-border rounded-3xl p-6 backdrop-blur-xl">
|
||||||
<Kanban />
|
<Kanban />
|
||||||
</div>
|
</div>
|
||||||
<div className="lg:col-span-12 bg-card border border-border rounded-2xl p-6">
|
<div className="lg:col-span-12 bg-card/80 border border-border rounded-3xl p-6 backdrop-blur-xl">
|
||||||
<ActivityFeed />
|
<ActivityFeed />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue