2025-08-04 17:45:44 +02:00
|
|
|
import { AlignLeft, Home } from "lucide-react";
|
2025-08-28 18:27:07 +02:00
|
|
|
import Link, { type LinkProps } from "next/link";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useRouter } from "next/navigation";
|
2025-08-28 18:27:07 +02:00
|
|
|
import type { ReactNode } from "react";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { IconMatrix } from "~/components/IconComponents";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { LangToggle } from "~/components/navbar/lang-toggle";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { UserHeaderSection } from "~/components/navbar/login-button";
|
|
|
|
|
import { ASvg, LogoSvg } from "~/components/svgs";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { Separator } from "~/components/ui/separator";
|
|
|
|
|
import {
|
|
|
|
|
Sheet,
|
|
|
|
|
SheetContent,
|
|
|
|
|
SheetDescription,
|
|
|
|
|
SheetHeader,
|
|
|
|
|
SheetTitle,
|
|
|
|
|
SheetTrigger,
|
|
|
|
|
} from "~/components/ui/sheet";
|
2025-08-04 17:45:44 +02:00
|
|
|
import { useMediaQuery } from "~/hooks/use-media-query";
|
2025-08-28 18:27:07 +02:00
|
|
|
import { cn } from "~/lib/utils";
|
|
|
|
|
import { useTranslation } from "~/providers/I18nProvider";
|
|
|
|
|
import { useSession } from "~/providers/SessionProvider";
|
2025-08-04 17:45:44 +02:00
|
|
|
|
|
|
|
|
type MobileNavProps = {
|
2025-08-28 18:27:07 +02:00
|
|
|
pathname: string;
|
|
|
|
|
open: boolean;
|
|
|
|
|
setOpen: (status: boolean) => void;
|
|
|
|
|
userOpen: boolean;
|
|
|
|
|
setUserOpen: (status: boolean) => void;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function MobileNav({
|
2025-08-28 18:27:07 +02:00
|
|
|
pathname,
|
|
|
|
|
open,
|
|
|
|
|
setOpen,
|
|
|
|
|
userOpen,
|
|
|
|
|
setUserOpen,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: MobileNavProps) {
|
2025-08-28 18:27:07 +02:00
|
|
|
const session = useSession();
|
|
|
|
|
const { t } = useTranslation();
|
|
|
|
|
const verySmallScreen = useMediaQuery("(max-width: 320px)");
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
<Sheet open={open} onOpenChange={setOpen}>
|
|
|
|
|
<SheetTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
aria-label="Toggle Mobile Menu"
|
|
|
|
|
variant="ghost"
|
|
|
|
|
className="mr-2 size-8 p-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden [&_svg]:size-6"
|
|
|
|
|
>
|
|
|
|
|
<AlignLeft />
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<span className="sr-only">Toggle Menu</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</SheetTrigger>
|
|
|
|
|
<SheetContent side="left" className="z-500 p-0">
|
|
|
|
|
<SheetHeader>
|
|
|
|
|
<SheetTitle className="sr-only">Mobile Menu</SheetTitle>
|
|
|
|
|
<SheetDescription className="sr-only">
|
|
|
|
|
This is the mobile menu
|
|
|
|
|
</SheetDescription>
|
|
|
|
|
</SheetHeader>
|
|
|
|
|
<div className="flex h-full flex-col space-y-2 overflow-y-auto px-3 pb-4">
|
|
|
|
|
<MobileLink
|
|
|
|
|
href="/"
|
|
|
|
|
aria-label="mainNavLinkHome"
|
|
|
|
|
className="ml-2 flex items-center gap-3"
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
>
|
|
|
|
|
<Home className="size-6" />{" "}
|
|
|
|
|
<span className="text-xl font-medium">Home</span>
|
|
|
|
|
</MobileLink>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex h-full flex-col space-y-2 pt-4">
|
|
|
|
|
{pathname.startsWith("/area-riservata") && (
|
|
|
|
|
<>
|
|
|
|
|
{session?.user && (
|
|
|
|
|
<AreaRiservataLinks
|
|
|
|
|
isAdmin={session.user.isAdmin}
|
|
|
|
|
pathname={pathname}
|
|
|
|
|
setOpen={setOpen}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<Separator />
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="text-primary flex h-full grow flex-col gap-3">
|
|
|
|
|
{t.nav.mainNav.map((item) => (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.href}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix
|
|
|
|
|
key={item.title}
|
|
|
|
|
type={item.icon}
|
|
|
|
|
className="size-5"
|
|
|
|
|
/>{" "}
|
|
|
|
|
{item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
))}
|
|
|
|
|
<Separator />
|
|
|
|
|
</div>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex h-fit flex-col gap-4 pb-2">
|
|
|
|
|
<UserHeaderSection
|
|
|
|
|
open={userOpen}
|
|
|
|
|
setOpen={setUserOpen}
|
|
|
|
|
setSidebar={setOpen}
|
|
|
|
|
inSidebar
|
|
|
|
|
/>
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
<div className="flex items-center justify-center gap-6">
|
|
|
|
|
<LangToggle />
|
|
|
|
|
{/* <ThemeSwitch /> */}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</SheetContent>
|
|
|
|
|
</Sheet>
|
|
|
|
|
<Link
|
|
|
|
|
href="/"
|
|
|
|
|
className="ml-3 flex shrink-0 items-center md:hidden"
|
|
|
|
|
aria-label="mainNavLink"
|
|
|
|
|
>
|
|
|
|
|
{verySmallScreen ? (
|
|
|
|
|
<ASvg className="h-8 w-auto" />
|
|
|
|
|
) : (
|
|
|
|
|
<LogoSvg className="xs:h-8 [&>text]:fill-primary [&>text]:stroke-primary h-6 w-auto text-neutral-700 sm:h-10 [&>g]:fill-red-500 dark:[&>g]:fill-neutral-100 dark:[&>text]:fill-neutral-100 dark:[&>text]:stroke-neutral-100" />
|
|
|
|
|
)}
|
|
|
|
|
</Link>
|
|
|
|
|
</>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface MobileLinkProps extends LinkProps {
|
2025-08-28 18:27:07 +02:00
|
|
|
onOpenChange?: (open: boolean) => void;
|
|
|
|
|
children: ReactNode;
|
|
|
|
|
className?: string;
|
|
|
|
|
disabled?: boolean;
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function MobileLink({
|
2025-08-28 18:27:07 +02:00
|
|
|
href,
|
|
|
|
|
onOpenChange,
|
|
|
|
|
className,
|
|
|
|
|
children,
|
|
|
|
|
disabled,
|
|
|
|
|
...props
|
2025-08-04 17:45:44 +02:00
|
|
|
}: MobileLinkProps) {
|
2025-08-28 18:27:07 +02:00
|
|
|
const router = useRouter();
|
|
|
|
|
return (
|
|
|
|
|
<Link
|
|
|
|
|
aria-label="mobileLink"
|
|
|
|
|
href={href}
|
|
|
|
|
onClick={(e) => {
|
|
|
|
|
if (disabled) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
onOpenChange?.(false);
|
|
|
|
|
} else {
|
|
|
|
|
router.push(
|
|
|
|
|
href.toString(),
|
|
|
|
|
);
|
|
|
|
|
onOpenChange?.(false);
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
className={cn(className)}
|
|
|
|
|
{...props}
|
|
|
|
|
>
|
|
|
|
|
{children}
|
|
|
|
|
</Link>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AreaRiservataLinksProps = {
|
2025-08-28 18:27:07 +02:00
|
|
|
isAdmin: boolean;
|
|
|
|
|
setOpen?: (status: boolean) => void;
|
|
|
|
|
pathname: string;
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
const AreaRiservataLinks = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
isAdmin,
|
|
|
|
|
setOpen,
|
|
|
|
|
pathname,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: AreaRiservataLinksProps) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
return (
|
|
|
|
|
<div className="text-primary flex flex-col space-y-4 text-base">
|
|
|
|
|
{isAdmin
|
|
|
|
|
? t.nav.areaRiservataAdminNav.map((item) => {
|
|
|
|
|
if (item.items !== undefined) {
|
|
|
|
|
return (
|
|
|
|
|
<div key={item.title}>
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
{item.title}
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<ul>
|
|
|
|
|
{item.items.map((item) => {
|
|
|
|
|
return (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
`hover:text-accent-foreground mx-4 block w-full border-l px-6 py-2 duration-150 hover:border-neutral-800`,
|
|
|
|
|
item.href === pathname &&
|
|
|
|
|
"border-neutral-900 text-neutral-900",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
|
|
|
|
{item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
: t.nav.areaRiservataUserNav.map((item) => (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
|
|
|
|
{item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const ARMinimizableLinks = ({
|
2025-08-28 18:27:07 +02:00
|
|
|
isAdmin,
|
|
|
|
|
setOpen,
|
|
|
|
|
pathname,
|
|
|
|
|
minimized,
|
2025-08-04 17:45:44 +02:00
|
|
|
}: {
|
2025-08-28 18:27:07 +02:00
|
|
|
isAdmin: boolean;
|
|
|
|
|
setOpen?: (status: boolean) => void;
|
|
|
|
|
pathname: string;
|
|
|
|
|
minimized: boolean;
|
2025-08-04 17:45:44 +02:00
|
|
|
}) => {
|
2025-08-28 18:27:07 +02:00
|
|
|
const { t } = useTranslation();
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"text-primary flex w-full flex-col space-y-4 text-base",
|
|
|
|
|
minimized && "justify-center",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{isAdmin
|
|
|
|
|
? t.nav.areaRiservataAdminNav.map((item) => {
|
|
|
|
|
if (item.items !== undefined) {
|
|
|
|
|
return (
|
|
|
|
|
<div key={item.title}>
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
minimized && "justify-center",
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
2025-08-04 17:45:44 +02:00
|
|
|
|
2025-08-28 18:27:07 +02:00
|
|
|
{!minimized && item.title}
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<ul className="truncate">
|
|
|
|
|
{item.items.map((item) => {
|
|
|
|
|
return (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
`hover:text-accent-foreground mx-4 block w-full border-l px-6 py-2 duration-150 hover:border-neutral-800`,
|
|
|
|
|
item.href === pathname &&
|
|
|
|
|
"border-neutral-900 text-neutral-900",
|
|
|
|
|
minimized && "hidden",
|
|
|
|
|
)}
|
|
|
|
|
disabled={minimized}
|
|
|
|
|
>
|
|
|
|
|
{item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
minimized && "justify-center",
|
|
|
|
|
)}
|
|
|
|
|
disabled={minimized}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
|
|
|
|
{!minimized && item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
);
|
|
|
|
|
})
|
|
|
|
|
: t.nav.areaRiservataUserNav.map((item) => (
|
|
|
|
|
<MobileLink
|
|
|
|
|
key={item.title}
|
|
|
|
|
href={item.href}
|
|
|
|
|
onOpenChange={setOpen}
|
|
|
|
|
className={cn(
|
|
|
|
|
"flex items-center gap-3 rounded-lg px-2 py-1 font-medium",
|
|
|
|
|
item.href === pathname && "bg-muted",
|
|
|
|
|
minimized && "justify-center",
|
|
|
|
|
)}
|
|
|
|
|
disabled={minimized}
|
|
|
|
|
>
|
|
|
|
|
<IconMatrix type={item.icon} className="size-5" />
|
|
|
|
|
{!minimized && item.title}
|
|
|
|
|
</MobileLink>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2025-08-04 17:45:44 +02:00
|
|
|
};
|