Files
ProxmoxVE-Local/src/app/_components/LoadingModal.tsx

103 lines
3.1 KiB
TypeScript

"use client";
import { Loader2, CheckCircle, X } from "lucide-react";
import { useRegisterModal } from "./modal/ModalStackProvider";
import { useEffect, useRef } from "react";
import { Button } from "./ui/button";
interface LoadingModalProps {
isOpen: boolean;
action?: string;
logs?: string[];
isComplete?: boolean;
title?: string;
onClose?: () => void;
}
export function LoadingModal({
isOpen,
action: _action,
logs = [],
isComplete = false,
title,
onClose,
}: LoadingModalProps) {
// Allow dismissing with ESC only when complete, prevent during running
useRegisterModal(isOpen, {
id: "loading-modal",
allowEscape: isComplete,
onClose: onClose ?? (() => null),
});
const logsEndRef = useRef<HTMLDivElement>(null);
// Auto-scroll to bottom when new logs arrive
useEffect(() => {
logsEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [logs]);
if (!isOpen) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4 backdrop-blur-sm">
<div className="bg-card border-border relative flex max-h-[80vh] w-full max-w-2xl flex-col rounded-lg border p-8 shadow-xl">
{/* Close button - only show when complete */}
{isComplete && onClose && (
<Button
variant="ghost"
size="sm"
onClick={onClose}
className="absolute top-4 right-4 h-6 w-6 p-0"
>
<X className="h-4 w-4" />
</Button>
)}
<div className="flex flex-col items-center space-y-4">
<div className="relative">
{isComplete ? (
<CheckCircle className="text-success h-12 w-12" />
) : (
<>
<Loader2 className="text-primary h-12 w-12 animate-spin" />
<div className="border-primary/20 absolute inset-0 animate-pulse rounded-full border-2"></div>
</>
)}
</div>
{/* Static title text */}
{title && <p className="text-muted-foreground text-sm">{title}</p>}
{/* Log output */}
{logs.length > 0 && (
<div className="bg-card border-border text-chart-2 terminal-output max-h-[60vh] w-full overflow-y-auto rounded-lg border p-4 font-mono text-xs">
{logs.map((log, index) => (
<div
key={index}
className="mb-1 break-words whitespace-pre-wrap"
>
{log}
</div>
))}
<div ref={logsEndRef} />
</div>
)}
{!isComplete && (
<div className="flex space-x-1">
<div className="bg-primary h-2 w-2 animate-bounce rounded-full"></div>
<div
className="bg-primary h-2 w-2 animate-bounce rounded-full"
style={{ animationDelay: "0.1s" }}
></div>
<div
className="bg-primary h-2 w-2 animate-bounce rounded-full"
style={{ animationDelay: "0.2s" }}
></div>
</div>
)}
</div>
</div>
</div>
);
}