import { useEffect, useState, useRef } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Loader2, Terminal } from "lucide-react"; interface LogViewerProps { containerId: string | null; containerName: string; isOpen: boolean; onClose: () => void; } export function LogViewer({ containerId, containerName, isOpen, onClose }: LogViewerProps) { const [logs, setLogs] = useState([]); const [isConnected, setIsConnected] = useState(false); const scrollRef = useRef(null); // Auto-scroll to bottom when new logs arrive useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [logs]); useEffect(() => { if (!isOpen || !containerId) return; setLogs([]); setIsConnected(false); const eventSource = new EventSource(`/api/containers/${containerId}/logs`); eventSource.onopen = () => setIsConnected(true); eventSource.onmessage = (event) => { try { const newLog = JSON.parse(event.data); setLogs(prev => { const updated = [...prev, newLog]; return updated.length > 1000 ? updated.slice(updated.length - 1000) : updated; }); } catch (err) { console.error("Failed to parse log line", err); } }; eventSource.onerror = () => { setIsConnected(false); eventSource.close(); }; return () => { eventSource.close(); }; }, [isOpen, containerId]); return ( !open && onClose()}> {containerName} {isConnected ? (connected) : }
{logs.length === 0 && !isConnected ? (
Connecting to container stream...
) : ( logs.map((log, i) => (
{log}
)) )}
); }