aegisai / frontend / src / components / Dashboard / Dashboard.tsx
Dashboard.tsx
Raw
import React, { useEffect, useRef } from 'react';
import {
  AreaChart,
  Area,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  ResponsiveContainer
} from 'recharts';
import type { SecurityEvent, SystemStats } from '../../types';
import { Activity, AlertTriangle, Eye, Cpu, Terminal } from 'lucide-react';

interface DashboardProps {
  events: SecurityEvent[];
  stats: SystemStats;
  isMonitoring: boolean;
  toggleMonitoring: () => void;
  lastAnalysis: SecurityEvent | null;
}

export const Dashboard: React.FC<DashboardProps> = ({
  events,
  stats,
  isMonitoring,
  toggleMonitoring,
  lastAnalysis
}) => {
  const scrollRef = useRef<HTMLDivElement>(null);

  // Auto-scroll terminal
  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [events]);

  const chartData = events.slice(-10).map((e) => ({
    time: new Date(e.timestamp).toLocaleTimeString([], {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    }),
    confidence: e.confidence,
    severity:
      e.severity === 'high'
        ? 100
        : e.severity === 'medium'
        ? 60
        : 20
  }));

  const getSeverityColor = (severity: string) => {
    switch (severity) {
      case 'high':
        return 'text-red-500';
      case 'medium':
        return 'text-orange-400';
      case 'low':
        return 'text-blue-400';
      default:
        return 'text-gray-400';
    }
  };

  const getSeverityBg = (severity: string) => {
    switch (severity) {
      case 'high':
        return 'bg-red-500/20 border-red-500';
      case 'medium':
        return 'bg-orange-500/20 border-orange-500';
      case 'low':
        return 'bg-blue-500/20 border-blue-500';
      default:
        return 'bg-gray-800 border-gray-700';
    }
  };

  return (
    <div className="flex flex-col h-full gap-4">

      {/* HEADER STATS */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-4">

        <div className="bg-aegis-panel border border-slate-700 rounded-lg p-4 flex items-center gap-4 shadow-lg">
          <div className="p-3 bg-blue-500/10 rounded-full text-blue-400">
            <Eye size={24} />
          </div>
          <div>
            <p className="text-slate-400 text-xs uppercase tracking-wider">
              Scans Performed
            </p>
            <p className="text-2xl font-mono font-bold text-white">
              {stats.scansPerformed}
            </p>
          </div>
        </div>

        <div className="bg-aegis-panel border border-slate-700 rounded-lg p-4 flex items-center gap-4 shadow-lg">
          <div
            className={`p-3 rounded-full ${
              stats.incidentsDetected > 0
                ? 'bg-red-500/10 text-red-500'
                : 'bg-green-500/10 text-green-500'
            }`}
          >
            <AlertTriangle size={24} />
          </div>
          <div>
            <p className="text-slate-400 text-xs uppercase tracking-wider">
              Incidents
            </p>
            <p className="text-2xl font-mono font-bold text-white">
              {stats.incidentsDetected}
            </p>
          </div>
        </div>

        <div className="bg-aegis-panel border border-slate-700 rounded-lg p-4 flex items-center gap-4 shadow-lg">
          <div className="p-3 bg-purple-500/10 rounded-full text-purple-400">
            <Cpu size={24} />
          </div>
          <div>
            <p className="text-slate-400 text-xs uppercase tracking-wider">
              System Load
            </p>
            <div className="flex items-end gap-2">
              <p className="text-2xl font-mono font-bold text-white">
                {stats.cpuUsage}%
              </p>
              <div className="h-1.5 w-16 bg-gray-700 rounded-full mb-2">
                <div
                  className="h-full bg-purple-500 rounded-full transition-all duration-500"
                  style={{ width: `${stats.cpuUsage}%` }}
                />
              </div>
            </div>
          </div>
        </div>

        <div className="bg-aegis-panel border border-slate-700 rounded-lg p-4 shadow-lg">
          <button
            onClick={toggleMonitoring}
            className={`w-full h-full rounded flex flex-col items-center justify-center transition-all ${
              isMonitoring
                ? 'bg-red-900/20 hover:bg-red-900/40 border border-red-500/50'
                : 'bg-green-900/20 hover:bg-green-900/40 border border-green-500/50'
            }`}
          >
            <div
              className={`text-sm font-bold tracking-widest uppercase mb-1 ${
                isMonitoring ? 'text-red-400' : 'text-green-400'
              }`}
            >
              {isMonitoring ? 'STOP SURVEILLANCE' : 'ACTIVATE AEGIS'}
            </div>
            <div
              className={`w-3 h-3 rounded-full ${
                isMonitoring
                  ? 'bg-red-500 animate-pulse'
                  : 'bg-green-500'
              }`}
            />
          </button>
        </div>
      </div>

      {/* MAIN GRID */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4 flex-1 min-h-0">

        {/* LEFT */}
        <div className="lg:col-span-2 flex flex-col gap-4">
          <div className="bg-aegis-panel border border-slate-700 rounded-lg p-4 flex-1 shadow-lg flex flex-col min-w-0">

            <h3 className="text-aegis-accent font-mono text-sm uppercase tracking-wider mb-4 flex items-center gap-2">
              <Activity size={16} /> Real-time Threat Analysis
            </h3>

            <div className="w-full h-[250px] min-w-0">
              <ResponsiveContainer width="100%" height="100%">
                <AreaChart data={chartData}>
                  <defs>
                    <linearGradient
                      id="colorConf"
                      x1="0"
                      y1="0"
                      x2="0"
                      y2="1"
                    >
                      <stop
                        offset="5%"
                        stopColor="#38bdf8"
                        stopOpacity={0.3}
                      />
                      <stop
                        offset="95%"
                        stopColor="#38bdf8"
                        stopOpacity={0}
                      />
                    </linearGradient>
                  </defs>
                  <CartesianGrid
                    strokeDasharray="3 3"
                    stroke="#334155"
                  />
                  <XAxis
                    dataKey="time"
                    stroke="#94a3b8"
                    fontSize={12}
                  />
                  <YAxis
                    stroke="#94a3b8"
                    fontSize={12}
                    domain={[0, 100]}
                  />
                  <Tooltip
                    contentStyle={{
                      backgroundColor: '#0f172a',
                      borderColor: '#334155',
                      color: '#f1f5f9'
                    }}
                  />
                  <Area
                    type="monotone"
                    dataKey="confidence"
                    stroke="#38bdf8"
                    fill="url(#colorConf)"
                  />
                </AreaChart>
              </ResponsiveContainer>
            </div>

            {/* LATEST */}
            <div className="mt-4 border-t border-slate-700 pt-4 flex-1">
              <h4 className="text-gray-400 text-xs uppercase mb-2">
                Latest Inference
              </h4>

              {lastAnalysis ? (
                <div
                  className={`border p-4 rounded bg-opacity-10 ${getSeverityBg(
                    lastAnalysis.severity
                  )}`}
                >
                  <div className="flex justify-between mb-2">
                    <span
                      className={`font-mono text-lg font-bold uppercase ${getSeverityColor(
                        lastAnalysis.severity
                      )}`}
                    >
                      {lastAnalysis.incident
                        ? '⚠ THREAT DETECTED'
                        : '✓ SECURE'}
                    </span>
                    <span className="font-mono text-xs text-gray-400">
                      {new Date(
                        lastAnalysis.timestamp
                      ).toLocaleTimeString()}
                    </span>
                  </div>

                  <div className="grid grid-cols-2 gap-4 mb-3">
                    <div>
                      <p className="text-gray-500 text-xs uppercase">
                        Type
                      </p>
                      <p className="text-white font-medium">
                        {lastAnalysis.type}
                      </p>
                    </div>
                    <div>
                      <p className="text-gray-500 text-xs uppercase">
                        Confidence
                      </p>
                      <p className="text-white font-medium">
                        {lastAnalysis.confidence}%
                      </p>
                    </div>
                  </div>

                  <p className="text-slate-300 text-sm italic">
                    "{lastAnalysis.reasoning}"
                  </p>
                </div>
              ) : (
                <div className="text-center py-8 text-gray-600 font-mono text-sm">
                  Waiting for video stream analysis...
                </div>
              )}
            </div>
          </div>
        </div>

        {/* RIGHT TERMINAL */}
        <div className="bg-black border border-slate-700 rounded-lg p-2 shadow-lg flex flex-col font-mono text-xs overflow-hidden">

          <div className="flex justify-between px-2 py-2 border-b border-gray-800">
            <span className="flex gap-2 text-gray-400">
              <Terminal size={14} /> SYSTEM_LOG
            </span>
            <div className="flex gap-1">
              <div className="w-2 h-2 rounded-full bg-red-500" />
              <div className="w-2 h-2 rounded-full bg-yellow-500" />
              <div className="w-2 h-2 rounded-full bg-green-500" />
            </div>
          </div>

          <div
            ref={scrollRef}
            className="flex-1 overflow-y-auto space-y-2 p-2"
          >
            {events.map((e) => (
              <div
                key={e.id}
                className="border-l-2 border-slate-700 pl-2"
              >
                <span className="text-gray-500">
                  [{new Date(e.timestamp).toLocaleTimeString()}]
                </span>{' '}
                <span
                  className={
                    e.incident
                      ? 'text-red-400'
                      : 'text-green-400'
                  }
                >
                  {e.incident ? 'ALRT' : 'INFO'}
                </span>{' '}
                <span className="text-blue-300">
                  @{e.type}
                </span>
                <p className="text-gray-300">
                  {e.reasoning}
                </p>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
};

export default Dashboard;