247 lines
7.4 KiB
JavaScript
247 lines
7.4 KiB
JavaScript
"use client";
|
|
|
|
import { usePathname, useRouter } from "next/navigation";
|
|
import { useMemo } from "react";
|
|
import Link from "next/link";
|
|
import PropTypes from "prop-types";
|
|
import ProviderIcon from "@/shared/components/ProviderIcon";
|
|
import { ThemeToggle, LanguageSwitcher } from "@/shared/components";
|
|
import NineRemoteButton from "@/shared/components/NineRemoteButton";
|
|
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config";
|
|
import { translate } from "@/i18n/runtime";
|
|
|
|
const getPageInfo = (pathname) => {
|
|
if (!pathname) return { title: "", description: "", breadcrumbs: [] };
|
|
|
|
// Provider detail page: /dashboard/providers/[id]
|
|
const providerMatch = pathname.match(/\/providers\/([^/]+)$/);
|
|
if (providerMatch) {
|
|
const providerId = providerMatch[1];
|
|
const providerInfo =
|
|
OAUTH_PROVIDERS[providerId] || APIKEY_PROVIDERS[providerId];
|
|
if (providerInfo) {
|
|
return {
|
|
title: providerInfo.name,
|
|
description: "",
|
|
breadcrumbs: [
|
|
{ label: "Providers", href: "/dashboard/providers" },
|
|
{
|
|
label: providerInfo.name,
|
|
image: `/providers/${providerInfo.id}.png`,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
}
|
|
|
|
if (pathname.includes("/providers"))
|
|
return {
|
|
title: "Providers",
|
|
description: "Manage your AI provider connections",
|
|
icon: "dns",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/combos"))
|
|
return {
|
|
title: "Combos",
|
|
description: "Model combos with fallback",
|
|
icon: "layers",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/usage"))
|
|
return {
|
|
title: "Usage & Analytics",
|
|
description:
|
|
"Monitor your API usage, token consumption, and request logs",
|
|
icon: "bar_chart",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/quota"))
|
|
return {
|
|
title: "Quota Tracker",
|
|
description: "Track and manage your API quota limits",
|
|
icon: "data_usage",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/mitm"))
|
|
return {
|
|
title: "MITM Proxy",
|
|
description: "Intercept CLI tool traffic and route through 9Router",
|
|
icon: "security",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/cli-tools"))
|
|
return {
|
|
title: "CLI Tools",
|
|
description: "Configure CLI tools",
|
|
icon: "terminal",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/proxy-pools"))
|
|
return {
|
|
title: "Proxy Pools",
|
|
description: "Manage your proxy pool configurations",
|
|
icon: "lan",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/endpoint"))
|
|
return {
|
|
title: "Endpoint",
|
|
description: "API endpoint configuration",
|
|
icon: "api",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/profile"))
|
|
return {
|
|
title: "Settings",
|
|
description: "Manage your preferences",
|
|
icon: "settings",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/translator"))
|
|
return {
|
|
title: "Translator",
|
|
description: "Debug translation flow between formats",
|
|
icon: "translate",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname.includes("/console-log"))
|
|
return {
|
|
title: "Console Log",
|
|
description: "Live server console output",
|
|
icon: "monitor",
|
|
breadcrumbs: [],
|
|
};
|
|
if (pathname === "/dashboard")
|
|
return {
|
|
title: "Endpoint",
|
|
description: "API endpoint configuration",
|
|
icon: "api",
|
|
breadcrumbs: [],
|
|
};
|
|
return { title: "", description: "", breadcrumbs: [] };
|
|
};
|
|
|
|
export default function Header({ onMenuClick, showMenuButton = true }) {
|
|
const pathname = usePathname();
|
|
const router = useRouter();
|
|
|
|
// Memoize page info to prevent unnecessary recalculations
|
|
const pageInfo = useMemo(() => getPageInfo(pathname), [pathname]);
|
|
const { title, description, icon, breadcrumbs } = pageInfo;
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
const res = await fetch("/api/auth/logout", { method: "POST" });
|
|
if (res.ok) {
|
|
router.push("/login");
|
|
router.refresh();
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to logout:", err);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<header className="flex items-center justify-between px-8 py-5 border-b border-black/5 dark:border-white/5 bg-bg/80 backdrop-blur-xl z-10 sticky top-0">
|
|
{/* Mobile menu button */}
|
|
<div className="flex items-center gap-3 lg:hidden">
|
|
{showMenuButton && (
|
|
<button
|
|
onClick={onMenuClick}
|
|
className="text-text-main hover:text-primary transition-colors"
|
|
>
|
|
<span className="material-symbols-outlined">menu</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Page title with breadcrumbs - desktop */}
|
|
<div className="hidden lg:flex flex-col">
|
|
{breadcrumbs.length > 0 ? (
|
|
<div className="flex items-center gap-2">
|
|
{breadcrumbs.map((crumb, index) => (
|
|
<div
|
|
key={`${crumb.label}-${crumb.href || "current"}`}
|
|
className="flex items-center gap-2"
|
|
>
|
|
{index > 0 && (
|
|
<span className="material-symbols-outlined text-text-muted text-base">
|
|
chevron_right
|
|
</span>
|
|
)}
|
|
{crumb.href ? (
|
|
<Link
|
|
href={crumb.href}
|
|
className="text-text-muted hover:text-primary transition-colors"
|
|
>
|
|
{crumb.label}
|
|
</Link>
|
|
) : (
|
|
<div className="flex items-center gap-2">
|
|
{crumb.image && (
|
|
<ProviderIcon
|
|
src={crumb.image}
|
|
alt={crumb.label}
|
|
size={28}
|
|
className="object-contain rounded max-w-[28px] max-h-[28px]"
|
|
fallbackText={crumb.label.slice(0, 2).toUpperCase()}
|
|
/>
|
|
)}
|
|
<h1 className="text-2xl font-semibold text-text-main tracking-tight">
|
|
{translate(crumb.label)}
|
|
</h1>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : title ? (
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
{icon && (
|
|
<span className="material-symbols-outlined text-primary text-2xl">
|
|
{icon}
|
|
</span>
|
|
)}
|
|
<h1 className="text-2xl font-semibold tracking-tight">
|
|
{translate(title)}
|
|
</h1>
|
|
</div>
|
|
{description && (
|
|
<p className="text-sm text-text-muted">
|
|
{translate(description)}
|
|
</p>
|
|
)}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
|
|
{/* Right actions */}
|
|
<div className="flex items-center gap-3 ml-auto">
|
|
{/* 9Remote button */}
|
|
<NineRemoteButton />
|
|
|
|
{/* Language switcher */}
|
|
<LanguageSwitcher />
|
|
|
|
{/* Theme toggle */}
|
|
<ThemeToggle />
|
|
|
|
{/* Logout button */}
|
|
<button
|
|
onClick={handleLogout}
|
|
className="flex items-center justify-center p-2 rounded-lg text-text-muted hover:text-red-500 hover:bg-red-500/10 transition-all"
|
|
title="Logout"
|
|
>
|
|
<span className="material-symbols-outlined">logout</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
Header.propTypes = {
|
|
onMenuClick: PropTypes.func,
|
|
showMenuButton: PropTypes.bool,
|
|
};
|