296 lines
9.5 KiB
TypeScript
296 lines
9.5 KiB
TypeScript
"use client";
|
|
|
|
import { Menu } from "lucide-react";
|
|
import React from "react";
|
|
|
|
import {
|
|
Accordion,
|
|
AccordionContent,
|
|
AccordionItem,
|
|
AccordionTrigger,
|
|
} from "@/components/ui/accordion";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuContent,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
NavigationMenuTrigger,
|
|
navigationMenuTriggerStyle,
|
|
} from "@/components/ui/navigation-menu";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const NAV_LOGO = {
|
|
url: "https://www.ShadcnBlocks",
|
|
src: "/block-media/shadcnblocks/block/logos/ShadcnBlockscom-icon.svg",
|
|
alt: "لوگو",
|
|
title: "وندوک",
|
|
};
|
|
|
|
const NAV_ITEMS = [
|
|
{
|
|
name: "محصولات",
|
|
link: "/",
|
|
hasSubmenu: true,
|
|
submenu: [
|
|
{
|
|
title: "معرفی",
|
|
href: "/docs",
|
|
description:
|
|
"ماژولهای آماده CRM برای مخاطبین، فرصتها و گزارش فروش.",
|
|
},
|
|
{
|
|
title: "راهاندازی",
|
|
href: "/docs/installation",
|
|
description: "نحوه ساخت فضای کاری و دعوت اعضای تیم فروش.",
|
|
},
|
|
{
|
|
title: "فیلدهای سفارشی",
|
|
href: "/docs/primitives/typography",
|
|
description: "تعریف فیلدها، برچسبها و مراحل قیف فروش.",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: "منابع",
|
|
link: "/",
|
|
hasSubmenu: true,
|
|
submenu: [
|
|
{
|
|
title: "هشدار فرصت",
|
|
href: "/docs/primitives/alert-dialog",
|
|
description:
|
|
"هشدار ازدسترفتن معامله و اقدام پیشنهادی برای نماینده.",
|
|
},
|
|
{
|
|
title: "کارت مخاطب",
|
|
href: "/docs/primitives/hover-card",
|
|
description: "پیشنمایش سریع مخاطب، شرکت و فعالیتهای اخیر.",
|
|
},
|
|
{
|
|
title: "پیشرفت معامله",
|
|
href: "/docs/primitives/progress",
|
|
description: "نمایش مرحله فعلی فرصت در قیف فروش.",
|
|
},
|
|
{
|
|
title: "فهرست فعالیت",
|
|
href: "/docs/primitives/scroll-area",
|
|
description: "مرور تایملاین تماسها، ایمیلها و یادداشتها.",
|
|
},
|
|
{
|
|
title: "نمایهای فروش",
|
|
href: "/docs/primitives/tabs",
|
|
description: "جابهجایی بین لیست، کانبان و پیشبینی.",
|
|
},
|
|
{
|
|
title: "راهنمای زمینه",
|
|
href: "/docs/primitives/tooltip",
|
|
description:
|
|
"توضیح متریکها و فیلدها در لحظه استفاده.",
|
|
},
|
|
],
|
|
},
|
|
{ name: "یکپارچهسازی", link: "/" },
|
|
];
|
|
|
|
interface Navbar18Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Navbar18 = ({ className }: Navbar18Props) => {
|
|
return (
|
|
<section
|
|
lang="fa"
|
|
className={cn(
|
|
"relative z-10 mx-auto flex max-w-full items-center justify-between overflow-visible border border-t-0 bg-muted px-6 py-3 text-start md:w-fit md:rounded-b-2xl lg:gap-4",
|
|
className,
|
|
)}
|
|
>
|
|
<a href={NAV_LOGO.url} className="flex items-center gap-1">
|
|
<img
|
|
src={NAV_LOGO.src}
|
|
className="max-h-5 dark:invert"
|
|
alt={NAV_LOGO.alt}
|
|
/>
|
|
<span className="sr-only">{NAV_LOGO.title}</span>
|
|
</a>
|
|
|
|
<MobileNav />
|
|
|
|
<NavigationMenu
|
|
className="relative z-50 hidden md:flex"
|
|
dir="rtl"
|
|
viewport={false}
|
|
>
|
|
<NavigationMenuList className="h-full w-full">
|
|
{NAV_ITEMS.map((item) =>
|
|
item.hasSubmenu ? (
|
|
<NavigationMenuItem key={item.name} className="rounded-2xl">
|
|
<NavigationMenuTrigger className="bg-transparent px-2 py-1 text-start text-xs [&_svg]:ms-1 [&_svg]:!me-0 [&_svg]:!ml-0">
|
|
{item.name}
|
|
</NavigationMenuTrigger>
|
|
<NavigationMenuContent
|
|
dir="rtl"
|
|
className="start-0 end-auto left-auto z-50 rounded-2xl text-start"
|
|
>
|
|
<ul
|
|
dir="rtl"
|
|
className={cn(
|
|
"grid w-full gap-2 p-2 text-start",
|
|
(item.submenu?.length ?? 0) > 3
|
|
? "w-[300px] md:w-[350px] md:grid-cols-2 lg:w-[400px]"
|
|
: "md:w-[150px] lg:w-[200px]",
|
|
)}
|
|
>
|
|
{item.submenu?.map((sub) => (
|
|
<ListItem
|
|
key={sub.title}
|
|
title={sub.title}
|
|
href={sub.href}
|
|
>
|
|
{sub.description}
|
|
</ListItem>
|
|
))}
|
|
</ul>
|
|
</NavigationMenuContent>
|
|
</NavigationMenuItem>
|
|
) : (
|
|
<NavigationMenuItem key={item.name}>
|
|
<NavigationMenuLink
|
|
href={item.link}
|
|
className={cn(
|
|
navigationMenuTriggerStyle(),
|
|
"bg-transparent px-3 py-1.5 text-start text-xs",
|
|
)}
|
|
>
|
|
{item.name}
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
),
|
|
)}
|
|
</NavigationMenuList>
|
|
</NavigationMenu>
|
|
|
|
<div className="hidden md:block">
|
|
<Button
|
|
variant="outline"
|
|
className="h-auto rounded-lg px-3 py-1.5 text-xs"
|
|
>
|
|
ورود
|
|
</Button>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
const ListItem = React.forwardRef<
|
|
React.ElementRef<"a">,
|
|
React.ComponentPropsWithoutRef<"a">
|
|
>(({ className, title, children, ...props }, ref) => {
|
|
return (
|
|
<li>
|
|
<NavigationMenuLink asChild>
|
|
<a
|
|
ref={ref}
|
|
className={cn(
|
|
"flex w-full flex-col items-start gap-1 rounded-md p-2 text-start text-xs leading-none no-underline transition-colors outline-none select-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
<div className="w-full text-start text-xs leading-none font-medium">
|
|
{title}
|
|
</div>
|
|
<p className="w-full text-start text-xs leading-snug text-muted-foreground">
|
|
{children}
|
|
</p>
|
|
</a>
|
|
</NavigationMenuLink>
|
|
</li>
|
|
);
|
|
});
|
|
ListItem.displayName = "ListItem";
|
|
|
|
const MobileNav = () => {
|
|
return (
|
|
<div className="me-2 flex items-center justify-center md:hidden">
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<button
|
|
type="button"
|
|
className="flex size-8 items-center justify-center rounded-md text-foreground hover:bg-accent"
|
|
aria-label="باز کردن منو"
|
|
>
|
|
<Menu className="size-5" />
|
|
</button>
|
|
</PopoverTrigger>
|
|
|
|
<PopoverContent
|
|
dir="rtl"
|
|
align="start"
|
|
sideOffset={8}
|
|
className="z-50 w-screen max-w-xs overflow-visible p-0 text-start"
|
|
>
|
|
<div className="w-full bg-card/80 pt-2 text-start text-foreground backdrop-blur-md">
|
|
<Accordion type="single" collapsible className="w-full text-start">
|
|
{NAV_ITEMS.map((navItem) =>
|
|
navItem.hasSubmenu ? (
|
|
<AccordionItem
|
|
key={navItem.name}
|
|
value={navItem.name}
|
|
className="border-b-0"
|
|
>
|
|
<AccordionTrigger className="justify-start px-4 py-3 text-start text-xs hover:bg-accent hover:no-underline [&>svg]:ms-auto [&>svg]:me-0">
|
|
<span className="text-foreground">{navItem.name}</span>
|
|
</AccordionTrigger>
|
|
<AccordionContent className="rounded-2xl text-start">
|
|
<div className="ms-4 border-s-2 border-muted ps-2">
|
|
<ul className="py-1 text-start">
|
|
{navItem.submenu?.map((sub) => (
|
|
<li
|
|
key={sub.title}
|
|
className="px-2 py-2 text-xs hover:bg-accent"
|
|
>
|
|
<a
|
|
href={sub.href}
|
|
className="block w-full text-start"
|
|
>
|
|
{sub.title}
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
) : (
|
|
<div
|
|
key={navItem.name}
|
|
className="rounded-lg px-4 py-3 text-start text-xs hover:bg-accent"
|
|
>
|
|
<a href={navItem.link} className="block w-full text-start">
|
|
<span className="text-foreground">{navItem.name}</span>
|
|
</a>
|
|
</div>
|
|
),
|
|
)}
|
|
</Accordion>
|
|
<div className="flex flex-col gap-2 py-2">
|
|
<Button variant="secondary" className="px-3 text-xs">
|
|
ورود
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</div>
|
|
);
|
|
};
|
|
export { Navbar18 };
|