CodebaseAi commited on
Commit
bf30d02
·
1 Parent(s): d58a19f

chore: remove frontend folder from backend deployment

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. frontend/.gitignore +0 -24
  2. frontend/README.md +0 -16
  3. frontend/components.json +0 -22
  4. frontend/eslint.config.js +0 -29
  5. frontend/index.html +0 -17
  6. frontend/jsconfig.json +0 -8
  7. frontend/package-lock.json +0 -0
  8. frontend/package.json +0 -65
  9. frontend/postcss.config.js +0 -6
  10. frontend/public/images.ico +0 -0
  11. frontend/public/vite.svg +0 -1
  12. frontend/src/App.jsx +0 -93
  13. frontend/src/api.js +0 -183
  14. frontend/src/assests/Dos-HTTPtest.jpg +0 -0
  15. frontend/src/assests/Dos_Attacks-hulk.jpeg +0 -0
  16. frontend/src/assests/HOIC.jpeg +0 -0
  17. frontend/src/assests/LOic.jpeg +0 -0
  18. frontend/src/assests/heroBlocks.svg +0 -30
  19. frontend/src/assests/sql.jpeg +0 -0
  20. frontend/src/assests/vpn.jpg +0 -0
  21. frontend/src/assests/web.jpeg +0 -0
  22. frontend/src/components/Alerts.jsx +0 -36
  23. frontend/src/components/ConstellationBackground.jsx +0 -85
  24. frontend/src/components/FeatureGuard.jsx +0 -63
  25. frontend/src/components/Header.jsx +0 -16
  26. frontend/src/components/IPInfoModal.jsx +0 -182
  27. frontend/src/components/LoadingOverlay.jsx +0 -12
  28. frontend/src/components/MainLayout.jsx +0 -13
  29. frontend/src/components/Navbar.jsx +0 -128
  30. frontend/src/components/NeonShield.jsx +0 -87
  31. frontend/src/components/ProtectedRoute.jsx +0 -10
  32. frontend/src/components/Sidebar.jsx +0 -324
  33. frontend/src/components/TopStatusBar.jsx +0 -13
  34. frontend/src/components/dashboard/Controls.jsx +0 -55
  35. frontend/src/components/dashboard/LiveDashboard.jsx +0 -254
  36. frontend/src/components/dashboard/LiveTable.jsx +0 -168
  37. frontend/src/components/dashboard/Sparkline.jsx +0 -28
  38. frontend/src/components/dashboard/StatsPanel.jsx +0 -104
  39. frontend/src/components/dashboard/ThreatFeed.jsx +0 -39
  40. frontend/src/components/dashboard/ThreatTimeline.jsx +0 -27
  41. frontend/src/components/dashboard/TopCountries.jsx +0 -26
  42. frontend/src/components/dashboard/TopIPs.jsx +0 -26
  43. frontend/src/components/ui/accordion.jsx +0 -43
  44. frontend/src/components/ui/badge.jsx +0 -34
  45. frontend/src/components/ui/button.jsx +0 -47
  46. frontend/src/components/ui/card.jsx +0 -50
  47. frontend/src/components/ui/input.jsx +0 -19
  48. frontend/src/components/ui/scroll-area.jsx +0 -38
  49. frontend/src/components/ui/separator.jsx +0 -23
  50. frontend/src/components/ui/switch.jsx +0 -24
frontend/.gitignore DELETED
@@ -1,24 +0,0 @@
1
- # Logs
2
- logs
3
- *.log
4
- npm-debug.log*
5
- yarn-debug.log*
6
- yarn-error.log*
7
- pnpm-debug.log*
8
- lerna-debug.log*
9
-
10
- node_modules
11
- dist
12
- dist-ssr
13
- *.local
14
-
15
- # Editor directories and files
16
- .vscode/*
17
- !.vscode/extensions.json
18
- .idea
19
- .DS_Store
20
- *.suo
21
- *.ntvs*
22
- *.njsproj
23
- *.sln
24
- *.sw?
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/README.md DELETED
@@ -1,16 +0,0 @@
1
- # React + Vite
2
-
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
-
5
- Currently, two official plugins are available:
6
-
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
-
10
- ## React Compiler
11
-
12
- The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
13
-
14
- ## Expanding the ESLint configuration
15
-
16
- If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/components.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "default",
4
- "rsc": false,
5
- "tsx": false,
6
- "tailwind": {
7
- "config": "tailwind.config.js",
8
- "css": "src/index.css",
9
- "baseColor": "slate",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "iconLibrary": "lucide",
14
- "aliases": {
15
- "components": "@/components",
16
- "utils": "@/lib/utils",
17
- "ui": "@/components/ui",
18
- "lib": "@/lib",
19
- "hooks": "@/hooks"
20
- },
21
- "registries": {}
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/eslint.config.js DELETED
@@ -1,29 +0,0 @@
1
- import js from '@eslint/js'
2
- import globals from 'globals'
3
- import reactHooks from 'eslint-plugin-react-hooks'
4
- import reactRefresh from 'eslint-plugin-react-refresh'
5
- import { defineConfig, globalIgnores } from 'eslint/config'
6
-
7
- export default defineConfig([
8
- globalIgnores(['dist']),
9
- {
10
- files: ['**/*.{js,jsx}'],
11
- extends: [
12
- js.configs.recommended,
13
- reactHooks.configs['recommended-latest'],
14
- reactRefresh.configs.vite,
15
- ],
16
- languageOptions: {
17
- ecmaVersion: 2020,
18
- globals: globals.browser,
19
- parserOptions: {
20
- ecmaVersion: 'latest',
21
- ecmaFeatures: { jsx: true },
22
- sourceType: 'module',
23
- },
24
- },
25
- rules: {
26
- 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
27
- },
28
- },
29
- ])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/index.html DELETED
@@ -1,17 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link
7
- rel="stylesheet"
8
- href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
9
- />
10
- <link rel="shortcut icon" href="/images.ico" />
11
- <title>NIDS Cyber Security</title>
12
- </head>
13
- <body>
14
- <div id="root"></div>
15
- <script type="module" src="/src/main.jsx"></script>
16
- </body>
17
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/jsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "baseUrl": "./src",
4
- "paths": {
5
- "@/*": ["*"]
6
- }
7
- }
8
- }
 
 
 
 
 
 
 
 
 
frontend/package-lock.json DELETED
The diff for this file is too large to render. See raw diff
 
frontend/package.json DELETED
@@ -1,65 +0,0 @@
1
- {
2
- "name": "frontend",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "lint": "eslint .",
10
- "preview": "vite preview",
11
- "tailwind-init": "tailwindcss init -p"
12
- },
13
- "dependencies": {
14
- "@radix-ui/react-accordion": "^1.2.12",
15
- "@radix-ui/react-scroll-area": "^1.2.10",
16
- "@radix-ui/react-separator": "^1.1.8",
17
- "@radix-ui/react-slot": "^1.2.4",
18
- "@radix-ui/react-switch": "^1.2.6",
19
- "@radix-ui/react-tabs": "^1.1.13",
20
- "@react-three/drei": "^10.7.6",
21
- "@react-three/fiber": "^9.4.0",
22
- "axios": "^1.13.1",
23
- "class-variance-authority": "^0.7.1",
24
- "clsx": "^2.1.1",
25
- "firebase": "^12.5.0",
26
- "framer-motion": "^12.23.24",
27
- "html2canvas": "^1.4.1",
28
- "jspdf": "^3.0.3",
29
- "leaflet": "^1.9.4",
30
- "lodash": "^4.17.21",
31
- "lucide-react": "^0.552.0",
32
- "prop-types": "^15.8.1",
33
- "react": "^19.1.1",
34
- "react-circular-progressbar": "^2.2.0",
35
- "react-dom": "^19.1.1",
36
- "react-draggable": "^4.5.0",
37
- "react-gauge-chart": "^0.5.1",
38
- "react-hot-toast": "^2.6.0",
39
- "react-is": "^19.2.0",
40
- "react-leaflet": "^5.0.0",
41
- "react-parallax-tilt": "^1.7.313",
42
- "react-router-dom": "^7.9.5",
43
- "react-simple-maps": "^3.0.0",
44
- "recharts": "^3.3.0",
45
- "socket.io-client": "^4.8.1",
46
- "tailwind-merge": "^3.4.0",
47
- "tailwindcss-animate": "^1.0.7",
48
- "three": "^0.181.1",
49
- "xlsx": "^0.18.5"
50
- },
51
- "devDependencies": {
52
- "@eslint/js": "^9.36.0",
53
- "@types/react": "^19.1.16",
54
- "@types/react-dom": "^19.1.9",
55
- "@vitejs/plugin-react": "^5.0.4",
56
- "autoprefixer": "^10.4.21",
57
- "eslint": "^9.36.0",
58
- "eslint-plugin-react-hooks": "^5.2.0",
59
- "eslint-plugin-react-refresh": "^0.4.22",
60
- "globals": "^16.4.0",
61
- "postcss": "^8.5.6",
62
- "tailwindcss": "^3.4.18",
63
- "vite": "^7.1.7"
64
- }
65
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/postcss.config.js DELETED
@@ -1,6 +0,0 @@
1
- export default {
2
- plugins: {
3
- tailwindcss: {},
4
- autoprefixer: {},
5
- },
6
- };
 
 
 
 
 
 
 
frontend/public/images.ico DELETED
Binary file (4.29 kB)
 
