TorbatYar/frontend/components/admin/AdminShell.tsx
Mortezakoohjani 0d424c500a feat(platform): complete commercial runtime consolidation
Unify commercial runtime ownership across backend and frontend so platform, experience, and hospitality modules use the shared commercial source of truth.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 11:11:53 +03:30

91 lines
3.3 KiB
TypeScript
Raw 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 { ReactNode } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { clearTokens, loginWithRedirect } from "@/lib/auth";
const NAV_ITEMS: { href: string; label: string; icon: string }[] = [
{ href: "/admin", label: "نمای کلی", icon: "▦" },
{ href: "/admin/commercial", label: "تجاری", icon: "◈" },
{ href: "/admin/tenants", label: "Tenantها", icon: "🏢" },
{ href: "/admin/users", label: "کاربران", icon: "👤" },
{ href: "/admin/commercial/plans", label: "پلن‌ها", icon: "📦" },
{ href: "/admin/commercial/capabilities", label: "قابلیت‌ها", icon: "🧩" },
{ href: "/admin/services", label: "سرویس‌ها", icon: "🛠" },
{ href: "/admin/settings", label: "حساب کاربری", icon: "⚙" },
];
function isActive(pathname: string, href: string): boolean {
if (href === "/admin") return pathname === "/admin";
return pathname === href || pathname.startsWith(`${href}/`);
}
export function AdminShell({ children }: { children: ReactNode }) {
const pathname = usePathname();
const handleLogout = () => {
clearTokens();
void loginWithRedirect("/admin");
};
return (
<div className="flex min-h-[calc(100vh-4rem)]">
<aside className="hidden w-60 shrink-0 border-l border-gray-100 bg-white/60 p-4 lg:block">
<nav className="space-y-1">
{NAV_ITEMS.map((item) => {
const active = isActive(pathname, item.href);
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-colors ${
active
? "bg-primary text-white shadow-sm"
: "text-gray-600 hover:bg-gray-100 hover:text-secondary"
}`}
>
<span aria-hidden className="text-base">
{item.icon}
</span>
{item.label}
</Link>
);
})}
</nav>
<button
type="button"
onClick={handleLogout}
className="mt-6 flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium text-red-600 transition-colors hover:bg-red-50"
>
<span aria-hidden className="text-base">
</span>
خروج
</button>
</aside>
{/* ناوبری موبایل */}
<div className="w-full">
<div className="flex gap-2 overflow-x-auto border-b border-gray-100 bg-white px-4 py-2 lg:hidden">
{NAV_ITEMS.map((item) => {
const active = isActive(pathname, item.href);
return (
<Link
key={item.href}
href={item.href}
className={`whitespace-nowrap rounded-lg px-3 py-1.5 text-xs font-medium ${
active ? "bg-primary text-white" : "text-gray-600 hover:bg-gray-100"
}`}
>
{item.label}
</Link>
);
})}
</div>
<main className="px-4 py-8 sm:px-6">{children}</main>
</div>
</div>
);
}