/** * Error Logs Panel Component * * Real-time error monitoring with: * - Error list with filtering * - Severity-based sorting * - Error acknowledgment and resolution * - Error statistics */ import React, { useState, useEffect, useCallback } from 'react'; import { AlertTriangle, AlertCircle, AlertOctagon, Info, RefreshCw, Check, Eye, Filter, Clock, Search, ChevronDown, ChevronUp, User, Code, XCircle } from 'lucide-react'; import { getToken } from '../../services/api'; const API_BASE = import.meta.env.VITE_API_URL || ''; // Severity badge component const SeverityBadge = ({ severity }) => { const config = { critical: { icon: AlertOctagon, color: '#ef4444', bg: '#fee2e2' }, error: { icon: AlertCircle, color: '#f97316', bg: '#ffedd5' }, warning: { icon: AlertTriangle, color: '#f59e0b', bg: '#fef3c7' }, info: { icon: Info, color: '#3b82f6', bg: '#dbeafe' } }; const { icon: Icon, color, bg } = config[severity] || config.info; return ( {severity} ); }; // Status badge component const StatusBadge = ({ status }) => { const config = { new: { color: '#ef4444', bg: '#fee2e2', label: 'New' }, acknowledged: { color: '#f59e0b', bg: '#fef3c7', label: 'Acknowledged' }, resolved: { color: '#10b981', bg: '#d1fae5', label: 'Resolved' }, ignored: { color: '#6b7280', bg: '#f3f4f6', label: 'Ignored' } }; const { color, bg, label } = config[status] || config.new; return ( {label} ); }; // Error detail modal const ErrorDetailModal = ({ error, onClose, onAcknowledge, onResolve }) => { if (!error) return null; return (
e.stopPropagation()}>

Error Details

{error.error_type}

{error.error_message}

{error.method} {error.endpoint}
{new Date(error.created_at).toLocaleString()}
{error.stack_trace && (
{error.stack_trace}
)}
{error.status === 'new' && ( )} {(error.status === 'new' || error.status === 'acknowledged') && ( )}
); }; function ErrorLogsPanel() { const [errors, setErrors] = useState([]); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedError, setSelectedError] = useState(null); // Filters const [severity, setSeverity] = useState(''); const [status, setStatus] = useState(''); const [hours, setHours] = useState(24); const [limit, setLimit] = useState(50); const [autoRefresh, setAutoRefresh] = useState(true); // Fetch errors const fetchErrors = useCallback(async () => { setLoading(true); setError(null); try { const token = getToken(); const params = new URLSearchParams({ hours: hours.toString(), limit: limit.toString() }); if (severity) params.append('severity', severity); if (status) params.append('status', status); const response = await fetch(`${API_BASE}/api/admin/errors?${params}`, { headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); if (!response.ok) throw new Error('Failed to fetch errors'); const data = await response.json(); if (data.success) { setErrors(data.errors); setStats(data.stats); } } catch (err) { setError(err.message); } finally { setLoading(false); } }, [severity, status, hours, limit]); // Acknowledge error const acknowledgeError = async (errorId) => { try { const token = getToken(); const response = await fetch(`${API_BASE}/api/admin/errors/${errorId}/acknowledge`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); if (response.ok) { fetchErrors(); setSelectedError(null); } } catch (err) { console.error('Error acknowledging:', err); } }; // Resolve error const resolveError = async (errorId) => { try { const token = getToken(); const response = await fetch(`${API_BASE}/api/admin/errors/${errorId}/resolve`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' } }); if (response.ok) { fetchErrors(); setSelectedError(null); } } catch (err) { console.error('Error resolving:', err); } }; // Initial fetch useEffect(() => { fetchErrors(); }, [fetchErrors]); // Auto-refresh every 30 seconds useEffect(() => { if (!autoRefresh) return; const interval = setInterval(fetchErrors, 30000); return () => clearInterval(interval); }, [autoRefresh, fetchErrors]); if (loading) { return (

Loading error logs...

); } return (
{/* Header */}

Error Logs

{stats && ( {stats.total} total ({stats.by_status?.new || 0} new) )}
{/* Stats Bar */} {stats && (
{stats.by_severity?.critical || 0} Critical
{stats.by_severity?.error || 0} Errors
{stats.by_severity?.warning || 0} Warnings
{stats.by_severity?.info || 0} Info
)} {/* Filters */}
{/* Error List */}
{errors.length === 0 ? (

No errors found for the selected filters

) : ( {errors.map((err) => ( ))}
Severity Error Type Message Endpoint Time Status Actions
{err.error_type} {err.error_message?.substring(0, 50)}... {err.method} {err.endpoint} {new Date(err.created_at).toLocaleTimeString()} {err.status === 'new' && ( )}
)}
{/* Error Detail Modal */} {selectedError && ( setSelectedError(null)} onAcknowledge={acknowledgeError} onResolve={resolveError} /> )}
); } export default ErrorLogsPanel;