import { useEffect, Suspense } from "react";
import { NavLink, useLocation, useBlocker, Outlet, useNavigate } from "react-router-dom";
import Sidebar from "./Sidebar";
import { useVideoGen } from "../stores/videoGenStore";
import { useDirty } from "../stores/dirtyStore";
import { useAuth } from "../stores/authStore";
// Fallback nhẹ khi chunk trang đang tải (code-splitting).
function PageFallback() {
return (
Đang tải công cụ…
);
}
const tabs = [
{ to: "/", label: "Trang chủ" },
{ to: "/huong-dan", label: "Hướng dẫn" },
{ to: "/ho-tro", label: "Hỗ trợ" },
];
export default function Layout() {
const location = useLocation();
const navigate = useNavigate();
const { authorized, user, logout } = useAuth();
useEffect(() => {
if (!authorized) navigate("/dang-nhap", { replace: true });
}, [authorized]);
const handleLogout = () => {
logout();
navigate("/dang-nhap");
};
const isVideoGen = location.pathname === "/tao-video";
const { connected, url } = useVideoGen();
const { dirty, message } = useDirty();
const blocker = useBlocker(
({ currentLocation, nextLocation }) =>
dirty && currentLocation.pathname !== nextLocation.pathname
);
useEffect(() => {
if (blocker.state === "blocked") {
const proceed = window.confirm(
message || "Bạn có chắc muốn rời khỏi công cụ này? Tiến trình hiện tại sẽ bị mất."
);
if (proceed) {
blocker.proceed();
} else {
blocker.reset();
}
}
}, [blocker.state, message]);
return (
{tabs.map((t) => (
"tab" + (isActive ? " active" : "")}>
{t.label}
))}
{user}
TNT
{/* Chỉ MỘT
được render tại một thời điểm (cái còn lại là `false`)
-> trang route chỉ mount 1 lần, không còn chạy đôi effect/poll/upload.
iframe Gradio LUÔN nằm trong cây (ngoài điều kiện) để giữ phiên kết nối
khi chuyển tool rồi quay lại /tao-video. */}
}>{!isVideoGen && }
}>{isVideoGen && }
);
}