gh_UserManager/apps/web/components/admin/admin-layout.tsx
bermooda-company 54d5891edf user
2026-08-23 23:59:14 +03:30

159 lines
6.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useState, ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
LayoutDashboard,
Users,
Building2,
Shield,
LogOut,
Menu,
X,
Fingerprint,
Activity,
Bell,
Mail,
} from "lucide-react";
import { useLanguage } from "@/components/language-provider";
import { useAuth } from "@/components/auth/auth-provider";
import { RouteGuard } from "@/components/auth/route-guard";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const navItems = [
{ key: "overview", labelFa: "نمایش کلی", labelEn: "Overview", href: "/admin", icon: LayoutDashboard },
{ key: "users", labelFa: "کاربران", labelEn: "Users", href: "/admin/users", icon: Users },
{ key: "members", labelFa: "اعضا", labelEn: "Members", href: "/admin/members", icon: Users },
{ key: "invitations", labelFa: "دعوت‌ها", labelEn: "Invitations", href: "/admin/invitations", icon: Mail },
{ key: "orgs", labelFa: "سازمان\u200cها", labelEn: "Organizations", href: "/admin/organizations", icon: Building2 },
{ key: "products", labelFa: "محصول‌ها", labelEn: "Products", href: "/admin/products", icon: Shield },
{ key: "apps", labelFa: "اپلیکیشن‌ها", labelEn: "Applications", href: "/admin/applications", icon: Shield },
{ key: "notifications", labelFa: "اعلانات", labelEn: "Notifications", href: "/admin/notifications", icon: Bell },
{ key: "sessions", labelFa: "نشست\u200cها", labelEn: "Sessions", href: "/admin/sessions", icon: Activity },
{ key: "events", labelFa: "رویدادهای امنیتی", labelEn: "Security Events", href: "/admin/events", icon: Activity },
{ key: "security", labelFa: "امنیت", labelEn: "Security", href: "/admin/security", icon: Fingerprint },
];
export function AdminLayout({ children, className }: { children: ReactNode; className?: string }) {
const { lang, toggle, dir } = useLanguage();
const { logout } = useAuth();
const pathname = usePathname();
const [sidebarOpen, setSidebarOpen] = useState(false);
const t = (fa: string, en: string) => (lang === "fa" ? fa : en);
return (
<RouteGuard>
<div className="flex h-screen overflow-hidden bg-ink text-slate-300">
{sidebarOpen && (
<div
className="fixed inset-0 z-40 bg-black/50 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
<aside
className={cn(
"fixed inset-y-0 z-50 flex h-full w-64 flex-col overflow-y-auto bg-ink-soft border-l border-white/10 transition-transform duration-300 lg:translate-x-0",
sidebarOpen ? "translate-x-0" : "-translate-x-full lg:translate-x-0",
dir === "rtl" ? "lg:border-r" : "lg:border-l",
)}
>
<div className="flex items-center justify-between p-4 border-b border-white/10">
<div className="flex items-center gap-2.5">
<span className="flex h-8 w-8 items-center justify-center rounded-lg bg-brand-gradient text-sm font-bold text-white">
<Fingerprint className="h-5 w-5" />
</span>
<span className="font-semibold text-white">
{process.env.NEXT_PUBLIC_SITE_NAME || "MyAccount Hamsoo"}
</span>
</div>
<Button
variant="ghost"
size="sm"
className="lg:hidden"
onClick={() => setSidebarOpen(false)}
>
<X className="h-4 w-4" />
</Button>
</div>
<nav className="flex-1 p-4 space-y-1">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.href;
return (
<Link
key={item.key}
href={item.href}
className={cn(
"flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition",
isActive
? "bg-brand-500/15 text-brand-200"
: "text-slate-400 hover:text-white hover:bg-white/5",
)}
>
<Icon className="h-4 w-4" />
{t(item.labelFa, item.labelEn)}
</Link>
);
})}
</nav>
<div className="border-t border-white/10 p-4">
<div className="flex items-center justify-between mb-3">
<span className="text-xs text-slate-500">{lang === "fa" ? "زبان" : "Language"}</span>
<button
onClick={toggle}
className="px-2 py-1 rounded text-xs bg-white/5 hover:bg-white/10"
>
{lang === "fa" ? "EN" : "FA"}
</button>
</div>
<Button
variant="ghost"
size="sm"
className="w-full"
onClick={async () => {
await logout();
window.location.href = `/login?next=${encodeURIComponent(pathname)}`;
}}
>
<LogOut className="h-4 w-4" />
{t("خروج", "Logout")}
</Button>
</div>
</aside>
<main className={cn("flex-1 overflow-auto", dir === "rtl" ? "lg:mr-72" : "lg:ml-72")}>
<header className="sticky top-0 z-10 flex items-center justify-between border-b border-white/10 bg-ink/80 px-6 py-4 backdrop-blur">
<div className="flex items-center gap-4">
<Button
variant="ghost"
size="sm"
className="lg:hidden"
onClick={() => setSidebarOpen(true)}
>
<Menu className="h-5 w-5" />
</Button>
<h1 className="text-xl font-bold text-white">
{navItems.find((i) => pathname === i.href)
? t(
navItems.find((i) => pathname === i.href)?.labelFa || "",
navItems.find((i) => pathname === i.href)?.labelEn || "",
)
: t("نمایش کلی", "Overview")}
</h1>
</div>
</header>
<div className="p-6">
{children}
</div>
</main>
</div>
</RouteGuard>
);
}