mirror of
https://github.com/arkorty/B.Tech-Project-III.git
synced 2026-04-19 20:51:49 +00:00
init
This commit is contained in:
208
thirdeye/dashboard/app/agents/AgentCards.tsx
Normal file
208
thirdeye/dashboard/app/agents/AgentCards.tsx
Normal file
@@ -0,0 +1,208 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
fetchGroups,
|
||||
fetchSignals,
|
||||
Group,
|
||||
Signal,
|
||||
formatRelativeTime,
|
||||
getSignalIcon,
|
||||
getSeverityColor,
|
||||
} from "../lib/api";
|
||||
|
||||
function GroupAgentCard({ group, signals, index }: { group: Group; signals: Signal[]; index: number }) {
|
||||
const recentSignals = signals.slice(0, 3);
|
||||
const latestTimestamp = signals[0]?.metadata?.timestamp;
|
||||
const criticalCount = signals.filter(
|
||||
(s) => s.metadata.severity === "critical" || s.metadata.severity === "high"
|
||||
).length;
|
||||
|
||||
const isActive = group.signal_count > 0;
|
||||
const statusLabel = isActive ? "ACTIVE" : "IDLE";
|
||||
const statusBg = isActive ? "rgba(16, 185, 129, 0.1)" : "rgba(168, 140, 251, 0.1)";
|
||||
const statusColor = isActive ? "#10b981" : "#a88cfb";
|
||||
|
||||
const gradients = [
|
||||
["#a88cfb", "#00daf3"],
|
||||
["#432390", "#a88cfb"],
|
||||
["#ee7d77", "#ffb300"],
|
||||
["#00daf3", "#a88cfb"],
|
||||
];
|
||||
const [colorA, colorB] = gradients[index % gradients.length];
|
||||
|
||||
return (
|
||||
<div
|
||||
className="glass rounded-xl border flex flex-col relative overflow-hidden group card-interactive animate-fade-in-up opacity-0"
|
||||
style={{ borderColor: "rgba(255,255,255,0.05)", animationDelay: `${index * 100}ms` }}
|
||||
>
|
||||
{/* Top gradient bar */}
|
||||
<div
|
||||
className="h-1 w-full absolute top-0 left-0 opacity-80"
|
||||
style={{ background: `linear-gradient(to right, ${colorA}, ${colorB}, transparent)` }}
|
||||
/>
|
||||
|
||||
<div className="p-6 flex flex-col gap-5 w-full h-full mt-1">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="space-y-1">
|
||||
<h4
|
||||
className="font-bold text-[15px] tracking-wide text-white drop-shadow-md"
|
||||
style={{ fontFamily: "'Inter Tight', sans-serif" }}
|
||||
>
|
||||
{group.group_name.toUpperCase()}
|
||||
</h4>
|
||||
<p className="text-[10px] font-mono-data opacity-50" style={{ color: "#a88cfb" }}>
|
||||
LENS: {group.lens?.toUpperCase() || "UNKNOWN"} · {group.signal_count} signals
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
className="px-2.5 py-0.5 rounded text-[9px] font-bold font-mono-data border uppercase tracking-widest shadow-sm"
|
||||
style={{
|
||||
backgroundColor: statusBg,
|
||||
color: statusColor,
|
||||
borderColor: `${statusColor}4d`,
|
||||
}}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Current Task */}
|
||||
<div
|
||||
className="p-4 rounded-xl bg-black/40 border shadow-inner"
|
||||
style={{ borderColor: "rgba(255,255,255,0.03)" }}
|
||||
>
|
||||
<p className="text-[9px] uppercase tracking-widest mb-2 text-zinc-500 font-mono-data">
|
||||
Latest Signal
|
||||
</p>
|
||||
<p className="text-[12px] font-medium tracking-wide text-zinc-300 line-clamp-2">
|
||||
{recentSignals[0]?.document || "No signals yet"}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Metrics */}
|
||||
<div className="grid grid-cols-2 gap-4 py-4 border-y border-white/5">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-[9px] uppercase tracking-widest text-zinc-500 font-mono-data">
|
||||
Total Signals
|
||||
</p>
|
||||
<p className="text-[13px] text-zinc-300 font-mono-data">{group.signal_count}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-[9px] uppercase tracking-widest text-zinc-500 font-mono-data">
|
||||
High Priority
|
||||
</p>
|
||||
<p
|
||||
className="text-[13px] font-mono-data"
|
||||
style={{ color: criticalCount > 0 ? "#ff6f78" : "#10b981" }}
|
||||
>
|
||||
{criticalCount > 0 ? `${criticalCount} alerts` : "all clear"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Terminal Output - last 3 signals */}
|
||||
<div className="space-y-3 mt-auto flex-1 flex flex-col">
|
||||
<div className="flex justify-between items-center opacity-70">
|
||||
<p className="text-[9px] uppercase tracking-widest text-zinc-400 font-mono-data">
|
||||
Signal Stream
|
||||
</p>
|
||||
<span className="material-symbols-outlined text-[16px] text-zinc-500">terminal</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="text-[10px] p-4 rounded-xl bg-black/50 border overflow-y-auto font-mono-data shadow-inner flex-1 mt-2"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,0.03)",
|
||||
color: "#9ca3af",
|
||||
minHeight: "5rem",
|
||||
maxHeight: "7rem",
|
||||
}}
|
||||
>
|
||||
{recentSignals.length === 0 ? (
|
||||
<p className="opacity-40">no signals yet_</p>
|
||||
) : (
|
||||
recentSignals.map((sig, idx) => (
|
||||
<p key={idx} className="mb-1.5 leading-relaxed">
|
||||
<span style={{ color: getSeverityColor(sig.metadata.severity), opacity: 0.9 }}>
|
||||
{sig.metadata.type}:
|
||||
</span>{" "}
|
||||
<span className="opacity-70 text-zinc-300">
|
||||
{sig.document.slice(0, 60)}
|
||||
{sig.document.length > 60 ? "…" : ""}
|
||||
</span>
|
||||
</p>
|
||||
))
|
||||
)}
|
||||
<p className="animate-pulse mt-2 opacity-40">_</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AgentCards() {
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [groupSignals, setGroupSignals] = useState<Record<string, Signal[]>>({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
try {
|
||||
const grps = await fetchGroups();
|
||||
setGroups(grps);
|
||||
const sigMap: Record<string, Signal[]> = {};
|
||||
await Promise.all(
|
||||
grps.map(async (g) => {
|
||||
try {
|
||||
const sigs = await fetchSignals(g.group_id);
|
||||
sigMap[g.group_id] = sigs;
|
||||
} catch {
|
||||
sigMap[g.group_id] = [];
|
||||
}
|
||||
})
|
||||
);
|
||||
setGroupSignals(sigMap);
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-20 text-zinc-600 col-span-3">
|
||||
<span className="material-symbols-outlined animate-spin mr-3">autorenew</span>
|
||||
Loading groups...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (groups.length === 0) {
|
||||
return (
|
||||
<div className="text-center py-20 text-zinc-600 col-span-3">
|
||||
<span className="material-symbols-outlined text-4xl mb-3 block">group_off</span>
|
||||
<p>No monitored groups yet.</p>
|
||||
<p className="text-[11px] mt-2">Connect Telegram groups to see agents here.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 animate-fade-in-up">
|
||||
{groups.map((group, idx) => (
|
||||
<GroupAgentCard
|
||||
key={group.group_id}
|
||||
group={group}
|
||||
signals={groupSignals[group.group_id] || []}
|
||||
index={idx}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
104
thirdeye/dashboard/app/agents/AgentStats.tsx
Normal file
104
thirdeye/dashboard/app/agents/AgentStats.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { fetchGroups, fetchAllSignals, Group, Signal } from "../lib/api";
|
||||
|
||||
export default function AgentStats() {
|
||||
const [groups, setGroups] = useState<Group[]>([]);
|
||||
const [signals, setSignals] = useState<Signal[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
try {
|
||||
const [grps, all] = await Promise.all([fetchGroups(), fetchAllSignals()]);
|
||||
setGroups(grps);
|
||||
setSignals(all.flatMap((g) => g.signals));
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
load();
|
||||
}, []);
|
||||
|
||||
const totalSignals = groups.reduce((acc, g) => acc + g.signal_count, 0);
|
||||
const activeGroups = groups.filter((g) => g.signal_count > 0).length;
|
||||
const criticalSignals = signals.filter(
|
||||
(s) => s.metadata.severity === "critical" || s.metadata.severity === "high"
|
||||
).length;
|
||||
const errorRate =
|
||||
totalSignals > 0
|
||||
? `${((criticalSignals / totalSignals) * 100).toFixed(2)}%`
|
||||
: "0.00%";
|
||||
|
||||
const stats = [
|
||||
{
|
||||
title: "Active Groups",
|
||||
value: loading ? "—" : `${activeGroups} / ${groups.length}`,
|
||||
icon: "memory",
|
||||
iconColor: "#a88cfb",
|
||||
},
|
||||
{
|
||||
title: "Total Signals",
|
||||
value: loading ? "—" : totalSignals >= 1000 ? `${(totalSignals / 1000).toFixed(1)}k` : String(totalSignals),
|
||||
icon: "speed",
|
||||
iconColor: "#00daf3",
|
||||
},
|
||||
{
|
||||
title: "High Priority",
|
||||
value: loading ? "—" : `${criticalSignals}`,
|
||||
icon: "warning",
|
||||
iconColor: criticalSignals > 0 ? "#ff6f78" : "#10b981",
|
||||
},
|
||||
{
|
||||
title: "Lens Coverage",
|
||||
value: loading ? "—" : `${[...new Set(groups.map((g) => g.lens).filter(Boolean))].length} types`,
|
||||
icon: "verified",
|
||||
iconColor: "#10b981",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 animate-fade-in-scale mb-8">
|
||||
{stats.map((stat, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="glass p-5 rounded-2xl border border-white/5 relative overflow-hidden group card-interactive flex flex-col justify-between"
|
||||
style={{
|
||||
minHeight: "100px",
|
||||
background: "rgba(20,20,25,0.4)",
|
||||
backdropFilter: "blur(12px)",
|
||||
}}
|
||||
>
|
||||
<p className="text-[10px] uppercase tracking-widest text-zinc-500 font-mono-data mb-3">
|
||||
{stat.title}
|
||||
</p>
|
||||
<div className="flex flex-row items-end justify-between">
|
||||
<h3 className="text-2xl font-light tracking-wide text-zinc-200 font-mono-data drop-shadow">
|
||||
{stat.value.split(" ").map((part, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className={
|
||||
i % 2 !== 0 && part.match(/[a-zA-Z]/)
|
||||
? "text-sm ml-1 text-zinc-500"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{part}{" "}
|
||||
</span>
|
||||
))}
|
||||
</h3>
|
||||
<span
|
||||
className="material-symbols-outlined text-[24px] opacity-90 drop-shadow-md"
|
||||
style={{ color: stat.iconColor }}
|
||||
>
|
||||
{stat.icon}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
thirdeye/dashboard/app/agents/SystemTicker.tsx
Normal file
30
thirdeye/dashboard/app/agents/SystemTicker.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
export default function SystemTicker() {
|
||||
return (
|
||||
<div
|
||||
className="mt-12 p-3.5 rounded-lg overflow-hidden bg-black/20 border"
|
||||
style={{
|
||||
borderColor: "rgba(167, 139, 250, 0.05)",
|
||||
boxShadow: "inset 0 2px 10px rgba(0,0,0,0.1)"
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<span
|
||||
className="material-symbols-outlined text-[16px] opacity-80"
|
||||
style={{ color: "#a88cfb" }}
|
||||
>
|
||||
pulse_alert
|
||||
</span>
|
||||
<div className="flex-1 overflow-hidden whitespace-nowrap">
|
||||
<div
|
||||
className="animate-marquee font-mono-data text-[10px] uppercase tracking-widest pl-[100%]"
|
||||
style={{ color: "#75757c" }}
|
||||
>
|
||||
SYSTEM_UPDATE: Node 14 synchronized. New encryption keys deployed. Agent
|
||||
'Graph_Builder_02' memory usage spike detected at 14:22:01. Global latency
|
||||
remains within 40ms threshold.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
82
thirdeye/dashboard/app/agents/agents.css
Normal file
82
thirdeye/dashboard/app/agents/agents.css
Normal file
@@ -0,0 +1,82 @@
|
||||
/* Agent fleet specific CSS */
|
||||
.glass-card {
|
||||
background: linear-gradient(180deg, rgba(28, 20, 45, 0.4) 0%, rgba(18, 14, 28, 0.6) 100%);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(167, 139, 250, 0.08);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.glass-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 1px;
|
||||
background: linear-gradient(to bottom, transparent, rgba(167, 139, 250, 0.5), transparent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: rgba(167, 139, 250, 0.2);
|
||||
box-shadow: 0 10px 40px rgba(167, 139, 250, 0.15), inset 0 0 0 1px rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.glass-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.neon-glow-violet {
|
||||
box-shadow: 0 0 20px rgba(167, 139, 250, 0.15);
|
||||
}
|
||||
|
||||
.font-mono-data {
|
||||
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.terminal-scroll::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
.terminal-scroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(167, 139, 250, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
@keyframes agent-marquee {
|
||||
0% { transform: translateX(100%); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}
|
||||
|
||||
.animate-marquee {
|
||||
display: inline-block;
|
||||
animation: agent-marquee 35s linear infinite;
|
||||
}
|
||||
|
||||
/* Terminal Line Entry Animation */
|
||||
.terminal-line {
|
||||
opacity: 0;
|
||||
animation: fade-in-up 0.5s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.terminal-line:nth-child(1) { animation-delay: 0.2s; }
|
||||
.terminal-line:nth-child(2) { animation-delay: 0.5s; }
|
||||
.terminal-line:nth-child(3) { animation-delay: 0.8s; }
|
||||
.terminal-line:nth-child(4) { animation-delay: 1.1s; }
|
||||
75
thirdeye/dashboard/app/agents/page.tsx
Normal file
75
thirdeye/dashboard/app/agents/page.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import "./agents.css";
|
||||
import Sidebar from "../components/Sidebar";
|
||||
import TopBar from "../components/TopBar";
|
||||
import AgentStats from "./AgentStats";
|
||||
import AgentCards from "./AgentCards";
|
||||
import SystemTicker from "./SystemTicker";
|
||||
|
||||
export const metadata = {
|
||||
title: "ThirdEye | Agent Operations",
|
||||
description: "Agent Fleet Management — ThirdEye Sovereign Protocol",
|
||||
};
|
||||
|
||||
export default function AgentsPage() {
|
||||
return (
|
||||
<div className="flex h-screen overflow-hidden bg-[#09090B] text-white" style={{ fontFamily: "'Poppins', sans-serif" }}>
|
||||
<Sidebar />
|
||||
<main className="flex-1 ml-[240px] flex flex-col h-screen overflow-hidden">
|
||||
<TopBar />
|
||||
|
||||
{/* Scrollable content */}
|
||||
<div className="flex-1 overflow-y-auto custom-scrollbar px-10 pb-12 pt-10">
|
||||
{/* Header & Stats */}
|
||||
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-end mb-10 gap-6">
|
||||
<div className="space-y-1">
|
||||
<h1
|
||||
className="text-3xl font-black tracking-tighter text-white flex items-center gap-3"
|
||||
style={{ fontFamily: "'Inter Tight', sans-serif" }}
|
||||
>
|
||||
Fleet Management
|
||||
<span
|
||||
className="px-2 py-0.5 text-[10px] rounded border font-mono-data tracking-widest uppercase"
|
||||
style={{
|
||||
backgroundColor: "rgba(168, 140, 251, 0.1)",
|
||||
color: "#a88cfb",
|
||||
borderColor: "rgba(168, 140, 251, 0.2)",
|
||||
}}
|
||||
>
|
||||
Live Status
|
||||
</span>
|
||||
</h1>
|
||||
<p
|
||||
className="max-w-xl text-sm leading-relaxed"
|
||||
style={{ color: "#acaab1" }}
|
||||
>
|
||||
Active deployment of neural processing agents across ThirdEye node
|
||||
clusters. Monitoring real-time throughput and cognitive load.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
className="font-bold text-sm px-6 py-3 rounded-lg flex items-center gap-3 neon-glow-violet active:scale-95 transition-all"
|
||||
style={{
|
||||
backgroundColor: "#a88cfb",
|
||||
color: "#260069",
|
||||
fontFamily: "'Inter Tight', sans-serif",
|
||||
}}
|
||||
>
|
||||
<span className="material-symbols-outlined text-lg">add_circle</span>
|
||||
Deploy New Agent
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<AgentStats />
|
||||
|
||||
{/* Agent Cards Grid */}
|
||||
<AgentCards />
|
||||
|
||||
{/* Ticker Section */}
|
||||
<SystemTicker />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user