/**
* 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 (
);
}
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
) : (
| Severity |
Error Type |
Message |
Endpoint |
Time |
Status |
Actions |
{errors.map((err) => (
|
{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;