163 lines
4.7 KiB
React
163 lines
4.7 KiB
React
// ChatLayout.jsx
|
|
import React, { useState, useEffect, useRef } from "react";
|
|
import ChatHeader from "./ChatHeader";
|
|
import ChatWindow from "./ChatWindow";
|
|
import ChatInput from "./ChatInput";
|
|
import SessionTable from "./SessionTable";
|
|
import NoSessionBox from "./NoSessionBox";
|
|
|
|
// ⏱ Tempo massimo configurabile (in millisecondi)
|
|
const MAX_EXECUTION_TIME_MS = 3 * 60 * 1000; // 3 minuti
|
|
|
|
export default function ChatLayout({
|
|
theme,
|
|
messages,
|
|
loading,
|
|
onSend,
|
|
onStop,
|
|
onToggleTheme,
|
|
onReloadHistory,
|
|
onFreshStart,
|
|
onCreateSession,
|
|
onEditSession,
|
|
onSelectSession,
|
|
userId,
|
|
sessionId,
|
|
sessionName
|
|
}) {
|
|
const [showSessionsPanel, setShowSessionsPanel] = useState(false);
|
|
const [timeLeft, setTimeLeft] = useState(null); // in secondi
|
|
const stopTimerRef = useRef(null);
|
|
const countdownIntervalRef = useRef(null);
|
|
|
|
// Gestione auto-stop e countdown
|
|
useEffect(() => {
|
|
if (loading) {
|
|
// Imposta il tempo rimanente
|
|
setTimeLeft(Math.floor(MAX_EXECUTION_TIME_MS / 1000));
|
|
|
|
// Avvia il timer di stop automatico
|
|
stopTimerRef.current = setTimeout(() => {
|
|
console.warn("⏱ Tempo massimo raggiunto, stop automatico");
|
|
onStop();
|
|
}, MAX_EXECUTION_TIME_MS);
|
|
|
|
// Avvia il countdown visivo
|
|
countdownIntervalRef.current = setInterval(() => {
|
|
setTimeLeft(prev => {
|
|
if (prev === null) return null;
|
|
if (prev <= 1) {
|
|
clearInterval(countdownIntervalRef.current);
|
|
return 0;
|
|
}
|
|
return prev - 1;
|
|
});
|
|
}, 1000);
|
|
} else {
|
|
// Reset se non sta più caricando
|
|
clearTimeout(stopTimerRef.current);
|
|
clearInterval(countdownIntervalRef.current);
|
|
stopTimerRef.current = null;
|
|
countdownIntervalRef.current = null;
|
|
setTimeLeft(null);
|
|
}
|
|
|
|
// Pulizia in caso di smontaggio
|
|
return () => {
|
|
clearTimeout(stopTimerRef.current);
|
|
clearInterval(countdownIntervalRef.current);
|
|
};
|
|
}, [loading, onStop]);
|
|
|
|
function getScrollbarWidth() {
|
|
return window.innerWidth - document.documentElement.clientWidth;
|
|
}
|
|
|
|
useEffect(() => {
|
|
document.body.classList.toggle("sessions-open", showSessionsPanel);
|
|
}, [showSessionsPanel]);
|
|
|
|
const openSessionManager = () => {
|
|
const scrollBarWidth = getScrollbarWidth();
|
|
document.body.style.overflow = "hidden";
|
|
document.body.style.paddingRight = `${scrollBarWidth}px`;
|
|
setShowSessionsPanel(true);
|
|
};
|
|
|
|
const closeSessionManager = () => {
|
|
document.body.style.overflow = "";
|
|
document.body.style.paddingRight = "";
|
|
setShowSessionsPanel(false);
|
|
document.body.classList.remove("sessions-open");
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className={`chat-layout sessions-wrapper ${theme.bodyBg}`}>
|
|
{/* HEADER */}
|
|
<ChatHeader
|
|
theme={theme}
|
|
onToggleTheme={onToggleTheme}
|
|
onReloadHistory={onReloadHistory}
|
|
onFreshStart={onFreshStart}
|
|
onCreateSession={onCreateSession}
|
|
onEditSession={onEditSession}
|
|
onManageSession={openSessionManager}
|
|
sessionId={sessionId}
|
|
sessionName={sessionName}
|
|
/>
|
|
|
|
{/* AREA SCROLLABILE */}
|
|
<div className="flex-grow-1 overflow-auto" style={{ minHeight: 0 }}>
|
|
<ChatWindow messages={messages} loading={loading} theme={theme} />
|
|
|
|
{loading && (
|
|
<div className="p-2 text-center">
|
|
<button className="btn btn-warning btn-sm me-2" onClick={onStop}>
|
|
⏹ Stop Generating
|
|
</button>
|
|
{timeLeft !== null && (
|
|
<span className="text-muted small">
|
|
Auto-stop in {timeLeft}s
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* INPUT SEMPRE IN BASSO */}
|
|
{sessionId ? (
|
|
<ChatInput onSend={onSend} onStop={onStop} loading={loading} />
|
|
) : (
|
|
<NoSessionBox onCreateSession={onCreateSession} />
|
|
)}
|
|
</div>
|
|
|
|
{/* PANEL SESSIONI */}
|
|
<div
|
|
className={`offcanvas offcanvas-start ${showSessionsPanel ? "show" : ""}`}
|
|
tabIndex="-1"
|
|
style={{ visibility: showSessionsPanel ? "visible" : "hidden" }}
|
|
>
|
|
<div className="offcanvas-header">
|
|
<h5 className="offcanvas-title">Manage Sessions</h5>
|
|
<button
|
|
type="button"
|
|
className="btn-close text-reset"
|
|
onClick={closeSessionManager}
|
|
></button>
|
|
</div>
|
|
<div className="offcanvas-body">
|
|
<SessionTable
|
|
userId={userId}
|
|
onSelectSession={onSelectSession}
|
|
onClosePanel={closeSessionManager}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
|