first commit
This commit is contained in:
1 parent
abb09fbea8
commit
72f6d49818
355 files changed
+121239
-3
No files matched your search
@@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ScrollText, X } from "lucide-react";
|
||||
import { containers as containersApi, type Container } from "@/lib/api";
|
||||
import { sanitizeLogText } from "@/lib/terminal-output";
|
||||
|
||||
interface ContainerLogsModalProps {
|
||||
container: Container;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export default function ContainerLogsModal({
|
||||
container,
|
||||
onClose,
|
||||
}: ContainerLogsModalProps) {
|
||||
const [logs, setLogs] = useState("Loading...");
|
||||
const [loading, setLoading] = useState(true);
|
||||
const viewportRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
containersApi
|
||||
.logs(container.id, 200)
|
||||
.then((response) =>
|
||||
setLogs(sanitizeLogText(response.data?.logs || "(no output)")),
|
||||
)
|
||||
.catch((err) =>
|
||||
setLogs(
|
||||
sanitizeLogText(
|
||||
"Error: " + (err instanceof Error ? err.message : "Failed"),
|
||||
),
|
||||
),
|
||||
)
|
||||
.finally(() => setLoading(false));
|
||||
}, [container.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
const viewport = viewportRef.current;
|
||||
if (!viewport) return;
|
||||
|
||||
const scrollFrame = window.requestAnimationFrame(() => {
|
||||
viewport.scrollTop = viewport.scrollHeight;
|
||||
});
|
||||
|
||||
return () => window.cancelAnimationFrame(scrollFrame);
|
||||
}, [logs, loading]);
|
||||
|
||||
return (
|
||||
<div className="modal-overlay">
|
||||
<div
|
||||
className="modal animate-slide-in"
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: 760,
|
||||
padding: 0,
|
||||
maxHeight: "85vh",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: "16px 20px",
|
||||
borderBottom: "1px solid var(--border)",
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
||||
<ScrollText size={16} style={{ color: "var(--accent)" }} />
|
||||
<h3
|
||||
style={{
|
||||
color: "var(--text-primary)",
|
||||
fontWeight: 700,
|
||||
fontSize: 15,
|
||||
}}
|
||||
>
|
||||
Logs - {container.name}
|
||||
</h3>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
color: "var(--text-muted)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
ref={viewportRef}
|
||||
style={{
|
||||
flex: 1,
|
||||
overflowY: "auto",
|
||||
padding: "16px 20px",
|
||||
background: "#0d1117",
|
||||
fontFamily: "JetBrains Mono, monospace",
|
||||
fontSize: 12,
|
||||
lineHeight: 1.6,
|
||||
color: "#e6edf3",
|
||||
whiteSpace: "pre-wrap",
|
||||
wordBreak: "break-all",
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
<span style={{ color: "#8b949e" }}>Loading...</span>
|
||||
) : (
|
||||
logs
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user