import React, { useState, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Loader2, ArrowUp, ArrowDown } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; interface TrafficLogEntry { timestamp: string; direction: string; requestId: string; endpoint: string; method?: string; status?: number; durationMs?: number; } interface TrafficLogTableProps { logs: TrafficLogEntry[]; isLoading: boolean; } type SortKey = 'timestamp' | 'direction' | 'endpoint' | 'method' | 'status' | 'duration'; type SortDir = 'asc' | 'desc'; function SortIcon({ column, sortKey, sortDir }: { column: SortKey; sortKey: SortKey; sortDir: SortDir }) { if (sortKey !== column) return ; return sortDir === 'asc' ? ( ) : ( ); } export const TrafficLogTable: React.FC = ({ logs, isLoading }) => { const { t } = useTranslation(); const [sortKey, setSortKey] = useState('timestamp'); const [sortDir, setSortDir] = useState('desc'); const toggleSort = (key: SortKey) => { if (sortKey === key) { setSortDir((prev) => (prev === 'asc' ? 'desc' : 'asc')); } else { setSortKey(key); setSortDir('asc'); } }; const sortedLogs = useMemo(() => { const dir = sortDir === 'asc' ? 1 : -1; return [...logs].sort((a, b) => { switch (sortKey) { case 'timestamp': return (new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()) * dir; case 'direction': return (a.direction || '').localeCompare(b.direction || '') * dir; case 'endpoint': return (a.endpoint || '').localeCompare(b.endpoint || '') * dir; case 'method': return (a.method || '').localeCompare(b.method || '') * dir; case 'status': { const aStatus = a.status ?? -1; const bStatus = b.status ?? -1; return (aStatus - bStatus) * dir; } case 'duration': { const aDur = a.durationMs ?? -1; const bDur = b.durationMs ?? -1; return (aDur - bDur) * dir; } default: return 0; } }); }, [logs, sortKey, sortDir]); return ( {t('proxy.advanced.trafficLog.title')} {isLoading ? (
) : logs.length === 0 ? (
{t('proxy.advanced.trafficLog.noLogs')}
) : (
{sortedLogs.map((log, i) => ( ))}
toggleSort('timestamp')} > {t('proxy.advanced.trafficLog.timestamp')} toggleSort('direction')} > {t('proxy.advanced.trafficLog.direction')} toggleSort('endpoint')} > {t('proxy.advanced.trafficLog.endpoint')} toggleSort('method')} > {t('proxy.advanced.trafficLog.method')} toggleSort('status')} > {t('proxy.advanced.trafficLog.status')} toggleSort('duration')} > {t('proxy.advanced.trafficLog.duration')}
{new Date(log.timestamp).toLocaleTimeString()} {log.direction} {log.endpoint} {log.method || '—'} {log.status != null ? ( = 400 ? 'destructive' : 'secondary'} className="text-xs" > {log.status} ) : ( '—' )} {log.durationMs != null ? `${log.durationMs}ms` : '—'}
)}
); };