"use client"; import { useState, useEffect, useMemo } from "react"; import { usePoll } from "@/lib/use-poll"; import { fetchAPI } from "@/lib/api"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { ScrollArea } from "@/components/ui/scroll-area"; import { cn } from "@/lib/utils"; interface LogFile { name: string; filename?: string; size?: string; size_bytes?: number; modified?: number; } export default function LogsPage() { const { data: logsRaw } = usePoll("/api/logs", 60000); const logFiles = Array.isArray(logsRaw) ? logsRaw : (logsRaw?.files ?? []); const [selected, setSelected] = useState(null); const [content, setContent] = useState(""); const [loadingContent, setLoadingContent] = useState(false); const [search, setSearch] = useState(""); useEffect(() => { if (!selected) { setContent(""); return; } let cancelled = false; setLoadingContent(true); fetchAPI<{ lines?: string[]; content?: string } | string>(`/api/logs/${encodeURIComponent(selected)}?tail=200`) .then((data) => { if (cancelled) return; if (typeof data === "string") setContent(data); else if (Array.isArray((data as Record).lines)) setContent(((data as Record).lines as string[]).join("\n")); else if ((data as Record).content) setContent(String((data as Record).content)); else setContent(JSON.stringify(data, null, 2)); }) .catch((err) => { if (cancelled) return; setContent(`Error loading log: ${err}`); }) .finally(() => { if (!cancelled) setLoadingContent(false); }); return () => { cancelled = true; }; }, [selected]); const filteredLines = useMemo(() => { if (!content) return []; const lines = content.split("\n"); if (!search.trim()) return lines; const lower = search.toLowerCase(); return lines.filter(line => line.toLowerCase().includes(lower)); }, [content, search]); return (

Logs

{/* Left sidebar: log file list */} Log Files {logFiles.length === 0 ? (

Loading...

) : (
{logFiles.map((file) => ( ))}
)}
{/* Right: log content viewer */} {selected ?? "Select a log file"} {selected && ( setSearch(e.target.value)} placeholder="Filter lines..." className="rounded-lg glass-input px-3 py-1.5 text-xs w-48" /> )} {!selected ? (

Select a log file from the sidebar

) : loadingContent ? (

Loading...

) : (
                  {filteredLines.join("\n") || "No matching lines"}
                
)}
); }