'use client'; import React, { useEffect, useRef } from 'react'; import { cn } from '@/lib/utils'; // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export type LogLevel = 'info' | 'success' | 'warning' | 'error'; export interface LogEntry { timestamp: string; message: string; level: LogLevel; } export interface DeployLogViewerProps { logs: LogEntry[]; maxHeight?: string; className?: string; } // --------------------------------------------------------------------------- // Level colors // --------------------------------------------------------------------------- const levelStyles: Record = { info: 'text-gray-400', success: 'text-emerald-400', warning: 'text-amber-400', error: 'text-red-400', }; const levelBadge: Record = { info: 'text-gray-500', success: 'text-emerald-500', warning: 'text-amber-500', error: 'text-red-500', }; const levelPrefix: Record = { info: '○', success: '✓', warning: '⚠', error: '✗', }; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export function DeployLogViewer({ logs, maxHeight = '320px', className, }: DeployLogViewerProps) { const bottomRef = useRef(null); const containerRef = useRef(null); // Auto-scroll to bottom on new logs useEffect(() => { if (bottomRef.current) { bottomRef.current.scrollIntoView({ behavior: 'smooth' }); } }, [logs.length]); const formatTime = (ts: string) => { try { const d = new Date(ts); return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', }); } catch { return '--:--:--'; } }; return (
{/* Header */}
Deploy Log {logs.length} entries
{/* Log content */}
{logs.length === 0 && (
Waiting for deploy to start…
)} {logs.map((entry, i) => (
{/* Timestamp */} {formatTime(entry.timestamp)} {/* Level indicator */} {levelPrefix[entry.level]} {/* Message */} {entry.message}
))} {/* Cursor blink at bottom */} {logs.length > 0 && (
)}
); }