export function LogStream()

in app/components/LogStream.tsx [117:299]


export function LogStream({ jobId, className = "" }: LogStreamProps) {
  const [logs, setLogs] = useState<string>("");
  const [status, setStatus] = useState<string>("pending");
  const [isConnected, setIsConnected] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const logsEndRef = useRef<HTMLDivElement>(null);
  const eventSourceRef = useRef<EventSource | null>(null);

  // Auto-scroll to bottom when new logs arrive
  const scrollToBottom = () => {
    logsEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(() => {
    scrollToBottom();
  }, [logs]);

  useEffect(() => {
    if (!jobId) return;

    // Create EventSource for Server-Sent Events
    const eventSource = new EventSource(`/api/jobs/${jobId}/logs`);
    eventSourceRef.current = eventSource;

    eventSource.onopen = () => {
      console.log("Log stream connected");
      setIsConnected(true);
      setError(null);
    };

    eventSource.onmessage = (event) => {
      try {
        const message: LogMessage = JSON.parse(event.data);

        switch (message.type) {
          case "connected":
            console.log(
              "Connected to log stream for job:",
              message.jobId || jobId
            );
            break;

          case "logs":
            if (message.data) {
              setLogs((prev) => prev + message.data);
            }
            break;

          case "status":
            if (message.status) {
              setStatus(message.status);
            }
            break;

          case "finished":
            console.log("Job finished with status:", message.status);
            setStatus(message.status || "completed");
            setIsConnected(false);
            // Optionally trigger a page refresh after a short delay to show final results
            if (message.status === "completed") {
              setTimeout(() => {
                console.log(
                  "Job completed - refreshing page to show final results"
                );
                window.location.reload();
              }, 3000); // Wait 3 seconds before refresh
            }
            break;

          case "error":
            console.error("Log stream error:", message.message);
            setError(message.message || "Unknown error");
            break;
        }
      } catch (error) {
        console.error("Failed to parse log message:", error);
      }
    };

    eventSource.onerror = (event) => {
      console.error("EventSource error:", event);
      setError("Connection to log stream failed");
      setIsConnected(false);
    };

    // Cleanup on unmount
    return () => {
      eventSource.close();
      eventSourceRef.current = null;
    };
  }, [jobId]);

  // Manual cleanup method
  const disconnect = () => {
    if (eventSourceRef.current) {
      eventSourceRef.current.close();
      eventSourceRef.current = null;
      setIsConnected(false);
    }
  };

  return (
    <div className={`flex flex-col ${className}`}>
      {/* Status Bar */}
      <div className="flex items-center justify-between border-b bg-gray-100 p-2">
        <div className="flex items-center gap-2">
          <div
            className={`h-2 w-2 rounded-full ${
              isConnected ? "bg-green-500" : "bg-red-500"
            }`}
          />
          <span className="text-sm font-medium">
            Status:{" "}
            <span
              className={`${
                status === "completed"
                  ? "text-green-600"
                  : status === "failed"
                    ? "text-red-600"
                    : status === "running"
                      ? "text-blue-600"
                      : "text-gray-600"
              }`}
            >
              {status}
            </span>
          </span>
        </div>

        <div className="flex items-center gap-2">
          {isConnected && <span className="text-xs text-gray-500">Live</span>}
          {isConnected && (
            <button
              onClick={disconnect}
              className="rounded bg-gray-200 px-2 py-1 text-xs hover:bg-gray-300"
            >
              Disconnect
            </button>
          )}
        </div>
      </div>

      {/* Error Display */}
      {error && (
        <div className="border-b border-red-200 bg-red-50 p-2 text-sm text-red-700">
          Error: {error}
        </div>
      )}

      {/* Logs Display */}
      <div className="max-h-96 flex-1 overflow-auto bg-gray-900 p-4 font-mono text-sm">
        {logs ? (
          <div className="whitespace-pre-wrap">{parseTerminalOutput(logs)}</div>
        ) : (
          <div className="text-gray-500">Waiting for logs...</div>
        )}

        {/* Job completion indicator */}
        {!isConnected && status === "completed" && (
          <div className="mt-4 rounded border border-green-500/30 bg-green-900/20 p-2 text-center text-green-300">
            <i className="fas fa-check-circle mr-2"></i>
            Job completed successfully! Page will refresh shortly to show final
            results.
          </div>
        )}

        {!isConnected && status === "failed" && (
          <div className="mt-4 rounded border border-red-500/30 bg-red-900/20 p-2 text-center text-red-300">
            <i className="fas fa-times-circle mr-2"></i>
            Job failed. Check the logs above for error details.
          </div>
        )}

        <div ref={logsEndRef} />
      </div>

      {/* Footer */}
      <div className="border-t bg-gray-50 p-2 text-xs text-gray-500">
        Job ID: {jobId} | Logs: {logs.length} characters
      </div>
    </div>
  );
}