2024-09-18 16:35:40 +08:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
import Link from "next/link";
|
|
|
|
import { usePathname, useRouter } from "next/navigation";
|
|
|
|
import React, { useState } from "react";
|
|
|
|
import Image from "next/image";
|
2024-09-18 21:44:09 +08:00
|
|
|
|
2024-09-18 16:35:40 +08:00
|
|
|
import { FaPlus, FaBars } from "react-icons/fa6";
|
|
|
|
import { MdOutlineHome, MdLogout } from "react-icons/md";
|
|
|
|
import { PiSquaresFourBold } from "react-icons/pi";
|
|
|
|
import { BsLightningChargeFill } from "react-icons/bs";
|
2024-09-18 21:44:09 +08:00
|
|
|
import { useTranslations, useLocale } from "next-intl";
|
2024-09-18 16:35:40 +08:00
|
|
|
import classNames from "classnames";
|
|
|
|
import useUserStore from "@/store/userStore";
|
|
|
|
import useFetch from "@/hooks/useFetch";
|
|
|
|
import { useToast } from "@/contexts/ToastContext";
|
|
|
|
import useLoadingStore from "@/store/loadingStore";
|
|
|
|
type MenuItem = {
|
|
|
|
name: string;
|
|
|
|
href: string;
|
|
|
|
icon?: React.ComponentType;
|
|
|
|
};
|
|
|
|
|
|
|
|
export default function SideBar() {
|
|
|
|
const pathname = usePathname();
|
|
|
|
const router = useRouter();
|
|
|
|
const user = useUserStore((state) => state.user);
|
|
|
|
const t = useTranslations("sideBar");
|
2024-09-18 21:44:09 +08:00
|
|
|
const locale = useLocale();
|
|
|
|
|
2024-09-18 16:35:40 +08:00
|
|
|
const { addToast, addToastSuccess, addToastError } = useToast();
|
|
|
|
const [sidebarOpen, setSidebarOpen] = useState(false); // 控制侧边栏的开关状态
|
|
|
|
const hideLoading = useLoadingStore((state) => state.hideLoading);
|
|
|
|
const toggleSidebar = () => {
|
|
|
|
setSidebarOpen(!sidebarOpen);
|
|
|
|
};
|
|
|
|
|
|
|
|
const menu: { title: string; items: MenuItem[] }[] = [
|
|
|
|
{
|
|
|
|
title: t("creation"),
|
|
|
|
items: [
|
|
|
|
{
|
|
|
|
name: t("videos"),
|
|
|
|
href: "/projects",
|
|
|
|
icon: PiSquaresFourBold,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const {
|
|
|
|
fetchData: logoutFetch,
|
|
|
|
loading: logoutLoading,
|
|
|
|
data: logoutResult,
|
|
|
|
} = useFetch({
|
2024-09-18 21:44:09 +08:00
|
|
|
url: "/api/logout/", // 假设你的退出接口路径为 /api/logout/
|
2024-09-18 16:35:40 +08:00
|
|
|
method: "POST",
|
|
|
|
});
|
|
|
|
|
2024-09-18 21:44:09 +08:00
|
|
|
const logout = () => {
|
|
|
|
logoutFetch() // 不需要传递参数
|
2024-09-18 16:35:40 +08:00
|
|
|
.then((res) => {
|
|
|
|
addToastSuccess(t("logoutSuccess"));
|
2024-09-18 21:44:09 +08:00
|
|
|
router.push("/login"); // 退出成功后跳转到登录页面
|
2024-09-18 16:35:40 +08:00
|
|
|
})
|
|
|
|
.finally(() => {
|
|
|
|
hideLoading();
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
{/* 切换侧边栏按钮(仅在小屏幕显示) */}
|
|
|
|
<button
|
|
|
|
className="lg:hidden fixed top-4 left-4 z-30 bg-black text-white p-2 rounded-md"
|
|
|
|
onClick={toggleSidebar}
|
|
|
|
>
|
|
|
|
<FaBars size={20} />
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/* 侧边栏容器 */}
|
|
|
|
<div
|
|
|
|
className={classNames(
|
|
|
|
"fixed top-0 left-0 h-full bg-white border-r border-base-200 overflow-auto flex flex-col p-6 items-center z-20 transition-transform transform",
|
|
|
|
{
|
|
|
|
"translate-x-0": sidebarOpen, // 打开时显示
|
|
|
|
"-translate-x-full": !sidebarOpen, // 关闭时隐藏
|
|
|
|
"lg:translate-x-0 lg:relative lg:w-60": true, // 在大屏幕上始终显示
|
|
|
|
}
|
|
|
|
)}
|
|
|
|
style={{ width: "240px" }}
|
|
|
|
>
|
|
|
|
{/* 侧边栏内容 */}
|
2024-09-18 21:44:09 +08:00
|
|
|
<div className="w-full flex mt-2 mb-6 min-h-[34px]">
|
|
|
|
<Link className="w-full flex justify-center" href="/">
|
2024-09-18 16:35:40 +08:00
|
|
|
<Image
|
|
|
|
alt="typeframes.ai logo"
|
|
|
|
fetchPriority="high"
|
2024-09-18 21:44:09 +08:00
|
|
|
width={locale === "en" ? 168 : 128}
|
|
|
|
height={48}
|
2024-09-18 16:35:40 +08:00
|
|
|
decoding="async"
|
|
|
|
style={{ color: "transparent" }}
|
2024-09-18 21:44:09 +08:00
|
|
|
className=" object-contain"
|
|
|
|
src={
|
|
|
|
locale === "en"
|
|
|
|
? "/logo_en.png"
|
|
|
|
: "/logo_zh.png"
|
|
|
|
}
|
2024-09-18 16:35:40 +08:00
|
|
|
/>
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<Link
|
|
|
|
href="/create"
|
|
|
|
className="inline-flex gap-1.5 items-center justify-center whitespace-nowrap z-0 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-black border border-black relative text-white px-4 py-2 rounded-md w-full"
|
|
|
|
>
|
|
|
|
<span className="btn-icon">
|
|
|
|
<FaPlus size="1em" />
|
|
|
|
</span>
|
|
|
|
{t("createNewVideo")}
|
|
|
|
</Link>
|
|
|
|
|
|
|
|
<div className="mt-4 flex flex-col w-full gap-4 items-start justify-between h-full">
|
|
|
|
<div className="flex flex-col gap-2 w-full">
|
|
|
|
{menu.map((menuGroup) => {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className="flex flex-col gap-2"
|
|
|
|
key={menuGroup.title}
|
|
|
|
>
|
|
|
|
<div className="mt-4 mb-1 text-[10px] text-info tracking-wider">
|
|
|
|
{menuGroup.title}
|
|
|
|
</div>
|
|
|
|
{menuGroup.items.map((item) => {
|
|
|
|
return (
|
|
|
|
<div key={item.name}>
|
|
|
|
<Link
|
|
|
|
className={classNames(
|
|
|
|
"gap-2 cursor-pointer text-sm font-medium flex justify-start items-center p-2 hover:bg-base-100 rounded-md",
|
|
|
|
{
|
|
|
|
"bg-base-100/50":
|
|
|
|
pathname ===
|
|
|
|
item.href,
|
|
|
|
"text-info":
|
|
|
|
pathname !==
|
|
|
|
item.href,
|
|
|
|
}
|
|
|
|
)}
|
|
|
|
href={item.href}
|
|
|
|
>
|
|
|
|
<MdOutlineHome size={18} />
|
|
|
|
{item.name}
|
|
|
|
</Link>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="flex flex-col w-full items-start">
|
|
|
|
<div className="relative flex items-center text-sm w-fit mb-4">
|
|
|
|
<div className="z-[0]">
|
|
|
|
<Link
|
|
|
|
className="btn-tf btn-tf-sm btn-tf-lock-price bg-black text-white cursor-pointer flex items-center gap-2.5"
|
|
|
|
href="/pricing"
|
|
|
|
>
|
|
|
|
<BsLightningChargeFill size={16} />
|
|
|
|
{t("upgradeNow")}
|
|
|
|
</Link>
|
|
|
|
<div className="absolute z-[-1] -inset-0.5 bg-gradient-to-r from-sky-400 to-fuchsia-400 rounded-full blur opacity-75 group-hover:opacity-100 transition duration-1000 group-hover:duration-200 animate-tilt"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="mt-2 flex justify-between items-center w-full text-info bg-gray-50 border border-base-100 rounded-lg px-2.5 py-1.5">
|
|
|
|
<div className="flex flex-col justify-center">
|
|
|
|
<div className="text-sm text-gray-600 font-medium overflow-hidden text-ellipsis max-w-[142px]">
|
|
|
|
{user?.username
|
|
|
|
? user.username
|
|
|
|
: user?.email}
|
|
|
|
</div>
|
|
|
|
<div className="relative flex items-center text-xs cursor-pointer">
|
|
|
|
{t("aiCredits")}: {user?.points}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="cursor-pointer" onClick={logout}>
|
|
|
|
<MdLogout size={16} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* 点击空白处关闭侧边栏 */}
|
|
|
|
{sidebarOpen && (
|
|
|
|
<div
|
|
|
|
className="fixed inset-0 bg-black bg-opacity-50 z-10 lg:hidden"
|
|
|
|
onClick={toggleSidebar}
|
|
|
|
></div>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|