mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2025-12-14 08:06:22 +00:00
163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
"use client"
|
||
|
||
import { useState, useEffect } from "react"
|
||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||
import { Button } from "@/components/ui/button"
|
||
import { AlertCircle, CheckCircle2 } from "lucide-react"
|
||
import { io, type Socket } from "socket.io-client"
|
||
|
||
interface ScriptTerminalModalProps {
|
||
open: boolean
|
||
onClose: () => void
|
||
scriptPath: string
|
||
scriptName: string
|
||
params?: Record<string, string>
|
||
title: string
|
||
description: string
|
||
}
|
||
|
||
interface WebInteraction {
|
||
type: "yesno" | "menu" | "msgbox" | "input"
|
||
id: string
|
||
title: string
|
||
text: string
|
||
options?: string[]
|
||
}
|
||
|
||
export function ScriptTerminalModal({
|
||
open,
|
||
onClose,
|
||
scriptPath,
|
||
scriptName,
|
||
params = {},
|
||
title,
|
||
description,
|
||
}: ScriptTerminalModalProps) {
|
||
const [socket, setSocket] = useState<Socket | null>(null)
|
||
const [sessionId] = useState(() => Math.random().toString(36).substring(7))
|
||
const [terminalOutput, setTerminalOutput] = useState<string>("")
|
||
const [isRunning, setIsRunning] = useState(false)
|
||
const [isComplete, setIsComplete] = useState(false)
|
||
const [exitCode, setExitCode] = useState<number | null>(null)
|
||
const [interaction, setInteraction] = useState<WebInteraction | null>(null)
|
||
|
||
useEffect(() => {
|
||
if (!open) return
|
||
|
||
const newSocket = io("http://localhost:8008", {
|
||
transports: ["websocket"],
|
||
reconnection: true,
|
||
})
|
||
|
||
newSocket.on("connect", () => {
|
||
console.log("[v0] WebSocket connected")
|
||
// Ejecutar el script
|
||
newSocket.emit("execute_script", {
|
||
session_id: sessionId,
|
||
script_path: scriptPath,
|
||
script_name: scriptName,
|
||
params,
|
||
})
|
||
setIsRunning(true)
|
||
})
|
||
|
||
newSocket.on("script_output", (data: { line: string }) => {
|
||
setTerminalOutput((prev) => prev + data.line + "\n")
|
||
})
|
||
|
||
newSocket.on("web_interaction", (data: WebInteraction) => {
|
||
console.log("[v0] Web interaction received:", data)
|
||
setInteraction(data)
|
||
})
|
||
|
||
newSocket.on("script_complete", (data: { exit_code: number }) => {
|
||
console.log("[v0] Script complete, exit code:", data.exit_code)
|
||
setIsRunning(false)
|
||
setIsComplete(true)
|
||
setExitCode(data.exit_code)
|
||
})
|
||
|
||
setSocket(newSocket)
|
||
|
||
return () => {
|
||
newSocket.disconnect()
|
||
}
|
||
}, [open, sessionId, scriptPath, scriptName, params])
|
||
|
||
const handleInteractionResponse = (value: string) => {
|
||
if (!socket || !interaction) return
|
||
|
||
console.log("[v0] Sending interaction response:", value)
|
||
socket.emit("interaction_response", {
|
||
session_id: sessionId,
|
||
interaction_id: interaction.id,
|
||
value,
|
||
})
|
||
|
||
// Escribir la respuesta en la terminal
|
||
setTerminalOutput((prev) => prev + `\n> ${value}\n`)
|
||
setInteraction(null)
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<Dialog open={open} onOpenChange={onClose}>
|
||
<DialogContent className="max-w-4xl h-[80vh] flex flex-col">
|
||
<DialogHeader>
|
||
<DialogTitle className="flex items-center gap-2">
|
||
{isRunning && <span className="animate-spin">⚙️</span>}
|
||
{isComplete && exitCode === 0 && <CheckCircle2 className="w-5 h-5 text-green-500" />}
|
||
{isComplete && exitCode !== 0 && <AlertCircle className="w-5 h-5 text-red-500" />}
|
||
{title}
|
||
</DialogTitle>
|
||
<p className="text-sm text-muted-foreground">{description}</p>
|
||
</DialogHeader>
|
||
|
||
<div className="flex-1 bg-black rounded-md p-4 overflow-auto font-mono text-sm text-green-400">
|
||
<pre className="whitespace-pre-wrap">{terminalOutput}</pre>
|
||
{isRunning && <span className="animate-pulse">_</span>}
|
||
</div>
|
||
|
||
{isComplete && (
|
||
<div
|
||
className={`p-3 rounded-md ${exitCode === 0 ? "bg-green-500/10 text-green-500" : "bg-red-500/10 text-red-500"}`}
|
||
>
|
||
{exitCode === 0 ? "✓ Script completed successfully" : `✗ Script failed with exit code ${exitCode}`}
|
||
</div>
|
||
)}
|
||
|
||
<div className="flex justify-between items-center">
|
||
<span className="text-sm text-muted-foreground">Session ID: {sessionId}</span>
|
||
<Button onClick={onClose} disabled={isRunning}>
|
||
{isRunning ? "Running..." : "Close"}
|
||
</Button>
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
|
||
{interaction && (
|
||
<Dialog open={true} onOpenChange={() => setInteraction(null)}>
|
||
<DialogContent>
|
||
<DialogHeader>
|
||
<DialogTitle>{interaction.title}</DialogTitle>
|
||
</DialogHeader>
|
||
<p className="text-sm">{interaction.text}</p>
|
||
|
||
<div className="flex gap-2 justify-end">
|
||
{interaction.type === "yesno" && (
|
||
<>
|
||
<Button variant="outline" onClick={() => handleInteractionResponse("no")}>
|
||
No
|
||
</Button>
|
||
<Button onClick={() => handleInteractionResponse("yes")}>Yes</Button>
|
||
</>
|
||
)}
|
||
{interaction.type === "msgbox" && <Button onClick={() => handleInteractionResponse("ok")}>OK</Button>}
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
)}
|
||
</>
|
||
)
|
||
}
|