frontend/public/vite.svg DELETED
frontend/src/App.jsx DELETED
@@ -1,93 +0,0 @@
1
- import { BrowserRouter as Router, Routes, Route, useLocation, Navigate } from "react-router-dom";
2
- import { LiveDataProvider } from "./context/DataContext";
3
- import { AuthProvider } from "./context/AuthContext";
4
- import ProtectedRoute from "./components/ProtectedRoute";
5
- import Sidebar from "./components/Sidebar";
6
- import { useState } from "react";
7
-
8
- // --- IMPORT THE GUARD ---
9
- import FeatureGuard from "./components/FeatureGuard";
10
-
11
- import AuthPage from "./pages/Login";
12
- import Dashboard from "./pages/Dashboard";
13
- import LiveTraffic from "./components/dashboard/LiveDashboard";
14
- import FlowPage from "./pages/FlowPage";
15
- import SettingsPage from "./pages/SettingsPage";
16
- import InfoPage from "./pages/InfoPage";
17
- import ThreatIntel from "./pages/ThreatIntel";
18
- import Reports from "./pages/ReportsPage";
19
- import TrafficPage from "./pages/TrafficPage";
20
- import IncidentsPage from "./pages/IncidentsPage";
21
- import SystemPage from "./pages/SystemPage";
22
- import ResponsePage from "./pages/ResponsePage";
23
- import MlmodelPage from "./pages/MLModelsPage";
24
- import ConstellationBackground from "./components/ConstellationBackground";
25
- import SamplePred from "./pages/MLAttackSamplesPage";
26
- import Aichat from "./pages/ChatAssistant";
27
- import MainLayout from "./components/MainLayout";
28
-
29
- function AppLayout() {
30
- const location = useLocation();
31
- const hideSidebar = location.pathname === "/login";
32
- const [collapsed, setCollapsed] = useState(false);
33
-
34
- // 🛠️ DEFINE LOCAL-ONLY ROUTES HERE
35
- const localOnlyRoutes = ["/livetraffic", "/flow", "/system", "/traffic"];
36
- const isProtectedFeature = localOnlyRoutes.includes(location.pathname);
37
-
38
- return (
39
- <div className="flex min-h-screen text-slate-100 relative bg-transparent">
40
- <ConstellationBackground />
41
-
42
- {!hideSidebar && (
43
- <Sidebar collapsed={collapsed} setCollapsed={setCollapsed} />
44
- )}
45
-
46
- <main
47
- className={`flex-1 overflow-y-auto overflow-x-hidden p-6 min-h-0 transition-all duration-300
48
- ${collapsed ? "ml-20" : "ml-64"}
49
- `}
50
- >
51
- {/* Wrap the Routes in the FeatureGuard.
52
- It will only trigger the popup if the current path is in localOnlyRoutes
53
- AND the user is not on localhost.
54
- */}
55
- <FeatureGuard requireLocal={isProtectedFeature}>
56
- <Routes>
57
- <Route path="/login" element={<AuthPage />} />
58
- <Route path="/" element={<ProtectedRoute><MainLayout><Dashboard /></MainLayout></ProtectedRoute>} />
59
-
60
- {/* These routes will now trigger the popup if deployed */}
61
- <Route path="/livetraffic" element={<ProtectedRoute><MainLayout><LiveTraffic /></MainLayout></ProtectedRoute>} />
62
- <Route path="/flow" element={<ProtectedRoute><MainLayout><FlowPage /></MainLayout></ProtectedRoute>} />
63
- <Route path="/system" element={<ProtectedRoute><MainLayout><SystemPage /></MainLayout></ProtectedRoute>} />
64
- <Route path="/traffic" element={<ProtectedRoute><MainLayout><TrafficPage /></MainLayout></ProtectedRoute>} />
65
-
66
- {/* These routes work fine in the cloud (Database/AI based) */}
67
- <Route path="/settings" element={<ProtectedRoute><MainLayout><SettingsPage /></MainLayout></ProtectedRoute>} />
68
- <Route path="/alerts" element={<ProtectedRoute><MainLayout><InfoPage /></MainLayout></ProtectedRoute>} />
69
- <Route path="/samplepred" element={<ProtectedRoute><MainLayout><SamplePred /></MainLayout></ProtectedRoute>} />
70
- <Route path="/incidents" element={<ProtectedRoute><MainLayout><IncidentsPage /></MainLayout></ProtectedRoute>} />
71
- <Route path="/threats" element={<ProtectedRoute><MainLayout><ThreatIntel /></MainLayout></ProtectedRoute>} />
72
- <Route path="/reports" element={<ProtectedRoute><MainLayout><Reports /></MainLayout></ProtectedRoute>} />
73
- <Route path="/response" element={<ProtectedRoute><MainLayout><ResponsePage /></MainLayout></ProtectedRoute>} />
74
- <Route path="/mlmodels" element={<ProtectedRoute><MainLayout><MlmodelPage /></MainLayout></ProtectedRoute>} />
75
- <Route path="/chat" element={<ProtectedRoute><Aichat /></ProtectedRoute>} />
76
- </Routes>
77
- </FeatureGuard>
78
- </main>
79
- </div>
80
- );
81
- }
82
-
83
- export default function App() {
84
- return (
85
- <AuthProvider>
86
- <LiveDataProvider>
87
- <Router>
88
- <AppLayout />
89
- </Router>
90
- </LiveDataProvider>
91
- </AuthProvider>
92
- );
93
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/api.js DELETED
@@ -1,183 +0,0 @@
1
- // ===============================================
2
- // 🔐 API Layer for Adaptive AI NIDS Frontend
3
- // ===============================================
4
-
5
- const BASE_URL =
6
- import.meta.env.VITE_API_URL || "http://127.0.0.1:5000";
7
-
8
- // Safe fetch wrapper
9
- async function safeFetch(url, options = {}, timeout = 10000, retries = 1) {
10
- const controller = new AbortController();
11
- const id = setTimeout(() => controller.abort(), timeout);
12
-
13
- try {
14
- const res = await fetch(url, { ...options, signal: controller.signal });
15
-
16
- if (!res.ok) {
17
- const errText = await res.text();
18
- throw new Error(`HTTP ${res.status}: ${errText}`);
19
-     }
20
-     return await res.json();
21
-   } catch (err) {
22
-     console.error(`❌ API Error [${url}]:`, err.message);
23
-
24
-     if (retries > 0 && url.includes("/geo/")) {
25
-       await new Promise((r) => setTimeout(r, 1500));
26
-       return safeFetch(url, options, timeout * 1.5, retries - 1);
27
-     }
28
-
29
-     return { error: err.message };
30
-   } finally {
31
-     clearTimeout(id);
32
-   }
33
- }
34
-
35
- // -------------------------------------------------------------
36
- // 🚀 LIVE CAPTURE
37
- // -------------------------------------------------------------
38
- export async function startSniffer(iface = null) {
39
-   const q = iface ? `?iface=${iface}` : "";
40
-   return safeFetch(`${BASE_URL}/api/live/start${q}`);
41
- }
42
-
43
- export async function stopSniffer() {
44
-   return safeFetch(`${BASE_URL}/api/live/stop`);
45
- }
46
-
47
- export async function getStatus() {
48
-   return safeFetch(`${BASE_URL}/api/live/status`);
49
- }
50
-
51
- // MODEL-AWARE 🎯
52
- export async function getRecent(model, limit = 300) {
53
-   const res = await safeFetch(`${BASE_URL}/api/live/recent?model=${model}`);
54
-
55
-   if (res?.events && Array.isArray(res.events)) {
56
-     res.events = res.events.slice(-limit);
57
-   }
58
-   return res;
59
- }
60
-
61
- // MODEL-AWARE 🎯
62
- export async function getStats(model) {
63
-   return safeFetch(`${BASE_URL}/api/live/stats?model=${model}`);
64
- }
65
-
66
- // -------------------------------------------------------------
67
- // 🧾 LOGS (MODEL-AWARE)
68
- // -------------------------------------------------------------
69
- export function download_logs(model) {
70
-   window.location.href = `${BASE_URL}/api/logs/download?model=${model}`;
71
- }
72
-
73
- export async function clearLogs(model, n = 50) {
74
-   return safeFetch(
75
-     `${BASE_URL}/api/logs/clear?model=${model}&n=${n}`,
76
-     { method: "POST" }
77
-   );
78
- }
79
-
80
- export async function clearByPrediction(model, pred) {
81
-   return safeFetch(
82
-     `${BASE_URL}/api/logs/clear_pred?model=${model}&pred=${pred}`,
83
-     { method: "POST" }
84
-   );
85
- }
86
-
87
- export async function deleteOne(model, index) {
88
-   return safeFetch(
89
-     `${BASE_URL}/api/logs/delete_one?model=${model}&index=${index}`,
90
-     { method: "POST" }
91
-   );
92
- }
93
-
94
- // -------------------------------------------------------------
95
- // 🌍 GEO + ALERTS
96
- // -------------------------------------------------------------
97
- export async function getGeoData() {
98
-   return safeFetch(`${BASE_URL}/api/geo/recent`, {}, 20000, 2);
99
- }
100
-
101
- export async function getAlerts() {
102
-   return safeFetch(`${BASE_URL}/api/alerts`);
103
- }
104
-
105
- // -------------------------------------------------------------
106
- // 🧩 MODEL CONTROL
107
- // -------------------------------------------------------------
108
- export async function getActiveModel() {
109
-   return safeFetch(`${BASE_URL}/api/model/active`);
110
- }
111
-
112
- export async function switchModel(model) {
113
-   return safeFetch(`${BASE_URL}/api/model/select`, {
114
-     method: "POST",
115
-     headers: { "Content-Type": "application/json" },
116
-     body: JSON.stringify({ model }),
117
-   });
118
- }
119
-
120
- export async function getModelHealth() {
121
-   return safeFetch(`${BASE_URL}/api/model/health`);
122
- }
123
-
124
-
125
- // -------------------------------------------------------------
126
- // 🤖 AI HELPERS
127
- // -------------------------------------------------------------
128
- export async function explainThreat(event) {
129
-   return safeFetch(`${BASE_URL}/api/ai/explain`, {
130
-     method: "POST",
131
-     headers: { "Content-Type": "application/json" },
132
-     body: JSON.stringify(event),
133
-   });
134
- }
135
-
136
- export async function getAISummary(model, n = 200) {
137
-   return safeFetch(
138
-     `${BASE_URL}/api/ai/summary?model=${encodeURIComponent(model)}&n=${n}`
139
-   );
140
- }
141
-
142
-
143
- export async function sendMessageToAI(message) {
144
-   try {
145
-     const res = await fetch(`${BASE_URL}/api/chat`, {
146
-       method: "POST",
147
-       headers: { "Content-Type": "application/json" },
148
-       body: JSON.stringify({ message })
149
-     });
150
-
151
-     if (!res.ok) throw new Error("Chat API failed");
152
-     return res.json();
153
-   } catch (err) {
154
-     console.error("Chat error:", err);
155
-     return { reply: "⚠ AI Assistant not responding." };
156
-   }
157
- }
158
-
159
- // ➤ Offline CSV/PCAP prediction
160
- export const offlinePredictAPI = async (file, model) => {
161
-   const formData = new FormData();
162
-   formData.append("file", file);
163
-   formData.append("model", model);
164
-
165
-   // FIX: Using BASE_URL for live deployment
166
-   const res = await fetch(`${BASE_URL}/api/offline/predict`, {
167
-     method: "POST",
168
-     body: formData,
169
-   });
170
-
171
-   return res.json();
172
- };
173
-
174
- // ➤ Get PDF forensic report download link
175
- export const downloadOfflineReport = () => {
176
- // FIX: Using BASE_URL for live deployment
177
- window.open(`${BASE_URL}/api/offline/report`, "_blank");
178
- };
179
-
180
-
181
- // -------------------------------------------------------------
182
- export { BASE_URL };
183
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/assests/Dos-HTTPtest.jpg DELETED
Binary file (65.9 kB)
 
frontend/src/assests/Dos_Attacks-hulk.jpeg DELETED
Binary file (10.9 kB)
 
frontend/src/assests/HOIC.jpeg DELETED
Binary file (11.1 kB)
 
frontend/src/assests/LOic.jpeg DELETED
Binary file (6.24 kB)
 
frontend/src/assests/heroBlocks.svg DELETED
frontend/src/assests/sql.jpeg DELETED
Binary file (7.92 kB)
 
frontend/src/assests/vpn.jpg DELETED
Binary file (15.7 kB)
 
frontend/src/assests/web.jpeg DELETED
Binary file (10.2 kB)
 
frontend/src/components/Alerts.jsx DELETED
@@ -1,36 +0,0 @@
1
- import React, { useContext } from "react";
2
- import { AlertTriangle, ShieldAlert, Info } from "lucide-react";
3
- import { AlertContext } from "../context/AlertContext.jsx";
4
-
5
- export default function Alerts() {
6
- const { alerts, removeAlert } = useContext(AlertContext);
7
-
8
- const COLORS = {
9
- info: "border-blue-400/40 bg-blue-500/10 text-blue-300",
10
- warn: "border-yellow-400/40 bg-yellow-500/10 text-yellow-300",
11
- danger: "border-red-400/40 bg-red-500/10 text-red-300",
12
- };
13
-
14
- const ICONS = {
15
- info: <Info size={16} />,
16
- warn: <AlertTriangle size={16} />,
17
- danger: <ShieldAlert size={16} />,
18
- };
19
-
20
- return (
21
- <div className="fixed bottom-4 right-4 z-[9999] space-y-2 w-64 max-h-96 overflow-y-auto pointer-events-none">
22
- {alerts.map(a => (
23
- <div
24
- key={a.id}
25
- className={`pointer-events-auto cyber-card flex items-center gap-2 px-3 py-2
26
- border rounded-xl shadow-neon animate-fade
27
- ${COLORS[a.type]}`}
28
- onClick={() => removeAlert(a.id)}
29
- >
30
- {ICONS[a.type]}
31
- <span className="text-xs">{a.msg}</span>
32
- </div>
33
- ))}
34
- </div>
35
- );
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ConstellationBackground.jsx DELETED
@@ -1,85 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- export default function ConstellationBackground() {
4
- const canvasRef = useRef(null);
5
-
6
- useEffect(() => {
7
- const canvas = canvasRef.current;
8
- const ctx = canvas.getContext("2d");
9
-
10
- let particles = [];
11
- const count = 140; // number of stars
12
-
13
- // resize canvas
14
- function resize() {
15
- canvas.width = window.innerWidth;
16
- canvas.height = window.innerHeight;
17
- }
18
- resize();
19
- window.addEventListener("resize", resize);
20
-
21
- // create particles
22
- for (let i = 0; i < count; i++) {
23
- particles.push({
24
- x: Math.random() * canvas.width,
25
- y: Math.random() * canvas.height,
26
- vx: (Math.random() - 0.5) * 0.3,
27
- vy: (Math.random() - 0.5) * 0.3,
28
- size: Math.random() * 2 + 1,
29
- });
30
- }
31
-
32
- // animation loop
33
- function animate() {
34
- ctx.clearRect(0, 0, canvas.width, canvas.height);
35
-
36
- // draw particles
37
- particles.forEach((p) => {
38
- p.x += p.vx;
39
- p.y += p.vy;
40
-
41
- // soft bounce
42
- if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
43
- if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
44
-
45
- ctx.beginPath();
46
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
47
- ctx.fillStyle = "rgba(0, 220, 255, 0.9)";
48
- ctx.shadowBlur = 10;
49
- ctx.shadowColor = "#66e0ff";
50
- ctx.fill();
51
- });
52
-
53
- // draw connecting lines
54
- for (let i = 0; i < count; i++) {
55
- for (let j = i + 1; j < count; j++) {
56
- const a = particles[i];
57
- const b = particles[j];
58
- const dist = Math.hypot(a.x - b.x, a.y - b.y);
59
-
60
- if (dist < 140) {
61
- ctx.strokeStyle = `rgba(120, 255, 255, ${1 - dist / 140})`;
62
- ctx.lineWidth = 0.7;
63
- ctx.beginPath();
64
- ctx.moveTo(a.x, a.y);
65
- ctx.lineTo(b.x, b.y);
66
- ctx.stroke();
67
- }
68
- }
69
- }
70
-
71
- requestAnimationFrame(animate);
72
- }
73
-
74
- animate();
75
-
76
- return () => window.removeEventListener("resize", resize);
77
- }, []);
78
-
79
- return (
80
- <canvas
81
- ref={canvasRef}
82
- className="fixed inset-0 -z-10 pointer-events-none"
83
- />
84
- );
85
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/FeatureGuard.jsx DELETED
@@ -1,63 +0,0 @@
1
- // FeatureGuard.jsx
2
- import React, { useState, useEffect } from "react";
3
- import { AlertTriangle, Github, Monitor, Terminal } from "lucide-react";
4
-
5
- const FeatureGuard = ({ children, requireLocal = false }) => {
6
- const [isLocal, setIsLocal] = useState(true);
7
- const [showPopup, setShowPopup] = useState(false);
8
-
9
- useEffect(() => {
10
- // Detect environment
11
- const local = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1";
12
- setIsLocal(local);
13
-
14
- // Only show popup if it's required AND we are not local
15
- if (requireLocal && !local) {
16
- setShowPopup(true);
17
- }
18
- }, [requireLocal]);
19
-
20
- // If we are local, or the feature doesn't require local access, show content normally
21
- if (isLocal || !requireLocal) return <>{children}</>;
22
-
23
- return (
24
- <div className="relative min-h-screen">
25
- {/* Background content is blurred for "Demo" look */}
26
- <div className="blur-lg pointer-events-none opacity-50">
27
- {children}
28
- </div>
29
-
30
- {showPopup && (
31
- <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/70 backdrop-blur-md p-4">
32
- <div className="bg-[#0b1120] border-2 border-cyan-500/50 rounded-2xl p-8 max-w-lg w-full shadow-[0_0_50px_rgba(6,182,212,0.4)]">
33
- <div className="flex items-center gap-3 text-amber-400 mb-4">
34
- <AlertTriangle size={28} />
35
- <h2 className="text-2xl font-bold">Local Agent Required</h2>
36
- </div>
37
-
38
- <p className="text-gray-300 mb-6">
39
- This page requires <b>Live Network Sniffing</b>. To protect privacy, browsers cannot access your hardware. Please run the project locally.
40
- </p>
41
-
42
- <div className="bg-black/40 rounded-lg p-4 font-mono text-sm border border-cyan-900/50 mb-6">
43
- <p className="text-emerald-400">$ git clone [Your-Repo-URL]</p>
44
- <p className="text-blue-400">$ cd nids-project</p>
45
- <p className="text-purple-400">$ python app.py --mode live</p>
46
- </div>
47
-
48
- <div className="flex gap-3">
49
- <button onClick={() => window.open('https://github.com/your-repo', '_blank')} className="flex-1 bg-cyan-600 py-3 rounded-xl font-bold hover:bg-cyan-500 transition-all flex items-center justify-center gap-2">
50
- <Github size={18}/> GitHub
51
- </button>
52
- <button onClick={() => window.history.back()} className="flex-1 bg-gray-800 py-3 rounded-xl font-bold hover:bg-gray-700 transition-all text-gray-400">
53
- Go Back
54
- </button>
55
- </div>
56
- </div>
57
- </div>
58
- )}
59
- </div>
60
- );
61
- };
62
-
63
- export default FeatureGuard;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/Header.jsx DELETED
@@ -1,16 +0,0 @@
1
- // src/components/Header.jsx
2
- import React from "react";
3
- import { useAuth } from "../context/AuthContext";
4
-
5
- export default function Header() {
6
- const { user, logout } = useAuth();
7
- return (
8
- <div className="flex items-center justify-between p-3">
9
- <div className="text-cyan-300 font-bold">NIDS Cyber Defense</div>
10
- <div className="flex items-center gap-3">
11
- <div className="text-sm text-slate-300">{user?.email}</div>
12
- <button onClick={logout} className="px-3 py-1 bg-rose-500/10 rounded">Logout</button>
13
- </div>
14
- </div>
15
- );
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/IPInfoModal.jsx DELETED
@@ -1,182 +0,0 @@
1
- import React, { useEffect, useState } from "react";
2
-
3
- export default function IPInfoModal({ ip, onClose }) {
4
- const [info, setInfo] = useState(null);
5
- const [loading, setLoading] = useState(true);
6
- const [risk, setRisk] = useState({
7
- level: "Unknown",
8
- score: 0,
9
- reason: "Unverified",
10
- });
11
-
12
- useEffect(() => {
13
- if (!ip) return;
14
-
15
- // Reset state for new IP
16
- setInfo(null);
17
- setRisk({ level: "Unknown", score: 0, reason: "Unverified" });
18
- setLoading(true);
19
-
20
- // ✅ Private / Local IP detection
21
- const privateRanges = [
22
- /^127\./,
23
- /^10\./,
24
- /^192\.168\./,
25
- /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
26
- /^239\./, // multicast
27
- ];
28
-
29
- if (privateRanges.some((r) => r.test(ip))) {
30
- setInfo({
31
- city: "Local Network",
32
- region: "Private or Multicast Range",
33
- country_name: "N/A",
34
- org: "Local Device",
35
- asn: "LAN",
36
- version: "IPv4",
37
- latitude: "-",
38
- longitude: "-",
39
- });
40
- setRisk({
41
- level: "Low",
42
- score: 5,
43
- reason: "Private / local or multicast IP",
44
- });
45
- setLoading(false);
46
- return;
47
- }
48
-
49
- // ✅ Backend lookup (new route)
50
- fetch(`http://127.0.0.1:5000/api/geo/resolve?ip=${ip}`)
51
- .then(async (res) => {
52
- if (!res.ok) {
53
- const errData = await res.json().catch(() => ({}));
54
- throw new Error(errData.error || "Lookup failed");
55
- }
56
- return res.json();
57
- })
58
- .then((data) => {
59
- setInfo(data);
60
- setRisk({
61
- level:
62
- data.country === "Local"
63
- ? "Low"
64
- : data.country === "Unknown"
65
- ? "Medium"
66
- : "Low",
67
- score: data.country === "Unknown" ? 40 : 15,
68
- reason: `Detected in ${data.country || "Unknown"}`,
69
- });
70
- })
71
- .catch((err) => {
72
- console.warn("⚠️ Geo lookup failed:", err.message);
73
- setInfo({
74
- city: "Unknown",
75
- country_name: "Unknown",
76
- org: "No data",
77
- latitude: "-",
78
- longitude: "-",
79
- });
80
- setRisk({
81
- level: "Low",
82
- score: 10,
83
- reason: "No threat detected / lookup failed",
84
- });
85
- })
86
- .finally(() => setLoading(false));
87
- }, [ip]);
88
-
89
- if (!ip) return null;
90
-
91
- // ✅ Helper for dynamic color
92
- const riskColor =
93
- risk.level === "High"
94
- ? "text-red-400"
95
- : risk.level === "Medium"
96
- ? "text-yellow-400"
97
- : "text-green-400";
98
-
99
- const riskBarColor =
100
- risk.level === "High"
101
- ? "bg-red-500"
102
- : risk.level === "Medium"
103
- ? "bg-yellow-400"
104
- : "bg-green-400";
105
-
106
- return (
107
- <div className="fixed inset-0 bg-black/70 flex justify-center items-center z-50">
108
- <div className="bg-cyber-panel/90 border border-cyan-400/30 rounded-xl p-5 w-96 text-slate-200 shadow-lg relative">
109
- <h2 className="text-lg font-semibold text-cyan-400 mb-3">
110
- 🌐 IP Info — {ip}
111
- </h2>
112
-
113
- {loading ? (
114
- <p className="text-slate-400 text-sm animate-pulse">
115
- Fetching IP intelligence...
116
- </p>
117
- ) : info ? (
118
- <div className="space-y-2 text-sm">
119
- <p>
120
- <b>City:</b> {info.city || "Unknown"}
121
- </p>
122
- <p>
123
- <b>Region:</b> {info.region || "Unknown"}
124
- </p>
125
- <p>
126
- <b>Country:</b> {info.country_name || "Unknown"}
127
- </p>
128
- <p>
129
- <b>ISP:</b> {info.org || "Unknown"}
130
- </p>
131
- <p>
132
- <b>ASN:</b> {info.asn || "N/A"}
133
- </p>
134
- <p>
135
- <b>IP Type:</b> {info.version}
136
- </p>
137
- <p>
138
- <b>Coordinates:</b> {info.latitude}, {info.longitude}
139
- </p>
140
- </div>
141
- ) : (
142
- <p className="text-red-400 text-sm">
143
- Could not fetch IP intelligence data.
144
- </p>
145
- )}
146
-
147
- {/* Risk / Threat assessment */}
148
- {!loading && (
149
- <div className="mt-4 border-t border-cyan-400/10 pt-3">
150
- <h4 className="text-sm text-slate-300 mb-1">Threat Assessment</h4>
151
- <div className="flex items-center justify-between">
152
- <span className={`text-sm font-semibold ${riskColor}`}>
153
- {risk.level} Risk ({risk.score}%)
154
- </span>
155
- <span className="text-xs text-slate-400 max-w-[180px] text-right">
156
- {risk.reason}
157
- </span>
158
- </div>
159
-
160
- {/* Risk Progress Bar */}
161
- <div className="w-full bg-slate-700/40 rounded-full mt-2 h-2 overflow-hidden">
162
- <div
163
- className={`h-2 rounded-full ${riskBarColor}`}
164
- style={{ width: `${risk.score}%` }}
165
- />
166
- </div>
167
- </div>
168
- )}
169
-
170
- {/* Close button */}
171
- <button
172
- onClick={onClose}
173
- className="mt-5 px-3 py-2 bg-cyan-500/20 hover:bg-cyan-500/30 rounded-lg border border-cyan-400/30 w-full text-sm"
174
- >
175
- Close
176
- </button>
177
- </div>
178
- </div>
179
- );
180
- }
181
-
182
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/LoadingOverlay.jsx DELETED
@@ -1,12 +0,0 @@
1
- // src/components/LoadingOverlay.jsx
2
- export default function LoadingOverlay({ show, message = "Processing..." }) {
3
- if (!show) return null;
4
- return (
5
- <div className="fixed inset-0 bg-black/70 backdrop-blur-sm flex items-center justify-center z-50">
6
- <div className="text-center">
7
- <div className="animate-spin rounded-full h-12 w-12 border-t-4 border-cyan-400 mb-3 mx-auto"></div>
8
- <p className="text-cyan-300 font-semibold">{message}</p>
9
- </div>
10
- </div>
11
- );
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/MainLayout.jsx DELETED
@@ -1,13 +0,0 @@
1
- import React from "react";
2
- import Navbar from "./Navbar";
3
- import ChatAssistant from "../pages/ChatAssistant";
4
-
5
- export default function MainLayout({ children }) {
6
- return (
7
- <div className="relative">
8
- <Navbar />
9
- {children}
10
- <ChatAssistant />
11
- </div>
12
- );
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/Navbar.jsx DELETED
@@ -1,128 +0,0 @@
1
- import React, { useState } from "react";
2
- import { Shield, Search, User, ChevronDown, RefreshCcw, LogOut, CurrencyIcon } from "lucide-react";
3
- import { useAuth } from "../context/AuthContext";
4
- import { useLocation, useNavigate } from "react-router-dom";
5
-
6
- export default function Navbar({ onRefresh }) {
7
- const { user, logout } = useAuth();
8
- const navigate = useNavigate();
9
- const location = useLocation();
10
-
11
- const [searchQuery, setSearchQuery] = useState("");
12
- const [profileOpen, setProfileOpen] = useState(false);
13
-
14
- return (
15
- <nav className="relative z-20 border-b border-accent/20 backdrop-blur-xl py-4 shadow-[0_4px_20px_rgba(0,0,0,0.45)]">
16
- <div className="container mx-auto px-4 py-3 mb-2 flex items-center gap-6">
17
-
18
- {/* Brand */}
19
- <button onClick={() => navigate("/")} className="flex items-center gap-3 group">
20
- <div className="p-2 rounded-xl border border-accent/50 bg-accent/10 shadow-neon group-hover:bg-accent/20 transition">
21
- <CurrencyIcon size={20} className="text-accent" />
22
- </div>
23
- <div className="flex flex-col leading-tight">
24
- <span className="text-sm font-semibold text-accent tracking-[0.22em] uppercase">
25
- ASTRAGUARD
26
- </span>
27
- <span className="text-[11px] text-slate-400 uppercase tracking-[0.18em]">
28
- AI • NIDS
29
- </span>
30
- </div>
31
- </button>
32
-
33
- {/* Main Nav */}
34
- <div className="hidden lg:flex items-center gap-4 text-sm text-slate-300 ml-4">
35
-
36
- {[
37
- { path: "/", label: "Dashboard" },
38
- { path: "/livetraffic", label: "Live Traffic" },
39
- { path: "/flow", label: "Flow Analyzer" },
40
- { path: "/alerts", label: "Alerts" },
41
- { path: "/threats", label: "Threat Intel" },
42
- { path: "/reports", label: "Reports" },
43
- { path: "/system", label: "System" },
44
- { path: "/mlmodels", label: "ML Models" },
45
- ].map((item, i) => (
46
- <button
47
- key={i}
48
- onClick={() => navigate(item.path)}
49
- className={`px-3 py-1.5 rounded-full transition-all duration-200 ${
50
- location.pathname === item.path
51
- ? "text-accent bg-accent/10 shadow-[0_0_10px_rgba(0,229,255,0.35)]"
52
- : "text-slate-300 hover:text-accent hover:bg-accent/10"
53
- }`}
54
- >
55
- {item.label}
56
- </button>
57
- ))}
58
- </div>
59
-
60
- {/* Right side */}
61
- <div className="ml-auto flex items-center gap-4">
62
-
63
- {/* Search */}
64
- <div className="hidden md:flex items-center gap-2 px-3 py-1.5 rounded-full border border-accent/40 bg-white/5 focus-within:ring-1 focus-within:ring-accent/60 transition">
65
- <Search size={16} className="text-accent/80" />
66
- <input
67
- type="text"
68
- value={searchQuery}
69
- onChange={(e) => setSearchQuery(e.target.value)}
70
- placeholder="Search dashboard..."
71
- className="bg-transparent text-xs text-slate-200 placeholder:text-slate-500 focus:outline-none w-40"
72
- />
73
- </div>
74
-
75
- {/* Profile */}
76
- <div className="relative">
77
- <button
78
- onClick={() => setProfileOpen(!profileOpen)}
79
- className="flex items-center gap-2 px-2 py-1.5 rounded-full border border-accent/40 bg-white/5 hover:bg-accent/10 transition"
80
- >
81
- <div className="w-8 h-8 rounded-full border border-accent/40 bg-accent/10 flex items-center justify-center">
82
- <User size={16} className="text-accent" />
83
- </div>
84
- <div className="hidden sm:flex flex-col items-start max-w-[130px]">
85
- <span className="text-[10px] text-slate-400 uppercase tracking-[0.16em]">
86
- Operator
87
- </span>
88
- <span className="text-xs text-accent font-medium truncate">
89
- {user?.displayName || "Analyst"}
90
- </span>
91
- </div>
92
- <ChevronDown size={14} className="text-slate-400" />
93
- </button>
94
-
95
- {profileOpen && (
96
- <div className="absolute right-0 mt-2 w-44 rounded-xl border border-accent/30 bg-[#020617]/95 shadow-lg py-1 text-sm z-40">
97
- {onRefresh && (
98
- <>
99
- <button
100
- onClick={() => {
101
- onRefresh();
102
- setProfileOpen(false);
103
- }}
104
- className="w-full flex items-center gap-2 px-3 py-2 hover:bg-accent/10 text-slate-200"
105
- >
106
- <RefreshCcw size={14} className="text-accent" />
107
- <span>Reload data</span>
108
- </button>
109
- <div className="border-t border-slate-700/60 my-1" />
110
- </>
111
- )}
112
-
113
- <button
114
- onClick={() => logout()}
115
- className="w-full flex items-center gap-2 px-3 py-2 hover:bg-red-500/15 text-red-300"
116
- >
117
- <LogOut size={14} />
118
- <span>Logout</span>
119
- </button>
120
- </div>
121
- )}
122
- </div>
123
-
124
- </div>
125
- </div>
126
- </nav>
127
- );
128
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/NeonShield.jsx DELETED
@@ -1,87 +0,0 @@
1
- import { Canvas, useFrame } from "@react-three/fiber";
2
- import * as THREE from "three";
3
- import { useRef } from "react";
4
-
5
- /* ------------- Rotating Packet Ring ------------- */
6
- function PacketRing() {
7
- const ring = useRef();
8
- const pulse = useRef();
9
-
10
- useFrame(({ clock }) => {
11
- const t = clock.getElapsedTime();
12
-
13
- ring.current.rotation.y = t * 0.6;
14
-
15
- const r = 1.5;
16
- pulse.current.position.x = r * Math.cos(t * 1.5);
17
- pulse.current.position.y = r * Math.sin(t * 1.5);
18
- });
19
-
20
- return (
21
- <group>
22
- <mesh ref={ring} rotation={[0.5, 0.4, 0]}>
23
- <torusGeometry args={[1.5, 0.07, 32, 100]} />
24
- <meshStandardMaterial
25
- emissive="#00e5ff"
26
- emissiveIntensity={1.7}
27
- color="#00e5ff"
28
- />
29
- </mesh>
30
-
31
- <mesh ref={pulse}>
32
- <sphereGeometry args={[0.13, 32, 32]} />
33
- <meshStandardMaterial emissive="#a855f7" emissiveIntensity={2} />
34
- </mesh>
35
- </group>
36
- );
37
- }
38
-
39
- /* ------------- Floating Network Nodes ------------- */
40
- function NetworkNodes() {
41
- const group = useRef();
42
-
43
- useFrame(({ clock }) => {
44
- group.current.rotation.y = clock.getElapsedTime() * 0.3;
45
- });
46
-
47
- const nodes = Array.from({ length: 12 }).map(() => [
48
- (Math.random() - 0.5) * 2,
49
- (Math.random() - 0.5) * 1.5,
50
- (Math.random() - 0.5) * 1.2,
51
- ]);
52
-
53
- return (
54
- <group ref={group}>
55
- {nodes.map((pos, i) => (
56
- <mesh position={pos} key={i}>
57
- <sphereGeometry args={[0.1, 24, 24]} />
58
- <meshStandardMaterial
59
- emissive="#00e5ff"
60
- emissiveIntensity={1.3}
61
- color="#00e5ff"
62
- />
63
- </mesh>
64
- ))}
65
- </group>
66
- );
67
- }
68
-
69
- /* ------------- Main Scene ------------- */
70
- export default function NIDS3D() {
71
- return (
72
- <Canvas
73
- style={{ width: "100%", height: "100%" }}
74
- camera={{ position: [2.5, 2.5, 5], fov: 30 }}
75
- >
76
- <ambientLight intensity={0.5} />
77
- <pointLight position={[3, 3, 3]} intensity={1} />
78
-
79
- <group rotation={[0.5, 0.4, 0]}>
80
- <PacketRing />
81
- <NetworkNodes />
82
- </group>
83
- </Canvas>
84
- );
85
- }
86
-
87
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ProtectedRoute.jsx DELETED
@@ -1,10 +0,0 @@
1
- // src/components/ProtectedRoute.jsx
2
- import React from "react";
3
- import { Navigate } from "react-router-dom";
4
- import { useAuth } from "../context/AuthContext";
5
-
6
- export default function ProtectedRoute({ children }) {
7
- const { user, authLoading } = useAuth();
8
- if (authLoading) return null; // or a spinner
9
- return user ? children : <Navigate to="/login" replace />;
10
- }
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/Sidebar.jsx DELETED
@@ -1,324 +0,0 @@
1
- // src/components/Sidebar.jsx
2
- import React, { useEffect, useRef, useState } from "react";
3
- import { NavLink } from "react-router-dom";
4
- import {
5
- Activity,
6
- AlertTriangle,
7
- FileText,
8
- Info,
9
- Settings,
10
- TrafficCone,
11
- MonitorCog,
12
- GitBranchMinus,
13
- BrickWallShield,
14
- ChevronLeft,
15
- ChevronRight,
16
- MoonStar,
17
- LayoutDashboard,
18
- } from "lucide-react";
19
-
20
- /**
21
- * Sidebar with mini-constellation canvas (B3)
22
- *
23
- * Props:
24
- * - collapsed: boolean
25
- * - setCollapsed: function
26
- */
27
- export default function Sidebar({ collapsed, setCollapsed }) {
28
- const sidebarRef = useRef(null);
29
- const canvasRef = useRef(null);
30
- const [time, setTime] = useState("");
31
-
32
- // clock tick
33
- useEffect(() => {
34
- const tick = () => {
35
- const now = new Date();
36
- setTime(
37
- now.toLocaleTimeString("en-IN", {
38
- hour12: true,
39
- hour: "2-digit",
40
- minute: "2-digit",
41
- second: "2-digit",
42
- })
43
- );
44
- };
45
- tick();
46
- const id = setInterval(tick, 1000);
47
- return () => clearInterval(id);
48
- }, []);
49
-
50
- // mini-constellation canvas (node+line) - optimized for sidebar
51
- useEffect(() => {
52
- const canvas = canvasRef.current;
53
- const sidebar = sidebarRef.current;
54
- if (!canvas || !sidebar) return;
55
-
56
- const ctx = canvas.getContext("2d", { alpha: true });
57
- let width = sidebar.clientWidth;
58
- let height = sidebar.clientHeight;
59
- let rafId = null;
60
- let particles = [];
61
- const COUNT = Math.max(18, Math.floor((width * height) / 10000)); // scale nodes by size
62
-
63
- // Resize handler
64
- function resize() {
65
- width = sidebar.clientWidth;
66
- height = sidebar.clientHeight;
67
- const dpr = Math.max(1, window.devicePixelRatio || 1);
68
- canvas.width = Math.floor(width * dpr);
69
- canvas.height = Math.floor(height * dpr);
70
- canvas.style.width = `${width}px`;
71
- canvas.style.height = `${height}px`;
72
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
73
- // recreate particles to adapt density
74
- initParticles();
75
- }
76
-
77
- function rand(min, max) {
78
- return Math.random() * (max - min) + min;
79
- }
80
-
81
- function initParticles() {
82
- particles = [];
83
- const n = COUNT;
84
- for (let i = 0; i < n; i++) {
85
- particles.push({
86
- x: rand(10, width - 10),
87
- y: rand(10, height - 10),
88
- vx: rand(-0.15, 0.15),
89
- vy: rand(-0.15, 0.15),
90
- size: rand(0.8, 2.2),
91
- hue: rand(170, 200), // cyan-ish
92
- });
93
- }
94
- }
95
-
96
- function step() {
97
- ctx.clearRect(0, 0, width, height);
98
-
99
- // draw lines between close particles
100
- for (let i = 0; i < particles.length; i++) {
101
- const a = particles[i];
102
- for (let j = i + 1; j < particles.length; j++) {
103
- const b = particles[j];
104
- const dx = a.x - b.x;
105
- const dy = a.y - b.y;
106
- const dist = Math.sqrt(dx * dx + dy * dy);
107
- const MAX = Math.min(120, Math.max(70, (width + height) / 15));
108
- if (dist < MAX) {
109
- const alpha = 0.12 * (1 - dist / MAX);
110
- ctx.beginPath();
111
- ctx.moveTo(a.x, a.y);
112
- ctx.lineTo(b.x, b.y);
113
- ctx.strokeStyle = `rgba(40,220,210, ${alpha.toFixed(3)})`;
114
- ctx.lineWidth = 0.6;
115
- ctx.stroke();
116
- }
117
- }
118
- }
119
-
120
- // draw nodes
121
- for (let p of particles) {
122
- // move
123
- p.x += p.vx;
124
- p.y += p.vy;
125
-
126
- // gentle wrap/bounce
127
- if (p.x < -6) p.x = width + 6;
128
- if (p.x > width + 6) p.x = -6;
129
- if (p.y < -6) p.y = height + 6;
130
- if (p.y > height + 6) p.y = -6;
131
-
132
- // glow circle
133
- ctx.beginPath();
134
- const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 6);
135
- grad.addColorStop(0, `rgba(0,230,255,0.95)`);
136
- grad.addColorStop(0.2, `rgba(0,230,255,0.55)`);
137
- grad.addColorStop(1, `rgba(0,230,255,0.02)`);
138
- ctx.fillStyle = grad;
139
- ctx.arc(p.x, p.y, p.size * 3.2, 0, Math.PI * 2);
140
- ctx.fill();
141
-
142
- // center bright dot
143
- ctx.beginPath();
144
- ctx.fillStyle = `rgba(220,255,255,0.95)`;
145
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
146
- ctx.fill();
147
- }
148
-
149
- rafId = requestAnimationFrame(step);
150
- }
151
-
152
- // ResizeObserver to respond to sidebar size changes (collapse/expand)
153
- const ro = new ResizeObserver(resize);
154
- ro.observe(sidebar);
155
-
156
- resize();
157
- step(); // start animation
158
-
159
- return () => {
160
- if (rafId) cancelAnimationFrame(rafId);
161
- ro.disconnect();
162
- };
163
- // eslint-disable-next-line react-hooks/exhaustive-deps
164
- }, [sidebarRef, canvasRef, collapsed]);
165
-
166
- // navigation items
167
- const navItems = [
168
- { to: "/", label: "Dashboard", icon: <LayoutDashboard size={18} /> },
169
- { to: "/livetraffic", label: "Live Traffic", icon: <Activity size={18} /> },
170
- { to: "/threats", label: "Threat Intelligence", icon: <Info size={18} /> },
171
- { to: "/alerts", label: "Alerts", icon: <AlertTriangle size={18} /> },
172
- { to: "/incidents", label: "Offline Detection", icon: <BrickWallShield size={18} /> },
173
- { to: "/traffic", label: "Traffic Analysis", icon: <TrafficCone size={18} /> },
174
- { to: "/response", label: "Response System", icon: <GitBranchMinus size={18} /> },
175
- { to: "/mlmodels", label: "ML Model", icon: <MoonStar size={18} /> },
176
- { to: "/reports", label: "Reports", icon: <FileText size={18} /> },
177
- { to: "/system", label: "System Info", icon: <MonitorCog size={18} /> },
178
- { to: "/settings", label: "Settings", icon: <Settings size={18} /> },
179
- ];
180
-
181
- return (
182
- <aside
183
- ref={sidebarRef}
184
- aria-label="Main navigation"
185
- className={`fixed top-0 left-0 h-screen
186
- ${collapsed ? "w-20" : "w-64"}
187
- bg-[#030b17]/50 backdrop-blur-xl
188
- flex flex-col z-50 transition-all duration-300
189
- border-r-2 border-[var(--accent)]/14
190
- shadow-[inset_0_0_18px_rgba(0,229,255,0.06)]
191
- `}
192
- >
193
- {/* background canvas (mini constellation) */}
194
- <canvas
195
- ref={canvasRef}
196
- className="pointer-events-none absolute inset-0 -z-10"
197
- aria-hidden="true"
198
- />
199
-
200
- {/* double neon border / decorative rings */}
201
- <div
202
- className="absolute inset-0 pointer-events-none -z-5"
203
- style={{
204
- boxShadow:
205
- "inset 0 0 2px rgba(0,230,255,0.06), inset 0 0 30px rgba(0,230,255,0.03)",
206
- }}
207
- />
208
-
209
- {/* HEADER */}
210
- <div className="p-4 border-b border-[var(--accent)]/12 relative overflow-hidden shrink-0">
211
- <div className="relative z-10 flex items-center justify-between">
212
- <div className="flex items-center gap-2">
213
- <span className="text-[18px] font-extrabold text-[var(--accent)]">⚡</span>
214
- {!collapsed && (
215
- <div>
216
- <div className="text-xl font-bold text-[var(--accent)] leading-tight">NIDS</div>
217
- <div className="text-[11px] text-slate-400">Cyber Defense</div>
218
- </div>
219
- )}
220
- </div>
221
-
222
- <div className="flex items-center gap-2">
223
- <span
224
- className={`w-2.5 h-2.5 rounded-full ${
225
- navigator.onLine ? "bg-emerald-400 animate-pulse" : "bg-rose-500 animate-ping"
226
- }`}
227
- />
228
- {!collapsed && (
229
- <div className="text-xs text-slate-400">{navigator.onLine ? "LIVE" : "OFFLINE"}</div>
230
- )}
231
- </div>
232
- </div>
233
-
234
- {!collapsed && (
235
- <div className="mt-3 text-xs text-slate-400">
236
- <div className="flex justify-between items-center">
237
- <span className="text-[var(--accent)]">Host:</span>
238
- <span className="font-mono text-[var(--accent)] text-right">{window.location.hostname}</span>
239
- </div>
240
- <div className="mt-2 relative h-1.5 bg-gradient-to-r from-transparent to-transparent rounded-full overflow-hidden">
241
- <div className="absolute inset-0 bg-gradient-to-r from-cyan-400 via-emerald-400 to-cyan-400 opacity-30 blur-sm" />
242
- <div className="mt-2 text-[10px] text-slate-500">Monitoring anomalies…</div>
243
- </div>
244
- </div>
245
- )}
246
- </div>
247
-
248
- {/* NAV - scrollable */}
249
- <nav className="flex-1 p-3 overflow-y-auto overflow-x-hidden custom-scroll">
250
- <div className="space-y-1">
251
-
252
- {navItems.map(({ to, label, icon }, index) => (
253
- <div key={to}>
254
-
255
- <NavLink
256
- to={to}
257
- title={collapsed ? label : ""}
258
- className={({ isActive }) =>
259
- `
260
- group flex items-center gap-3 px-3 py-2 rounded-lg transition-all duration-200 text-sm relative
261
- ${isActive
262
- ? "bg-[var(--accent)]/18 border border-[var(--accent)]/28 text-[var(--accent)] shadow-[0_0_10px_rgba(0,230,255,0.06)]"
263
- : "text-slate-300 hover:bg-[var(--accent)]/6 hover:border hover:border-[var(--accent)]/18"}
264
- `
265
- }
266
- >
267
-
268
- <div className="w-5 h-5 flex items-center justify-center text-[var(--accent)]">
269
- {icon}
270
- </div>
271
-
272
- {!collapsed && <span className="truncate">{label}</span>}
273
-
274
- {/* Tooltip when collapsed */}
275
- {collapsed && (
276
- <div className="absolute left-[3.4rem] top-1/2 -translate-y-1/2 opacity-0
277
- group-hover:opacity-100 transition-opacity duration-150 pointer-events-none">
278
- <div className="bg-black/75 text-[var(--accent)] border border-[var(--accent)]/30
279
- px-2 py-1 rounded text-xs whitespace-nowrap shadow-md">
280
- {label}
281
- </div>
282
- </div>
283
- )}
284
-
285
- </NavLink>
286
-
287
- {/* Divider line (between items) */}
288
- {index < navItems.length - 1 && (
289
- <div className="h-px w-full bg-white/100 my-3"></div>
290
- )}
291
-
292
- </div>
293
- ))}
294
-
295
- </div>
296
- </nav>
297
-
298
-
299
- {/* FOOTER */}
300
- <div className="p-3 border-t border-[var(--accent)]/12 shrink-0">
301
- <div className="flex items-center justify-between">
302
- {!collapsed ? (
303
- <>
304
- <div className="text-[10px] text-slate-400">© 2025 Future Lelouch</div>
305
- <div className="text-[11px] text-[var(--accent)] font-mono">{time}</div>
306
- </>
307
- ) : (
308
- <div className="text-[10px] text-slate-400 text-center w-full">© 2025</div>
309
- )}
310
- </div>
311
- </div>
312
-
313
- {/* collapse/expand toggle */}
314
- <button
315
- aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
316
- onClick={() => setCollapsed(!collapsed)}
317
- className="absolute right-[-12px] bottom-6 w-8 h-8 rounded-full bg-[var(--accent)]/12 border border-[var(--accent)]/30 text-[var(--accent)] shadow-[0_6px_18px_rgba(0,230,255,0.06)] flex items-center justify-center transition-transform hover:scale-110"
318
- >
319
- {collapsed ? <ChevronRight size={16} /> : <ChevronLeft size={16} />}
320
- </button>
321
- </aside>
322
- );
323
- }
324
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/TopStatusBar.jsx DELETED
@@ -1,13 +0,0 @@
1
- // src/components/TopStatusBar.jsx
2
- export default function TopStatusBar({ running, packetCount, iface, lastUpdate }) {
3
- return (
4
- <div className="flex items-center justify-between bg-cyan-900/20 px-4 py-2 rounded-lg border border-cyan-400/20 mb-4">
5
- <span className="text-sm text-cyan-300">
6
- {running ? "🟢 Capturing Packets" : "🔴 Capture Stopped"}
7
- </span>
8
- <span className="text-sm text-slate-400">
9
- Packets: <b className="text-cyan-300">{packetCount}</b> | Interface: {iface || "auto"} | Updated: {lastUpdate || "—"}
10
- </span>
11
- </div>
12
- );
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/Controls.jsx DELETED
@@ -1,55 +0,0 @@
1
- import React, { useContext, useState, useEffect } from "react";
2
- import { LiveDataContext } from "../../context/DataContext";
3
- import { startSniffer, stopSniffer, getStatus } from "../../api";
4
-
5
- export default function Controls() {
6
- const { running, setRunning } = useContext(LiveDataContext);
7
- const [loading, setLoading] = useState(false);
8
-
9
- useEffect(() => {
10
- (async () => {
11
- const res = await getStatus();
12
- setRunning(res?.running || false);
13
- })();
14
- }, []);
15
-
16
- const handleStart = async () => {
17
- setLoading(true);
18
- // 🔥 RESET BUFFER on START
19
- window.__EVENT_BUFFER_RESET = true;
20
-
21
- await startSniffer();
22
- setRunning(true);
23
- setLoading(false);
24
- };
25
-
26
- const handleStop = async () => {
27
- setLoading(true);
28
- await stopSniffer();
29
- setRunning(false);
30
- setLoading(false);
31
- };
32
-
33
- return (
34
- <div className="flex gap-4">
35
- <button
36
- onClick={handleStart}
37
- disabled={running || loading}
38
- className={`px-4 py-2 rounded-lg border transition-all ${
39
- running ? "bg-green-600/20 border-green-500 text-green-400" : "border-green-400 text-green-300 hover:bg-green-500/20"
40
- }`}
41
- >
42
- 🚀 Start Capture
43
- </button>
44
- <button
45
- onClick={handleStop}
46
- disabled={!running || loading}
47
- className={`px-4 py-2 rounded-lg border transition-all ${
48
- !running ? "bg-red-500/20 border-red-500 text-red-400" : "border-red-400 text-red-300 hover:bg-red-500/30"
49
- }`}
50
- >
51
- 🛑 Stop Capture
52
- </button>
53
- </div>
54
- );
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/LiveDashboard.jsx DELETED
@@ -1,254 +0,0 @@
1
- import React, { useEffect, useState, useContext, useRef } from "react";
2
- import Controls from "./Controls.jsx";
3
- import LiveTable from "./LiveTable.jsx";
4
- import ChatAssistant from "../../pages/ChatAssistant.jsx";
5
- import StatsPanel from "./StatsPanel.jsx";
6
- import { Download, Loader2, MessageSquare } from "lucide-react";
7
- import { socket } from "../../socket.js";
8
- import {
9
- getRecent,
10
- getStats,
11
- download_logs,
12
- switchModel,
13
- getActiveModel,
14
- getStatus
15
- } from "../../api.js";
16
- import { AlertContext } from "../../context/AlertContext.jsx";
17
- import ThreatFeed from "./ThreatFeed";
18
- import ThreatTimeline from "./ThreatTimeline";
19
- import TopIPs from "./TopIPs";
20
- import TopCountries from "./TopCountries";
21
- import Sparkline from "./Sparkline";
22
-
23
- export default function LiveDashboard() {
24
- const [rows, setRows] = useState([]);
25
- const [stats, setStats] = useState({});
26
- const [connected, setConnected] = useState(false);
27
- const [lastUpdate, setLastUpdate] = useState(null);
28
- const [threatCount, setThreatCount] = useState(0);
29
- const { pushAlert } = useContext(AlertContext);
30
-
31
- const [model, setModel] = useState("bcc");
32
- const [snifferRunning, setSnifferRunning] = useState(false);
33
- const [switching, setSwitching] = useState(false);
34
- const [modelReady, setModelReady] = useState(false);
35
-
36
- const [tableKey, setTableKey] = useState(0);
37
- const eventBufferRef = useRef([]);
38
-
39
- // ============================================================
40
- // INIT LOAD: Active model + sniffer status
41
- // ============================================================
42
- useEffect(() => {
43
- (async () => {
44
- const res = await getActiveModel();
45
- if (res?.model) setModel(res.model);
46
-
47
- const st = await getStatus();
48
- setSnifferRunning(st?.running === true);
49
- })();
50
- }, []);
51
-
52
- // ============================================================
53
- // MODEL SWITCH (backend + UI reset)
54
- // ============================================================
55
- useEffect(() => {
56
- const doSwitch = async () => {
57
- setSwitching(true);
58
- setModelReady(false);
59
-
60
- setRows([]);
61
- setStats({});
62
- setThreatCount(0);
63
- eventBufferRef.current = [];
64
- setTableKey((k) => k + 1);
65
-
66
- const res = await switchModel(model);
67
-
68
- if (!res?.error) {
69
- pushAlert(`${model.toUpperCase()} model activated`, "info");
70
- setModelReady(true);
71
- } else {
72
- pushAlert("Model switch failed!", "danger");
73
- }
74
-
75
- setSwitching(false);
76
- };
77
-
78
- doSwitch();
79
- }, [model]);
80
-
81
- // ============================================================
82
- // INITIAL FETCH (after model switch)
83
- // ============================================================
84
- useEffect(() => {
85
- if (!modelReady) return;
86
- (async () => {
87
- const [r1, r2] = await Promise.all([
88
- getRecent(model),
89
- getStats(model)
90
- ]);
91
-
92
- setRows(Array.isArray(r1?.events) ? r1.events : []);
93
- setStats(r2 || {});
94
- })();
95
- }, [model, modelReady]);
96
-
97
- // ============================================================
98
- // SOCKET LIVE EVENTS
99
- // ============================================================
100
- useEffect(() => {
101
- socket.on("connect", () => setConnected(true));
102
- socket.on("disconnect", () => setConnected(false));
103
-
104
- socket.on("new_event", (payload) => {
105
- if (!modelReady || switching) return;
106
- const events = payload?.items ?? [];
107
- if (events.length) eventBufferRef.current.push(...events);
108
- });
109
-
110
- return () => {
111
- socket.off("connect");
112
- socket.off("disconnect");
113
- socket.off("new_event");
114
- };
115
- }, [modelReady, switching]);
116
-
117
- // ============================================================
118
- // BUFFER TO UI processing
119
- // ============================================================
120
- useEffect(() => {
121
- const interval = setInterval(() => {
122
- if (!modelReady) return;
123
- if (!eventBufferRef.current.length) return;
124
-
125
- const batch = eventBufferRef.current.splice(0, 50);
126
-
127
- setRows((prev) => [...batch, ...prev].slice(0, 180));
128
-
129
- setStats((prev) => {
130
- const next = { ...prev };
131
- batch.forEach((evt) => {
132
- const label = (evt.prediction || "UNKNOWN").toUpperCase();
133
- next[label] = (next[label] || 0) + 1;
134
- });
135
- return next;
136
- });
137
-
138
- batch.forEach((evt) => {
139
- const label = (evt.prediction || "").toUpperCase();
140
- const isThreat = (model === "bcc" && ["TOR","I2P","ZERONET"].includes(label)) ||
141
- (model === "cicids" && label !== "BENIGN");
142
- if (isThreat) setThreatCount((c) => c + 1);
143
- });
144
-
145
- setLastUpdate(new Date().toLocaleTimeString());
146
- }, 1200);
147
-
148
- return () => clearInterval(interval);
149
- }, [model, modelReady]);
150
-
151
- // ============================================================
152
- // AI Assistant Handler (local popup)
153
- // ============================================================
154
- const askAISummary = async () => {
155
- const dangerous = rows.filter(evt => {
156
- const label = (evt.prediction || "").toUpperCase();
157
- return (model === "bcc" && ["TOR","I2P","ZERONET"].includes(label)) ||
158
- (model === "cicids" && label !== "BENIGN");
159
- });
160
-
161
- alert(`AI Summary:
162
- Danger Events: ${dangerous.length}
163
- Recent Threat Types: ${dangerous.slice(0, 5).map(e => e.prediction).join(", ")}`);
164
- };
165
-
166
- // ============================================================
167
- // RENDER UI
168
- // ============================================================
169
- return (
170
- <div className="space-y-5">
171
- <h2 className="py-6 text-5xl md:text-6xl font-extrabold text-transparent bg-gradient-to-r from-cyan-300 via-purple-400 to-pink-400 bg-clip-text">
172
- Live Capture
173
- </h2>
174
-
175
- <div className="p-3 rounded-lg bg-cyan-500/10 border border-cyan-400/30 text-cyan-200">
176
- Active Model: {model.toUpperCase()}
177
- </div>
178
-
179
- {/* Model Toggle */}
180
- <div className="flex gap-3 mb-4">
181
- <button
182
- disabled={snifferRunning || switching}
183
- className={`px-4 py-2 rounded-lg border ${
184
- model === "bcc" ? "bg-cyan-500/20 border-cyan-400/50 text-cyan-300" :
185
- "bg-black/20 border-cyan-400/20 text-slate-400"
186
- }`}
187
- onClick={() => setModel("bcc")}
188
- >
189
- {switching && model === "bcc" && <Loader2 className="animate-spin" size={16} />}
190
- BCC Model
191
- </button>
192
-
193
- <button
194
- disabled={snifferRunning || switching}
195
- className={`px-4 py-2 rounded-lg border ${
196
- model === "cicids" ? "bg-purple-500/20 border-purple-400/50 text-purple-300" :
197
- "bg-black/20 border-purple-400/20 text-slate-400"
198
- }`}
199
- onClick={() => setModel("cicids")}
200
- >
201
- {switching && model === "cicids" && <Loader2 className="animate-spin" size={16} />}
202
- CICIDS Model
203
- </button>
204
-
205
- {/* 🧠 AI Button */}
206
- <button
207
- onClick={askAISummary}
208
- className="px-4 py-2 rounded-lg bg-yellow-500/10 border border-yellow-400/40 text-yellow-300 flex items-center gap-2 hover:bg-yellow-500/20"
209
- >
210
- <MessageSquare size={16} />
211
- AI Assist
212
- </button>
213
- </div>
214
-
215
- <Controls />
216
-
217
- {/* Status + Logs */}
218
- <div className="flex justify-between items-center">
219
- <h3 className="text-xl text-cyan-400 font-semibold">
220
- {model === "bcc" ? "Realtime BCC Monitor" : "Realtime CICIDS Monitor"}
221
- </h3>
222
-
223
- <button
224
- onClick={() => download_logs(model)}
225
- className="px-3 py-2 rounded-xl bg-emerald-500/20 border border-emerald-400/40 text-emerald-300 flex items-center gap-2"
226
- >
227
- <Download size={16} /> Logs
228
- </button>
229
- </div>
230
-
231
- <StatsPanel stats={stats} />
232
-
233
- <LiveTable key={tableKey} rows={rows} />
234
-
235
- <ChatAssistant />
236
-
237
-
238
- {/* VISUAL ANALYTICS BACK ADDED */}
239
- <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mt-6">
240
- <ThreatFeed key={`${tableKey}-feed`} events={rows} />
241
- <ThreatTimeline key={`${tableKey}-timeline`} events={rows} />
242
- <Sparkline key={`${tableKey}-spark`} events={rows} />
243
- <TopIPs key={`${tableKey}-ips`} events={rows} />
244
- <TopCountries key={`${tableKey}-countries`} events={rows} />
245
- </div>
246
-
247
- <p className="text-xs text-slate-400 text-right">
248
- Last packet: <span className="text-cyan-300">{lastUpdate || "Waiting…"}</span>
249
- </p>
250
- </div>
251
- );
252
- }
253
-
254
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/LiveTable.jsx DELETED
@@ -1,168 +0,0 @@
1
- import React, { useState } from "react";
2
- import Badge from "../../ui/Badge";
3
- import { clearLogs, clearByPrediction, deleteOne } from "../../api";
4
- import { Trash } from "lucide-react";
5
- import IPInfoModal from "../IPInfoModal";
6
-
7
- export default function LiveTable({ rows, refresh }) {
8
- const [clearing, setClearing] = useState(false);
9
- const [deleteCount, setDeleteCount] = useState(50);
10
- const [selectedIP, setSelectedIP] = useState(null);
11
-
12
-
13
-
14
- // ✅ Safely clear last N rows
15
- const handleClear = async (n) => {
16
- try {
17
- setClearing(true);
18
- // 🔥 RESET BUFFER on CLEAR
19
- window.__EVENT_BUFFER_RESET = true;
20
- await clearLogs(n);
21
- if (typeof refresh === "function") await refresh();
22
- } catch (err) {
23
- console.error("Clear logs failed:", err);
24
- } finally {
25
- setTimeout(() => setClearing(false), 500);
26
- }
27
- };
28
-
29
- // ✅ Clear logs by prediction type
30
- const handleClearPrediction = async (pred) => {
31
- if (!pred) return;
32
- try {
33
- await clearByPrediction(pred);
34
- if (typeof refresh === "function") await refresh();
35
- } catch (err) {
36
- console.error("Clear by prediction failed:", err);
37
- }
38
- };
39
-
40
- // ✅ Delete a single row safely
41
- const deleteSingleRow = async (index) => {
42
- try {
43
- await deleteOne(index);
44
- if (typeof refresh === "function") await refresh();
45
- } catch (err) {
46
- console.error("Delete single row failed:", err);
47
- }
48
- };
49
-
50
- return (
51
- <div className="cyber-card overflow-hidden transition-all duration-300">
52
- {/* HEADER + ACTIONS */}
53
- <div className="flex items-center justify-between mb-3">
54
- <div>
55
- <h3 className="font-semibold text-lg">Live Events</h3>
56
- <span className="text-xs text-slate-400">
57
- Showing last {rows.length} packets
58
- </span>
59
- </div>
60
-
61
- {/* DELETE CONTROLS */}
62
- <div className="flex items-center gap-2">
63
- {/* DELETE BY PREDICTION */}
64
- <select
65
- onChange={(e) => handleClearPrediction(e.target.value)}
66
- className="bg-cyber-panel/50 border border-cyan-400/20 px-2 py-1 rounded-lg text-xs"
67
- >
68
- <option value="">Clear by Class</option>
69
- <option value="VPN">Clear VPN</option>
70
- <option value="TOR">Clear TOR</option>
71
- <option value="I2P">Clear I2P</option>
72
- <option value="FREENET">Clear FREENET</option>
73
- <option value="ZERONET">Clear ZERONET</option>
74
- </select>
75
-
76
- {/* CLEAR LAST N */}
77
- <button
78
- onClick={() => handleClear(deleteCount)}
79
- className="px-3 py-2 bg-red-500/20 hover:bg-red-500/30 border border-red-500/40
80
- rounded-lg text-xs flex items-center gap-1"
81
- >
82
- <Trash size={14} /> Clear
83
- </button>
84
-
85
- {/* CLEAR ALL */}
86
- <button
87
- onClick={() => handleClear(99999)}
88
- className="px-3 py-2 bg-red-600/30 hover:bg-red-600/40 border border-red-500/50
89
- rounded-lg text-xs"
90
- >
91
- Clear All
92
- </button>
93
- </div>
94
- </div>
95
-
96
- {/* TABLE */}
97
- <div
98
- className={`overflow-auto transition-opacity duration-500 ${
99
- clearing ? "opacity-30" : "opacity-100"
100
- }`}
101
- style={{ maxHeight: "350px" }}
102
- >
103
- <table className="w-full text-sm">
104
- <thead className="text-slate-300/80">
105
- <tr className="border-b border-cyan-400/10">
106
- <th className="text-left py-2 pr-3">Time</th>
107
- <th className="text-left py-2 pr-3">Src → Dst</th>
108
- <th className="text-left py-2 pr-3">Ports</th>
109
- <th className="text-left py-2 pr-3">Proto</th>
110
- <th className="text-left py-2 pr-3">Prediction</th>
111
- </tr>
112
- </thead>
113
-
114
- <tbody>
115
- {rows
116
- .slice()
117
- .reverse()
118
- .map((r, idx) => (
119
- <tr
120
- key={idx}
121
- className="border-b border-cyan-400/5 hover:bg-white/5 transition-all"
122
- >
123
- <td className="py-2 pr-3 font-mono text-xs">{r.time}</td>
124
- <td className="py-2 pr-3 font-mono text-xs">
125
- <span
126
- className="text-cyan-400 cursor-pointer hover:text-cyan-300"
127
- onClick={() => setSelectedIP(r.src_ip || r.src)}
128
- >
129
- {r.src_ip || r.src}
130
- </span>{" "}
131
-
132
- <span
133
- className="text-cyan-400 cursor-pointer hover:text-cyan-300"
134
- onClick={() => setSelectedIP(r.dst_ip || r.dst)}
135
- >
136
- {r.dst_ip || r.dst}
137
- </span>
138
- </td>
139
- <td className="py-2 pr-3 font-mono text-xs">
140
- {r.sport} → {r.dport}
141
- </td>
142
- <td className="py-2 pr-3 font-mono text-xs">{r.proto}</td>
143
- <td className="py-2 pr-3 flex items-center gap-2">
144
- <Badge value={r.prediction} />
145
- <Trash
146
- size={14}
147
- className="text-red-400 cursor-pointer hover:text-red-600"
148
- onClick={() =>
149
- deleteSingleRow(rows.length - 1 - idx)
150
- }
151
- />
152
- </td>
153
- </tr>
154
- ))}
155
- </tbody>
156
- </table>
157
- </div>
158
-
159
- {/* ✅ IP Lookup Modal */}
160
- {selectedIP && (
161
- <IPInfoModal
162
- ip={selectedIP}
163
- onClose={() => setSelectedIP(null)}
164
- />
165
- )}
166
- </div>
167
- );
168
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/Sparkline.jsx DELETED
@@ -1,28 +0,0 @@
1
- import { LineChart, Line, ResponsiveContainer } from "recharts";
2
-
3
- export default function Sparkline({ events }) {
4
- const data = events.slice(-40).map((e, i) => ({
5
- index: i,
6
- value: (e.prediction || "").toUpperCase() !== "UNKNOWN" ? 1 : 0,
7
- }));
8
-
9
- return (
10
- <div className="cyber-card p-4">
11
- <h3 className="text-accent mb-2 font-semibold">Threat Activity Trend</h3>
12
-
13
- <div className="h-24">
14
- <ResponsiveContainer>
15
- <LineChart data={data}>
16
- <Line
17
- type="monotone"
18
- dataKey="value"
19
- stroke="#00e5ff"
20
- strokeWidth={2}
21
- dot={false}
22
- />
23
- </LineChart>
24
- </ResponsiveContainer>
25
- </div>
26
- </div>
27
- );
28
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/StatsPanel.jsx DELETED
@@ -1,104 +0,0 @@
1
- import React, { useMemo } from "react";
2
- import {
3
- PieChart,
4
- Pie,
5
- Cell,
6
- Tooltip,
7
- ResponsiveContainer,
8
- BarChart,
9
- Bar,
10
- XAxis,
11
- YAxis,
12
- CartesianGrid,
13
- } from "recharts";
14
-
15
- // 🎨 Neon cyber color palette
16
- const COLORS = ["#00e5ff", "#ff0059", "#a78bfa", "#fbbf24", "#10b981"];
17
-
18
- export default function StatsPanel({ stats }) {
19
- // ✅ Memoized data transformation for performance
20
- const data = useMemo(() => {
21
- if (!stats || typeof stats !== "object") return [];
22
- return Object.entries(stats)
23
- .map(([name, value]) => ({
24
- name: String(name).toUpperCase(),
25
- value: Number(value) || 0,
26
- }))
27
- .filter((d) => d.value > 0);
28
- }, [stats]);
29
-
30
- const total = data.reduce((sum, d) => sum + d.value, 0);
31
-
32
- return (
33
- <div className="grid md:grid-cols-2 gap-4">
34
- {/* ==== PIE CHART ==== */}
35
- <div className="cyber-card relative overflow-hidden">
36
- <h3 className="font-semibold mb-2 text-cyan-400">
37
- Class Distribution (Pie)
38
- </h3>
39
-
40
- <div className="h-64">
41
- {data.length > 0 ? (
42
- <ResponsiveContainer>
43
- <PieChart>
44
- <Pie
45
- data={data}
46
- dataKey="value"
47
- nameKey="name"
48
- outerRadius={90}
49
- label={({ name, value }) => `${name}: ${value}`}
50
- >
51
- {data.map((entry, index) => (
52
- <Cell key={index} fill={COLORS[index % COLORS.length]} />
53
- ))}
54
- </Pie>
55
- <Tooltip />
56
- </PieChart>
57
- </ResponsiveContainer>
58
- ) : (
59
- <div className="flex items-center justify-center h-full text-slate-400 text-sm">
60
- No Data Yet
61
- </div>
62
- )}
63
- </div>
64
-
65
- {/* Total Packets Indicator */}
66
- <p className="text-xs text-slate-400 absolute bottom-2 right-3">
67
- Total Packets:{" "}
68
- <span className="text-cyan-300 font-semibold">{total}</span>
69
- </p>
70
- </div>
71
-
72
- {/* ==== BAR CHART ==== */}
73
- <div className="cyber-card relative overflow-hidden">
74
- <h3 className="font-semibold mb-2 text-cyan-400">
75
- Class Counts (Bar)
76
- </h3>
77
-
78
- <div className="h-64">
79
- {data.length > 0 ? (
80
- <ResponsiveContainer>
81
- <BarChart data={data}>
82
- <CartesianGrid strokeDasharray="3 3" strokeOpacity={0.1} />
83
- <XAxis dataKey="name" stroke="#94a3b8" />
84
- <YAxis stroke="#94a3b8" allowDecimals={false} />
85
- <Tooltip />
86
- <Bar
87
- dataKey="value"
88
- radius={[6, 6, 0, 0]}
89
- fill="#00e5ff"
90
- opacity={0.8}
91
- />
92
- </BarChart>
93
- </ResponsiveContainer>
94
- ) : (
95
- <div className="flex items-center justify-center h-full text-slate-400 text-sm">
96
- Awaiting Data...
97
- </div>
98
- )}
99
- </div>
100
- </div>
101
- </div>
102
- );
103
- }
104
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/ThreatFeed.jsx DELETED
@@ -1,39 +0,0 @@
1
- import { AlertTriangle } from "lucide-react";
2
-
3
- export default function ThreatFeed({ events }) {
4
- const highRisk = events
5
- .filter((e) =>
6
- ["TOR", "I2P", "ZERONET"].includes((e.prediction || "").toUpperCase())
7
- )
8
- .slice(-20)
9
- .reverse();
10
-
11
- return (
12
- <div className="cyber-card p-4 border border-red-500/30">
13
- <h3 className="text-red-400 font-semibold mb-3 flex items-center gap-2">
14
- <AlertTriangle size={18} /> High-Risk Events
15
- </h3>
16
-
17
- {/* Scrollable list */}
18
- <div className="max-h-64 overflow-y-auto pr-2 custom-scroll space-y-2">
19
- {highRisk.length === 0 ? (
20
- <p className="text-slate-400 text-sm">No critical activity detected</p>
21
- ) : (
22
- highRisk.map((e, i) => (
23
- <div
24
- key={i}
25
- className="p-2 rounded-lg bg-red-500/10 border border-red-500/20
26
- animate-fadeIn"
27
- style={{ animationDelay: `${i * 50}ms` }}
28
- >
29
- <span className="text-red-300 font-semibold">{e.prediction}</span>
30
- <span className="text-slate-300"> from </span>
31
- <span className="text-cyan-300">{e.src_ip}</span>
32
- </div>
33
- ))
34
- )}
35
- </div>
36
- </div>
37
- );
38
- }
39
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/ThreatTimeline.jsx DELETED
@@ -1,27 +0,0 @@
1
- export default function ThreatTimeline({ events }) {
2
- const last20 = [...events].slice(-20).reverse();
3
-
4
- return (
5
- <div className="cyber-card p-4">
6
- <h3 className="text-accent font-semibold mb-3">Event Timeline</h3>
7
-
8
- {/* Scrollable container */}
9
- <div className="space-y-3 max-h-64 overflow-y-auto pr-2 custom-scroll">
10
- {last20.map((e, i) => (
11
- <div key={i} className="flex gap-3 items-start">
12
- <div className="w-2 h-2 rounded-full bg-accent mt-2 animate-pulse" />
13
- <div>
14
- <p className="text-sm text-slate-300">
15
- <span className="text-cyan-300">{e.prediction}</span>{" "}
16
- detected from {e.src_ip}
17
- </p>
18
- <p className="text-[11px] text-slate-500">
19
- {new Date().toLocaleTimeString()}
20
- </p>
21
- </div>
22
- </div>
23
- ))}
24
- </div>
25
- </div>
26
- );
27
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/TopCountries.jsx DELETED
@@ -1,26 +0,0 @@
1
- export default function TopCountries({ events }) {
2
- const map = {};
3
-
4
- events.forEach((e) => {
5
- const c = e.src_country || "Unknown";
6
- map[c] = (map[c] || 0) + 1;
7
- });
8
-
9
- const sorted = Object.entries(map)
10
- .sort((a, b) => b[1] - a[1])
11
- .slice(0, 6);
12
-
13
- return (
14
- <div className="cyber-card p-4">
15
- <h3 className="text-accent mb-2 font-semibold">Top Source Countries</h3>
16
- <ul className="text-sm space-y-1">
17
- {sorted.map(([country, count], i) => (
18
- <li key={i} className="flex justify-between">
19
- <span>{country}</span>
20
- <span className="text-cyan-300">{count}</span>
21
- </li>
22
- ))}
23
- </ul>
24
- </div>
25
- );
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/dashboard/TopIPs.jsx DELETED
@@ -1,26 +0,0 @@
1
- export default function TopIPs({ events }) {
2
- const map = {};
3
-
4
- events.forEach((e) => {
5
- const ip = e.src_ip || "Unknown";
6
- map[ip] = (map[ip] || 0) + 1;
7
- });
8
-
9
- const sorted = Object.entries(map)
10
- .sort((a, b) => b[1] - a[1])
11
- .slice(0, 6);
12
-
13
- return (
14
- <div className="cyber-card p-4">
15
- <h3 className="text-accent mb-2 font-semibold">Top Source IPs</h3>
16
- <ul className="text-sm space-y-1">
17
- {sorted.map(([ip, count], i) => (
18
- <li key={i} className="flex justify-between">
19
- <span className="text-slate-300">{ip}</span>
20
- <span className="text-cyan-300">{count}</span>
21
- </li>
22
- ))}
23
- </ul>
24
- </div>
25
- );
26
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/accordion.jsx DELETED
@@ -1,43 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
- import { ChevronDown } from "lucide-react"
6
-
7
- import { cn } from "@/lib/utils"
8
-
9
- const Accordion = AccordionPrimitive.Root
10
-
11
- const AccordionItem = React.forwardRef(({ className, ...props }, ref) => (
12
- <AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />
13
- ))
14
- AccordionItem.displayName = "AccordionItem"
15
-
16
- const AccordionTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
17
- <AccordionPrimitive.Header className="flex">
18
- <AccordionPrimitive.Trigger
19
- ref={ref}
20
- className={cn(
21
- "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
22
- className
23
- )}
24
- {...props}>
25
- {children}
26
- <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
27
- </AccordionPrimitive.Trigger>
28
- </AccordionPrimitive.Header>
29
- ))
30
- AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
31
-
32
- const AccordionContent = React.forwardRef(({ className, children, ...props }, ref) => (
33
- <AccordionPrimitive.Content
34
- ref={ref}
35
- className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
36
- {...props}>
37
- <div className={cn("pb-4 pt-0", className)}>{children}</div>
38
- </AccordionPrimitive.Content>
39
- ))
40
-
41
- AccordionContent.displayName = AccordionPrimitive.Content.displayName
42
-
43
- export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/badge.jsx DELETED
@@ -1,34 +0,0 @@
1
- import * as React from "react"
2
- import { cva } from "class-variance-authority";
3
-
4
- import { cn } from "@/lib/utils"
5
-
6
- const badgeVariants = cva(
7
- "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
- {
9
- variants: {
10
- variant: {
11
- default:
12
- "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
- secondary:
14
- "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
- destructive:
16
- "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
- outline: "text-foreground",
18
- },
19
- },
20
- defaultVariants: {
21
- variant: "default",
22
- },
23
- }
24
- )
25
-
26
- function Badge({
27
- className,
28
- variant,
29
- ...props
30
- }) {
31
- return (<div className={cn(badgeVariants({ variant }), className)} {...props} />);
32
- }
33
-
34
- export { Badge, badgeVariants }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/button.jsx DELETED
@@ -1,47 +0,0 @@
1
- import * as React from "react"
2
- import { Slot } from "@radix-ui/react-slot"
3
- import { cva } from "class-variance-authority";
4
-
5
- import { cn } from "@/lib/utils"
6
-
7
- const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
- {
10
- variants: {
11
- variant: {
12
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
- destructive:
14
- "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
- outline:
16
- "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
17
- secondary:
18
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
19
- ghost: "hover:bg-accent hover:text-accent-foreground",
20
- link: "text-primary underline-offset-4 hover:underline",
21
- },
22
- size: {
23
- default: "h-10 px-4 py-2",
24
- sm: "h-9 rounded-md px-3",
25
- lg: "h-11 rounded-md px-8",
26
- icon: "h-10 w-10",
27
- },
28
- },
29
- defaultVariants: {
30
- variant: "default",
31
- size: "default",
32
- },
33
- }
34
- )
35
-
36
- const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
37
- const Comp = asChild ? Slot : "button"
38
- return (
39
- <Comp
40
- className={cn(buttonVariants({ variant, size, className }))}
41
- ref={ref}
42
- {...props} />
43
- );
44
- })
45
- Button.displayName = "Button"
46
-
47
- export { Button, buttonVariants }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/card.jsx DELETED
@@ -1,50 +0,0 @@
1
- import * as React from "react"
2
-
3
- import { cn } from "@/lib/utils"
4
-
5
- const Card = React.forwardRef(({ className, ...props }, ref) => (
6
- <div
7
- ref={ref}
8
- className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
9
- {...props} />
10
- ))
11
- Card.displayName = "Card"
12
-
13
- const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
14
- <div
15
- ref={ref}
16
- className={cn("flex flex-col space-y-1.5 p-6", className)}
17
- {...props} />
18
- ))
19
- CardHeader.displayName = "CardHeader"
20
-
21
- const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
22
- <div
23
- ref={ref}
24
- className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
25
- {...props} />
26
- ))
27
- CardTitle.displayName = "CardTitle"
28
-
29
- const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
30
- <div
31
- ref={ref}
32
- className={cn("text-sm text-muted-foreground", className)}
33
- {...props} />
34
- ))
35
- CardDescription.displayName = "CardDescription"
36
-
37
- const CardContent = React.forwardRef(({ className, ...props }, ref) => (
38
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
39
- ))
40
- CardContent.displayName = "CardContent"
41
-
42
- const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
43
- <div
44
- ref={ref}
45
- className={cn("flex items-center p-6 pt-0", className)}
46
- {...props} />
47
- ))
48
- CardFooter.displayName = "CardFooter"
49
-
50
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/input.jsx DELETED
@@ -1,19 +0,0 @@
1
- import * as React from "react"
2
-
3
- import { cn } from "@/lib/utils"
4
-
5
- const Input = React.forwardRef(({ className, type, ...props }, ref) => {
6
- return (
7
- <input
8
- type={type}
9
- className={cn(
10
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
11
- className
12
- )}
13
- ref={ref}
14
- {...props} />
15
- );
16
- })
17
- Input.displayName = "Input"
18
-
19
- export { Input }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/scroll-area.jsx DELETED
@@ -1,38 +0,0 @@
1
- import * as React from "react"
2
- import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
3
-
4
- import { cn } from "@/lib/utils"
5
-
6
- const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => (
7
- <ScrollAreaPrimitive.Root
8
- ref={ref}
9
- className={cn("relative overflow-hidden", className)}
10
- {...props}>
11
- <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
12
- {children}
13
- </ScrollAreaPrimitive.Viewport>
14
- <ScrollBar />
15
- <ScrollAreaPrimitive.Corner />
16
- </ScrollAreaPrimitive.Root>
17
- ))
18
- ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
19
-
20
- const ScrollBar = React.forwardRef(({ className, orientation = "vertical", ...props }, ref) => (
21
- <ScrollAreaPrimitive.ScrollAreaScrollbar
22
- ref={ref}
23
- orientation={orientation}
24
- className={cn(
25
- "flex touch-none select-none transition-colors",
26
- orientation === "vertical" &&
27
- "h-full w-2.5 border-l border-l-transparent p-[1px]",
28
- orientation === "horizontal" &&
29
- "h-2.5 flex-col border-t border-t-transparent p-[1px]",
30
- className
31
- )}
32
- {...props}>
33
- <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
34
- </ScrollAreaPrimitive.ScrollAreaScrollbar>
35
- ))
36
- ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
37
-
38
- export { ScrollArea, ScrollBar }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/separator.jsx DELETED
@@ -1,23 +0,0 @@
1
- import * as React from "react"
2
- import * as SeparatorPrimitive from "@radix-ui/react-separator"
3
-
4
- import { cn } from "@/lib/utils"
5
-
6
- const Separator = React.forwardRef((
7
- { className, orientation = "horizontal", decorative = true, ...props },
8
- ref
9
- ) => (
10
- <SeparatorPrimitive.Root
11
- ref={ref}
12
- decorative={decorative}
13
- orientation={orientation}
14
- className={cn(
15
- "shrink-0 bg-border",
16
- orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
17
- className
18
- )}
19
- {...props} />
20
- ))
21
- Separator.displayName = SeparatorPrimitive.Root.displayName
22
-
23
- export { Separator }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
frontend/src/components/ui/switch.jsx DELETED
@@ -1,24 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as SwitchPrimitives from "@radix-ui/react-switch"
5
-
6
- import { cn } from "@/lib/utils"
7
-
8
- const Switch = React.forwardRef(({ className, ...props }, ref) => (
9
- <SwitchPrimitives.Root
10
- className={cn(
11
- "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
12
- className
13
- )}
14
- {...props}
15
- ref={ref}>
16
- <SwitchPrimitives.Thumb
17
- className={cn(
18
- "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
19
- )} />
20
- </SwitchPrimitives.Root>
21
- ))
22
- Switch.displayName = SwitchPrimitives.Root.displayName
23
-
24
- export { Switch }