'use client'; import { useEffect, useRef, useState } from 'react'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import { useWebSocket } from '@/lib/websocket/context'; import { WebSocketMessage, StepData, SteamOutputData, ErrorData, CompleteData } from '@/lib/websocket/client'; interface ServerCreationProgressClientProps { serverId: string; } interface ConsoleEntry { id: string; timestamp: number; type: 'step' | 'steam_output' | 'error' | 'complete'; content: string; level: 'info' | 'success' | 'warning' | 'error'; } interface StepStatus { step: string; status: 'pending' | 'in_progress' | 'completed' | 'failed'; message: string; } const STEPS = [ { key: 'validation', label: 'Validation' }, { key: 'directory_creation', label: 'Directory Creation' }, { key: 'steam_download', label: 'Steam Download' }, { key: 'config_generation', label: 'Config Generation' }, { key: 'service_creation', label: 'Service Creation' }, { key: 'firewall_rules', label: 'Firewall Rules' }, { key: 'database_save', label: 'Database Save' }, { key: 'completed', label: 'Completed' } ]; export function ServerCreationProgressClient({ serverId }: ServerCreationProgressClientProps) { const [entries, setEntries] = useState([]); const [steps, setSteps] = useState>({}); const [isCompleted, setIsCompleted] = useState(false); const [completionResult, setCompletionResult] = useState<{ success: boolean; message: string; } | null>(null); const [isMinimized, setIsMinimized] = useState(false); const { associateWithServer, addMessageHandler, removeMessageHandler, connectionStatus, connectionError, reconnect } = useWebSocket(); const router = useRouter(); const consoleRef = useRef(null); const addEntry = (entry: Omit) => { const newEntry = { ...entry, id: `${Date.now()}-${Math.random()}` }; setEntries((prev) => [...prev, newEntry]); }; const scrollToBottom = () => { if (consoleRef.current && !isMinimized) { consoleRef.current.scrollTop = consoleRef.current.scrollHeight; } }; useEffect(() => { scrollToBottom(); }, [entries, isMinimized]); useEffect(() => { if (serverId) { associateWithServer(serverId); } }, [serverId, associateWithServer]); useEffect(() => { const handleMessage = (message: WebSocketMessage) => { if (message.server_id !== serverId) return; const timestamp = message.timestamp; switch (message.type) { case 'step': { const data = message.data as StepData; setSteps((prev) => ({ ...prev, [data.step]: { step: data.step, status: data.status, message: data.message } })); let level: ConsoleEntry['level'] = 'info'; if (data.status === 'completed') level = 'success'; else if (data.status === 'failed') level = 'error'; else if (data.status === 'in_progress') level = 'warning'; addEntry({ timestamp, type: 'step', content: `[${data.step.toUpperCase()}] ${data.message}${data.error ? ` - ${data.error}` : ''}`, level }); break; } case 'steam_output': { const data = message.data as SteamOutputData; addEntry({ timestamp, type: 'steam_output', content: data.output, level: data.is_error ? 'error' : 'info' }); break; } case 'error': { const data = message.data as ErrorData; addEntry({ timestamp, type: 'error', content: `ERROR: ${data.error}${data.details ? ` - ${data.details}` : ''}`, level: 'error' }); break; } case 'complete': { const data = message.data as CompleteData; setIsCompleted(true); setCompletionResult({ success: data.success, message: data.message }); addEntry({ timestamp, type: 'complete', content: `COMPLETED: ${data.message}`, level: data.success ? 'success' : 'error' }); break; } } }; addMessageHandler(handleMessage); return () => { removeMessageHandler(handleMessage); }; }, [addMessageHandler, removeMessageHandler, serverId]); const handleReturnToDashboard = () => { router.push('/dashboard'); }; const handleReconnect = async () => { try { await reconnect(); } catch (error) { console.error('Failed to reconnect:', error); } }; const getStepStatusIcon = (status: StepStatus['status']) => { switch (status) { case 'pending': return '⏳'; case 'in_progress': return '🔄'; case 'completed': return '✅'; case 'failed': return '❌'; } }; const getEntryClassName = (level: ConsoleEntry['level']) => { switch (level) { case 'success': return 'text-green-400'; case 'warning': return 'text-yellow-400'; case 'error': return 'text-red-400'; default: return 'text-gray-300'; } }; const getConnectionStatusColor = () => { switch (connectionStatus) { case 'connected': return 'text-green-400'; case 'connecting': return 'text-yellow-400'; case 'disconnected': return 'text-gray-400'; case 'error': return 'text-red-400'; } }; const getConnectionStatusIcon = () => { switch (connectionStatus) { case 'connected': return '🟢'; case 'connecting': return '🟡'; case 'disconnected': return '⚫'; case 'error': return '🔴'; } }; return (
{/* Header */}

Server Creation Progress

{/* Connection Status */}
{getConnectionStatusIcon()} {connectionStatus === 'connected' && 'Connected'} {connectionStatus === 'connecting' && 'Connecting...'} {connectionStatus === 'disconnected' && 'Disconnected'} {connectionStatus === 'error' && 'Connection Error'} {(connectionStatus === 'disconnected' || connectionStatus === 'error') && ( )}
{isCompleted && ( )}
{/* Connection Error Banner */} {(connectionStatus === 'disconnected' || connectionStatus === 'error') && (

WebSocket Connection Lost - {connectionError ? ` ${connectionError}` : ' Unable to receive real-time updates.'}{' '} Progress may not be current.

)}
{/* Steps Progress */}

Progress Steps

{STEPS.map(({ key, label }) => { const stepStatus = steps[key]; return (
{getStepStatusIcon(stepStatus?.status || 'pending')} {label}
); })}
{/* Console Output */} {!isMinimized && (

Console Output

{entries.length} log entries
{entries.map((entry) => (
{new Date(entry.timestamp * 1000).toLocaleTimeString()} {' '} {entry.content}
))} {entries.length === 0 && (
Waiting for server creation to begin...
)}
)} {isMinimized && (
Console minimized - click the expand button in the header to view output
)}
); }