diff --git a/src/app/[locale]/admin/layout.tsx b/src/app/[locale]/admin/layout.tsx index 76eeccb..b0fd79f 100644 --- a/src/app/[locale]/admin/layout.tsx +++ b/src/app/[locale]/admin/layout.tsx @@ -1,6 +1,7 @@ import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminShell, type NavGroup } from "@/components/admin/AdminShell"; import { getCurrentUser } from "@/lib/auth/session"; export const dynamic = "force-dynamic"; @@ -15,58 +16,66 @@ export default async function AdminLayout({ redirect("/dashboard"); } const t = await getTranslations("auto.appAdminLayout"); - const links: { href: string; label: string }[] = [ - { href: "/admin/stats", label: t("stats") }, - { href: "/admin/categories", label: t("categories") }, - { href: "/admin/templates", label: t("templates") }, - { href: "/admin/projects", label: t("projects") }, - { href: "/admin/ranking", label: t("ranking") }, - { href: "/admin/tags", label: t("tags") }, - { href: "/admin/fonts", label: t("fonts") }, - { href: "/admin/music", label: t("music") }, - { href: "/admin/blogs", label: t("blogs") }, - { href: "/admin/slides", label: t("slides") }, - { href: "/admin/home-events", label: t("homeEvents") }, - { href: "/admin/routes", label: t("routes") }, - { href: "/admin/comments", label: t("comments") }, - { href: "/admin/files", label: t("media") }, - { href: "/admin/ai", label: t("aiContent") }, - { href: "/admin/messaging", label: t("messaging") }, - { href: "/admin/integrations", label: t("integrations") }, - { href: "/admin/marketing", label: t("marketing") }, - { href: "/admin/crm", label: t("crm") }, - { href: "/admin/users", label: t("users") }, - { href: "/admin/plans", label: t("plans") }, - { href: "/admin/discounts", label: t("discounts") }, - { href: "/admin/settings", label: t("siteSettings") }, - { href: "/admin/nodes", label: t("nodes") }, - { href: "/admin/node-fonts", label: t("nodeFonts") }, - { href: "/admin/renders", label: t("renderQueue") }, - { href: "/admin/exports", label: t("exports") }, + + const groups: NavGroup[] = [ + { + title: "نمای کلی", + items: [{ href: "/admin/stats", label: t("stats") }], + }, + { + title: "محتوا", + items: [ + { href: "/admin/categories", label: t("categories") }, + { href: "/admin/templates", label: t("templates") }, + { href: "/admin/projects", label: t("projects") }, + { href: "/admin/ranking", label: t("ranking") }, + { href: "/admin/tags", label: t("tags") }, + { href: "/admin/fonts", label: t("fonts") }, + { href: "/admin/music", label: t("music") }, + { href: "/admin/blogs", label: t("blogs") }, + { href: "/admin/slides", label: t("slides") }, + { href: "/admin/home-events", label: t("homeEvents") }, + { href: "/admin/routes", label: t("routes") }, + { href: "/admin/comments", label: t("comments") }, + { href: "/admin/files", label: t("media") }, + { href: "/admin/ai", label: t("aiContent") }, + ], + }, + { + title: "رشد و ارتباطات", + items: [ + { href: "/admin/messaging", label: t("messaging") }, + { href: "/admin/integrations", label: t("integrations") }, + { href: "/admin/marketing", label: t("marketing") }, + { href: "/admin/crm", label: t("crm") }, + ], + }, + { + title: "کاربران و مالی", + items: [ + { href: "/admin/users", label: t("users") }, + { href: "/admin/plans", label: t("plans") }, + { href: "/admin/discounts", label: t("discounts") }, + ], + }, + { + title: "فارم رندر", + items: [ + { href: "/admin/nodes", label: t("nodes") }, + { href: "/admin/node-fonts", label: t("nodeFonts") }, + { href: "/admin/renders", label: t("renderQueue") }, + { href: "/admin/exports", label: t("exports") }, + ], + }, + { + title: "سیستم", + items: [{ href: "/admin/settings", label: t("siteSettings") }], + }, ]; + return ( -
- -
{children}
-
+ + {children} + ); } diff --git a/src/components/admin/AdminShell.tsx b/src/components/admin/AdminShell.tsx new file mode 100644 index 0000000..95ef663 --- /dev/null +++ b/src/components/admin/AdminShell.tsx @@ -0,0 +1,94 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { useState } from "react"; + +export interface NavItem { href: string; label: string } +export interface NavGroup { title: string; items: NavItem[] } + +export function AdminShell({ + groups, + brand, + back, + children, +}: { + groups: NavGroup[]; + brand: string; + back: string; + children: React.ReactNode; +}) { + const pathname = usePathname() ?? ""; + const [open, setOpen] = useState(false); + + // Strip a leading locale segment (e.g. /en) so fa (no prefix) and en both match. + const clean = pathname.replace(/^\/[a-z]{2}(?=\/)/, ""); + const isActive = (href: string) => clean === href || clean.startsWith(href + "/"); + const current = groups.flatMap((g) => g.items).find((i) => isActive(i.href)); + + return ( +
+ {/* Sidebar */} + + + {/* Mobile overlay */} + {open &&
setOpen(false)} />} + + {/* Main column */} +
+
+ +

{current?.label ?? brand}

+ {back} +
+
{children}
+
+
+ ); +}