import React, { useEffect, useState, useRef, useMemo } from 'react'; import { useAuth } from '@clerk/clerk-react'; import { apiClient, getAdminStats } from '../api/client'; import { toast } from 'react-hot-toast'; import { Activity, Users, Folder, FileText, TerminalSquare, Wrench, Pause, Play, Search, Trash2, ShieldAlert, Zap, Server, CheckCircle2, Database, Trash, Cpu, ShieldCheck, DatabaseZap, AlertTriangle } from 'lucide-react'; import { SnapshotDashboard } from '../components/project/SnapshotDashboard'; import { motion, AnimatePresence } from 'framer-motion'; import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer, BarChart, Bar, Cell } from 'recharts'; import '../styles/admin.css'; interface AdminStats { status: string; database: { total_projects: number; total_users: number; total_generated_sections: number; }; generator: { active_tasks_count: number; active_tasks: string[]; subscribers: Record; }; recent_projects?: { id: string; title: string; created_at: string; has_final_document: boolean; has_audit: boolean; overall_score?: number; }[]; throughput?: any; } type TabType = 'overview' | 'telemetry' | 'tools' | 'regulation'; type LogLevel = 'INFO' | 'WARNING' | 'ERROR' | 'DEBUG'; const systemThroughputData = [ { time: '00:00', load: 12 }, { time: '02:00', load: 18 }, { time: '04:00', load: 15 }, { time: '06:00', load: 25 }, { time: '08:00', load: 45 }, { time: '10:00', load: 60 }, { time: '12:00', load: 85 }, { time: '14:00', load: 75 }, { time: '16:00', load: 90 }, { time: '18:00', load: 65 }, { time: '20:00', load: 40 }, { time: '22:00', load: 20 }, ]; const AdminDashboard: React.FC = () => { const [activeTab, setActiveTab] = useState('overview'); const [lawHistory, setLawHistory] = useState([]); const { getToken } = useAuth(); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [logs, setLogs] = useState([]); const [isPaused, setIsPaused] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [activeLevels, setActiveLevels] = useState(['INFO', 'WARNING', 'ERROR']); const logsEndRef = useRef(null); const isPausedRef = useRef(isPaused); const [isRunningCritic, setIsRunningCritic] = useState(false); const [targetProjectId, setTargetProjectId] = useState(''); const [isSyncingRag, setIsSyncingRag] = useState(false); const [ragCategory, setRagCategory] = useState(''); const [isClearingCache, setIsClearingCache] = useState(false); const [serviceStatus, setServiceStatus] = useState(null); const [isCheckingStatus, setIsCheckingStatus] = useState(false); useEffect(() => { isPausedRef.current = isPaused; }, [isPaused]); // Cycle 16: Load law change history for in-app notifications useEffect(() => { fetch('/api/admin/law-monitoring/history') .then(r => r.json()) .then(data => { if (data.history) setLawHistory(data.history); }) .catch(() => {}); }, []); const fetchStats = async () => { try { const data = await getAdminStats(); setStats(data); } catch (e) { console.error(e); } finally { setLoading(false); } }; useEffect(() => { fetchStats(); const interval = setInterval(fetchStats, 5000); let sse: EventSource; const setupSSE = async () => { const token = await getToken(); const baseURL = import.meta.env.VITE_API_URL ? import.meta.env.VITE_API_URL.replace('/api', '') : 'http://localhost:8000'; sse = new EventSource(`${baseURL}/api/admin/diagnostics/stream?token=${token}`); sse.addEventListener('telemetry_log', (e) => { if (isPausedRef.current) return; try { const log = JSON.parse(e.data); setLogs(prev => { const newLogs = [...prev, log]; return newLogs.slice(-300); }); } catch (err) { console.error("Failed to parse log", err); } }); }; setupSSE(); return () => { clearInterval(interval); if (sse) sse.close(); }; }, [getToken]); useEffect(() => { if (!isPaused) { logsEndRef.current?.scrollIntoView({ behavior: 'smooth' }); } }, [logs, isPaused]); const toggleLogLevel = (level: LogLevel) => { setActiveLevels(prev => prev.includes(level) ? prev.filter(l => l !== level) : [...prev, level] ); }; const filteredLogs = useMemo(() => { return logs.filter(log => { if (!activeLevels.includes(log.level as LogLevel)) return false; if (searchQuery && !log.message?.toLowerCase().includes(searchQuery.toLowerCase()) && !log.agent?.toLowerCase().includes(searchQuery.toLowerCase())) { return false; } return true; }); }, [logs, activeLevels, searchQuery]); const handleRunGlobalCritic = async () => { if (!targetProjectId.trim()) { toast.error('Proszę wprowadzić ID projektu'); return; } setIsRunningCritic(true); const loadingToast = toast.loading('Uruchamianie analizy Global Critic...', { style: { background: '#1e1b4b', color: '#c7d2fe', border: '1px solid #3730a3' } }); try { const { data } = await apiClient.post(`/api/projects/${targetProjectId.trim()}/holistic-review`); toast.dismiss(loadingToast); if (data.status === 'pending') { toast.success(`Rozpoczęto analizę w tle. Status: w toku.`, { duration: 5000, style: { background: '#064e3b', color: '#6ee7b7' } }); } else if (data.is_approved !== undefined) { if (data.is_approved) { toast.success(`Zatwierdzono: ${data.feedback || ''}`, { duration: 5000, style: { background: '#064e3b', color: '#6ee7b7' } }); } else { toast.error(`Odrzucono [${data.severity || 'high'}]: ${data.feedback || ''}`, { duration: 8000, style: { background: '#4c1d95', color: '#c4b5fd' } }); } } else { toast.success(`Zlecono pomyślnie.`, { duration: 5000, style: { background: '#064e3b', color: '#6ee7b7' } }); } } catch (err: any) { console.error(err); toast.error(err.response?.data?.detail || 'Wystąpił błąd podczas analizy', { id: loadingToast }); } finally { setIsRunningCritic(false); } }; const handleSyncRag = async () => { setIsSyncingRag(true); const loadingToast = toast.loading('Synchronizacja Bazy Wiedzy...'); try { await apiClient.post('/api/rag/sync', { category: ragCategory || 'SMART' }); toast.success('Synchronizacja RAG zakończona', { id: loadingToast }); } catch (err: any) { console.error(err); toast.error(err.response?.data?.detail || 'Synchronizacja RAG nie powiodła się', { id: loadingToast }); } finally { setIsSyncingRag(false); } }; const handleClearCache = async () => { setIsClearingCache(true); const loadingToast = toast.loading('Czyszczenie pamięci podręcznej systemu...'); try { const { data } = await apiClient.post('/api/admin/clear_cache'); toast.success(data.message || 'Pamięć podręczna wyczyszczona pomyślnie', { id: loadingToast }); } catch (err: any) { console.error(err); toast.error('Błąd podczas czyszczenia pamięci podręcznej', { id: loadingToast }); } finally { setIsClearingCache(false); } }; const handleCheckServices = async () => { setIsCheckingStatus(true); try { const { data } = await apiClient.get('/api/admin/diagnostics/services_status'); setServiceStatus(data); toast.success('Zaktualizowano status usług'); } catch (err: any) { console.error(err); toast.error('Nie udało się pobrać statusu usług'); } finally { setIsCheckingStatus(false); } }; if (loading) return (
Inicjalizacja Systemu Głównego
); return (
{/* HEADER */}
SYSTEM ONLINE

Nexus Control

Zaawansowana telemetria, zarządzanie infrastrukturą i podgląd operacyjny na żywo dla GrantForge AI.

setActiveTab('overview')} icon={} label="Przegląd" /> setActiveTab('telemetry')} icon={} label="Telemetria" /> setActiveTab('tools')} icon={} label="Narzędzia" /> setActiveTab('regulation')} icon={} label="Regulation & Trust" />
{/* TAB 1: OVERVIEW */} {activeTab === 'overview' && ( {/* KPI Metrics */}
} trend="+12% w tym tygodniu" /> } trend="Stabilny wzrost" /> } trend="Wysoka przepustowość" />
{/* CHARTS ROW */}
{/* Area Chart */}

