import React, { useEffect, useRef, useState } from 'react'; import { LogEntry } from '../services/logService'; import { Button } from './ui/Button'; import { Badge } from './ui/Badge'; import { useTranslation } from 'react-i18next'; interface LogViewerProps { logs: LogEntry[]; isLoading?: boolean; error?: Error | null; onClear?: () => void; } const LogViewer: React.FC = ({ logs, isLoading = false, error = null, onClear }) => { const { t } = useTranslation(); const logContainerRef = useRef(null); const [autoScroll, setAutoScroll] = useState(true); const [filter, setFilter] = useState(''); const [typeFilter, setTypeFilter] = useState>(['info', 'error', 'warn', 'debug']); const [sourceFilter, setSourceFilter] = useState>(['main', 'child']); // Auto scroll to bottom when new logs come in if autoScroll is enabled useEffect(() => { if (autoScroll && logContainerRef.current) { logContainerRef.current.scrollTop = logContainerRef.current.scrollHeight; } }, [logs, autoScroll]); // Filter logs based on current filter settings const filteredLogs = logs.filter(log => { const matchesText = filter ? log.message.toLowerCase().includes(filter.toLowerCase()) : true; const matchesType = typeFilter.includes(log.type); const matchesSource = sourceFilter.includes(log.source as 'main' | 'child'); return matchesText && matchesType && matchesSource; }); // Format timestamp to readable format const formatTimestamp = (timestamp: number) => { const date = new Date(timestamp); return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); }; // Get badge color based on log type const getLogTypeColor = (type: string) => { switch (type) { case 'error': return 'bg-red-400'; case 'warn': return 'bg-yellow-400'; case 'debug': return 'bg-purple-400'; default: return 'bg-blue-400'; } }; // Get badge color based on log source const getSourceColor = (source: string) => { switch (source) { case 'main': return 'bg-green-400'; case 'child': return 'bg-orange-400'; default: return 'bg-gray-400'; } }; return (
{t('logs.filters')}: {/* Text search filter */} setFilter(e.target.value)} /> {/* Log type filters */}
{(['info', 'error', 'warn', 'debug'] as const).map(type => ( { if (typeFilter.includes(type)) { setTypeFilter(prev => prev.filter(t => t !== type)); } else { setTypeFilter(prev => [...prev, type]); } }} > {type} ))}
{/* Log source filters */}
{(['main', 'child'] as const).map(source => ( { if (sourceFilter.includes(source)) { setSourceFilter(prev => prev.filter(s => s !== source)); } else { setSourceFilter(prev => [...prev, source]); } }} > {source === 'main' ? t('logs.mainProcess') : t('logs.childProcess')} ))}
{isLoading ? (
{t('logs.loading')}
) : error ? (
{error.message}
) : filteredLogs.length === 0 ? (
{filter || typeFilter.length < 4 || sourceFilter.length < 2 ? t('logs.noMatch') : t('logs.noLogs')}
) : ( filteredLogs.map((log, index) => (
[{formatTimestamp(log.timestamp)}] {log.type} {log.source === 'main' ? t('logs.main') : t('logs.child')} {log.processId ? ` (${log.processId})` : ''} {log.message}
)) )}
); }; export default LogViewer;