lms/resources/js/layouts/navbar/index.tsx
2025-12-15 12:26:23 +01:00

158 lines
6.2 KiB
TypeScript

import AppLogo from '@/components/app-logo';
import { Button } from '@/components/ui/button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { ScrollArea } from '@/components/ui/scroll-area';
import { useAuth } from '@/hooks/use-auth';
import useScreen from '@/hooks/use-screen';
import { cn } from '@/lib/utils';
import { SharedData } from '@/types/global';
import { Link, usePage } from '@inertiajs/react';
import { ChevronDown, Menu, X } from 'lucide-react';
import { Fragment, useEffect, useState } from 'react';
import Actions from './partials/actions';
interface NavbarProps {
language?: boolean;
heightCover?: boolean;
customizable?: boolean;
}
const Navbar = ({ language = false, heightCover = true, customizable = true }: NavbarProps) => {
const { props } = usePage<SharedData>();
const { ziggy, navbar, translate } = props;
const { isAdmin, isLoggedIn } = useAuth();
const [isSticky, setIsSticky] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const { screen } = useScreen();
useEffect(() => {
const handleScroll = () => {
const scrollPosition = window.scrollY;
if (scrollPosition > 100) {
setIsSticky(true);
} else {
setIsSticky(false);
}
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
const renderNavItems = (item: NavbarItem) => {
if (item.active) {
switch (item.type) {
case 'url':
return (
<Link key={item.id} href={item.value || ''} className="text-sm font-normal">
{item.title}
</Link>
);
case 'dropdown':
return (
<DropdownMenu key={item.id}>
<DropdownMenuTrigger className="flex cursor-pointer items-center gap-1 text-sm">
{item.title}
<ChevronDown className="ml-1 h-4 w-4" />
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="min-w-20">
{item.items &&
Array.isArray(item.items) &&
item.items.map((subItem: any, idx: number) => (
<DropdownMenuItem key={idx} asChild className="cursor-pointer px-5">
<Link href={subItem.url || ''}>{subItem.title}</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
default:
return null;
}
}
};
const sortedItems = navbar.navbar_items.sort((a, b) => a.sort - b.sort);
const customizeLink = props.customize ? ziggy.location : '?customize=true';
return (
<>
<div className={cn('fixed top-0 z-30 w-full', isMenuOpen && 'bg-background')}>
<div
className={cn(
'container mt-0 flex h-[72px] items-center justify-between gap-1 !px-4 transition-all duration-200 md:gap-6',
isSticky && 'bg-background shadow-card mx-auto mt-4 h-16 w-full rounded-2xl md:!max-w-6xl',
screen < 768 && 'mt-0 h-[72px] rounded-none',
)}
>
<div className="flex items-center gap-2">
{/* Mobile menu button */}
<Button size="icon" variant="secondary" className="md:hidden" onClick={() => setIsMenuOpen(!isMenuOpen)}>
{isMenuOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
</Button>
{/* Logo */}
<Link href="/">
<AppLogo />
</Link>
</div>
{/* Desktop Navigation */}
<div className="hidden gap-4 md:flex md:items-center">
{sortedItems.map((item) => (
<Fragment key={item.id}>{renderNavItems(item)}</Fragment>
))}
</div>
<div className="flex items-center gap-2">
{customizable && isAdmin && (
<Button asChild variant="outline" className="hidden text-sm font-normal sm:block">
<Link href={customizeLink}>{props.customize ? 'Back' : 'Customize'}</Link>
</Button>
)}
<Actions language={language} />
</div>
</div>
{/* Mobile Menu */}
{isMenuOpen && (
<ScrollArea className="bg-background h-[calc(100vh-72px)] border-t md:hidden">
<div className="flex flex-col space-y-4 px-6 py-4">
{sortedItems.map((item) => (
<Fragment key={item.id}>{renderNavItems(item)}</Fragment>
))}
{customizable && isAdmin && (
<Button asChild variant="outline" className="text-sm font-normal">
<Link href={customizeLink}>{props.customize ? 'Back' : 'Customize'}</Link>
</Button>
)}
{!isLoggedIn && (
<div className="block space-y-2 sm:hidden">
<Button asChild variant="outline" className="w-full rounded-sm shadow-none sm:px-5 md:h-10">
<Link href={route('register')}>{translate.button.sign_up}</Link>
</Button>
<Button asChild className="w-full rounded-sm shadow-none sm:px-5 md:h-10">
<Link href={route('login')}>{translate.button.log_in}</Link>
</Button>
</div>
)}
</div>
</ScrollArea>
)}
</div>
{heightCover && <div className="relative z-20 h-[72px] bg-transparent" />}
</>
);
};
export default Navbar;