Przepustowość Systemu (24h)

{/* Bar Chart for Scores */}

Ostatnie Wyniki Audytu

{stats?.recent_projects?.filter(p => p.overall_score).length ? ( p.overall_score).slice(0, 7)} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}> val.length > 10 ? val.substring(0,10)+'...' : val} /> {stats.recent_projects.filter(p => p.overall_score).slice(0, 7).map((entry, index) => ( = 80 ? '#34d399' : entry.overall_score && entry.overall_score >= 50 ? '#fbbf24' : '#f43f5e'} /> ))} ) : (
Brak ostatnich wyników audytu
)}
{/* Active Tasks Panel */}

Kolejka Zadań

{stats?.generator.active_tasks_count ? : null} {stats?.generator.active_tasks_count || 0} Aktywnych
{stats?.generator.active_tasks?.length ? (
{stats.generator.active_tasks.map((taskId, i) => (
TASK::{taskId.split('-')[0]}
{stats.generator.subscribers[taskId] || 0} USERS
))}
) : (
System w spoczynku Oczekuje na zadania generacyjne
)}
{/* Recent Projects Table */}

Dziennik Operacji

{stats?.recent_projects?.length ? stats.recent_projects.map((proj) => ( )) : ( )}
Projekt Stan Generacji Status Audytu Trust Wynik
{proj.title}
{proj.id}
{proj.has_final_document ? ( GOTOWE ) : (
PRZETWARZANIE )}
{proj.has_audit ? ( ZWERYFIKOWANO ) : ( - )} {proj.trust_score !== undefined ? (
= 80 ? 'admin-score-high' : proj.trust_score >= 65 ? 'admin-score-med' : 'admin-score-low' }`}> {proj.trust_score}
) : ( - )}
{proj.overall_score !== undefined && proj.overall_score !== null ? (
= 80 ? 'admin-score-high' : proj.overall_score >= 50 ? 'admin-score-med' : 'admin-score-low' }`}> {proj.overall_score}
) : ( - )}
No recent operational data found.
)} {/* TAB 2: TELEMETRY */} {activeTab === 'telemetry' && (
{/* Terminal Header */}
core_telemetry.log
setSearchQuery(e.target.value)} className="admin-input" />
{(['INFO', 'WARNING', 'ERROR', 'DEBUG'] as LogLevel[]).map(level => { const isActive = activeLevels.includes(level); return ( ); })}
*Przycisk STREAMING/HALTED wstrzymuje automatyczne przewijanie i pojawianie się nowych logów, co ułatwia ich analizę.
{/* Terminal Body */}
{filteredLogs.length === 0 ? (
Awaiting matrix input...
) : (
{filteredLogs.map((log, i) => { let colorClass = '#cbd5e1'; let labelColor = '#38bdf8'; if (log.level === 'ERROR') { colorClass = '#fda4af'; labelColor = '#f43f5e'; } if (log.level === 'WARNING') { colorClass = '#fde68a'; labelColor = '#fbbf24'; } if (log.level === 'DEBUG') { colorClass = '#64748b'; labelColor = '#475569'; } return (
{new Date(log.timestamp).toLocaleTimeString(undefined, {hour12: false, hour: '2-digit', minute:'2-digit', second:'2-digit', fractionalSecondDigits: 3})} {log.level.padEnd(5)}
[{log.agent}] {log.message} {log.metadata && Object.keys(log.metadata).length > 0 && (
{JSON.stringify(log.metadata, null, 2)}
)}
); })}
)}
{/* Terminal Footer */}
TCP/IP Socket ESTABLISHED
Rendered {filteredLogs.length} of {logs.length} vectors
)} {/* TAB 3: TOOLS */} {activeTab === 'tools' && ( {/* Holistic Critic */}

Całościowa Ocena Krytyczna

Wymuś weryfikację poprawności i testy LLM na wybranym projekcie. UUID projektu znajdziesz w adresie URL po otwarciu edytora projektu.

setTargetProjectId(e.target.value)} placeholder="UUID docelowego projektu" className="admin-tool-input fuchsia" />
{isRunningCritic && logs.length > 0 && ( {logs[logs.length - 1].message} )}
{/* RAG Sync */}

Synchronizacja Bazy Wektorowej

Wymusza odświeżenie wektorów embeddings w Pinecone dla podanej kategorii. Używane do RAG.

setRagCategory(e.target.value)} placeholder="Kategoria (domyślnie: SMART)" className="admin-tool-input teal" />
{isSyncingRag && logs.length > 0 && ( {logs[logs.length - 1].message} )}
{/* Cache & Services */}
{/* Service Status Results */} {serviceStatus && (

Diagnostyka Sieci

{Object.entries(serviceStatus).map(([service, info]: [string, any]) => (
{service}
{info.status === 'ok' ? 'ONLINE' : info.status === 'warning' ? 'WARNING' : 'ERROR'} 1000 ? 'slow' : 'fast'}`}> {info.latency_ms !== undefined ? `${info.latency_ms}ms` : 'N/A'}
{(info.status === 'error' || info.status === 'warning') && (
{info.message}
)}
))}
)}
)} {/* TAB 4: REGULATION & TRUST (Cycle 6) */} {activeTab === 'regulation' && ( {/* Cycle 17: In-app Law Change Notifications */} {lawHistory.length > 0 && (

Recent Law Changes

{lawHistory.length} changes detected
{lawHistory.slice(0, 4).map((change, idx) => (
{change.program} — new snapshot created ({new Date(change.timestamp).toLocaleDateString()})
))}
These changes automatically triggered new Regulation Snapshots with higher Trust Score.
)}
)}
); }; // Subcomponents const TabButton = ({ active, onClick, icon, label }: { active: boolean, onClick: () => void, icon: React.ReactNode, label: string }) => ( ); const GlassCard = ({ children, className = '', style }: { children: React.ReactNode, className?: string, style?: React.CSSProperties }) => (
{children}
); const StatCardContent = ({ title, value, icon, trend }: { title: string, value: string | number, icon: React.ReactNode, trend: string }) => { return ( <>
{icon}
{trend}
{value}
{title}
); }; export default AdminDashboard;