/**
* AI API Logs Panel Component
*
* Monitor OpenAI API calls with:
* - Call list with filtering by provider
* - Token usage statistics
* - Response time tracking
* - Error monitoring
*/
import React, { useState, useEffect, useCallback } from 'react';
import {
Zap, RefreshCw, Filter, Clock, Brain, Bot,
Check, XCircle, AlertTriangle, BarChart3, TrendingUp,
Cpu, Activity, Hash
} from 'lucide-react';
import { getToken } from '../../services/api';
const API_BASE = import.meta.env.VITE_API_URL || '';
// Provider badge (OpenAI)
const ProviderBadge = ({ provider }) => {
const config = {
openai: { bg: '#dbeafe', color: '#2563eb', icon: '🤖', label: 'OpenAI' }
};
const { bg, color, icon, label } = config[provider] || config.openai;
return (
{icon} {label}
);
};
// Status badge
const StatusBadge = ({ status }) => {
const config = {
success: { bg: '#d1fae5', color: '#059669', icon: },
error: { bg: '#fee2e2', color: '#dc2626', icon: },
timeout: { bg: '#fef3c7', color: '#d97706', icon: }
};
const { bg, color, icon } = config[status] || config.success;
return (
{icon} {status}
);
};
// Response time indicator
const ResponseTimeIndicator = ({ ms }) => {
if (!ms) return -;
let color;
if (ms < 1000) color = '#10b981';
else if (ms < 3000) color = '#f59e0b';
else color = '#ef4444';
return (
{ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`}
);
};
// Token display
const TokenDisplay = ({ tokens }) => {
if (!tokens) return -;
return (
{tokens.toLocaleString()}
);
};
function AILogsPanel() {
const [logs, setLogs] = useState([]);
const [stats, setStats] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Filters
const [provider, setProvider] = useState('');
const [operation, setOperation] = useState('');
const [status, setStatus] = useState('');
const [hours, setHours] = useState(24);
const [limit, setLimit] = useState(100);
const [autoRefresh, setAutoRefresh] = useState(true);
// Fetch logs
const fetchLogs = useCallback(async () => {
setLoading(true);
setError(null);
try {
const token = getToken();
const params = new URLSearchParams({
limit: limit.toString(),
hours: hours.toString()
});
if (provider) params.append('provider', provider);
if (operation) params.append('operation', operation);
if (status) params.append('status', status);
const response = await fetch(`${API_BASE}/api/admin/ai-logs?${params}`, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const data = await response.json();
if (data.success) {
setLogs(data.logs || []);
setStats(data.stats || null);
}
} else {
throw new Error('Failed to fetch AI logs');
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [provider, operation, status, hours, limit]);
useEffect(() => {
fetchLogs();
}, [fetchLogs]);
// Auto-refresh every 10 seconds
useEffect(() => {
if (!autoRefresh) return;
const interval = setInterval(fetchLogs, 10000);
return () => clearInterval(interval);
}, [autoRefresh, fetchLogs]);
// Format timestamp
const formatTime = (isoString) => {
if (!isoString) return '-';
const date = new Date(isoString);
return date.toLocaleTimeString('en-US', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
};
return (
{/* Header */}
{/* Stats Cards */}
{stats && (
{stats.total_calls?.toLocaleString() || 0}
Total Calls ({hours}h)
🌟
{stats.openai_calls?.toLocaleString() || 0}
OpenAI Calls
âš¡
{stats.total_calls?.toLocaleString() || 0}
Total Calls
{stats.success_rate || 100}%
Success Rate
{stats.total_tokens?.toLocaleString() || 0}
Total Tokens
{stats.avg_response_time_ms || 0}ms
Avg Response
)}
{/* Filters */}
Filters:
setOperation(e.target.value)}
style={{
padding: '0.5rem',
borderRadius: '6px',
border: '1px solid rgba(255,255,255,0.1)',
backgroundColor: 'rgba(0,0,0,0.3)',
color: 'white',
fontSize: '0.9rem',
width: '150px'
}}
/>
{/* Error display */}
{error && (
)}
{/* Logs Table */}
{loading && logs.length === 0 ? (
) : logs.length === 0 ? (
No AI API logs found
AI calls will appear here once made
) : (
| Time |
Provider |
Operation |
Model |
Tokens |
Response |
Status |
{logs.map((log, index) => (
e.currentTarget.style.backgroundColor = 'rgba(255,255,255,0.03)'}
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = 'transparent'}
>
|
{formatTime(log.created_at)}
|
|
{log.operation || '-'}
|
{log.model || '-'}
|
|
|
|
))}
)}
{/* Footer */}
Showing {logs.length} logs
Last updated: {new Date().toLocaleTimeString()}
);
}
export default AILogsPanel;