123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
- import { Link, usePage } from '@inertiajs/react';
- import { BookOpen, Folder, LayoutGrid, Menu, Search } from 'lucide-react';
- import { AppLogo } from '@/common/AppLogo';
- import { AppLogoIcon } from '@/common/AppLogoIcon';
- import { Breadcrumbs } from '@/common/Breadcrumbs';
- import { Icon } from '@/common/Icon';
- import { UserMenuContent } from '@/common/UserMenuContent';
- import { cn } from '@/common/helpers/cn';
- import { useInitials } from '@/common/hooks/useInitials';
- import { type BreadcrumbItem, type NavItem, type SharedData } from '@/common/types';
- import { dashboard } from '@/routes';
- import { Avatar, AvatarFallback, AvatarImage } from '@/shadcn/avatar';
- import { Button } from '@/shadcn/button';
- import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/shadcn/dropdown-menu';
- import { NavigationMenu, NavigationMenuItem, NavigationMenuList, navigationMenuTriggerStyle } from '@/shadcn/navigation-menu';
- import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/shadcn/sheet';
- import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/shadcn/tooltip';
- const mainNavItems: NavItem[] = [
- {
- title: 'Dashboard',
- href: dashboard(),
- icon: LayoutGrid,
- },
- ];
- const rightNavItems: NavItem[] = [
- {
- title: 'Repository',
- href: 'https://github.com/laravel/react-starter-kit',
- icon: Folder,
- },
- {
- title: 'Documentation',
- href: 'https://laravel.com/docs/starter-kits#react',
- icon: BookOpen,
- },
- ];
- const activeItemStyles = 'text-neutral-900 dark:bg-neutral-800 dark:text-neutral-100';
- interface AppHeaderProps {
- breadcrumbs?: BreadcrumbItem[];
- }
- export function AppHeader({ breadcrumbs = [] }: AppHeaderProps) {
- const page = usePage<SharedData>();
- const { auth } = page.props;
- const getInitials = useInitials();
- return (
- <>
- <div className="border-b border-sidebar-border/80">
- <div className="mx-auto flex h-16 items-center px-4 md:max-w-7xl">
- {/* Mobile Menu */}
- <div className="lg:hidden">
- <Sheet>
- <SheetTrigger asChild>
- <Button variant="ghost" size="icon" className="mr-2 h-[34px] w-[34px]">
- <Menu className="h-5 w-5" />
- </Button>
- </SheetTrigger>
- <SheetContent side="left" className="flex h-full w-64 flex-col items-stretch justify-between bg-sidebar">
- <SheetTitle className="sr-only">Navigation Menu</SheetTitle>
- <SheetHeader className="flex justify-start text-left">
- <AppLogoIcon className="h-6 w-6 fill-current text-black dark:text-white" />
- </SheetHeader>
- <div className="flex h-full flex-1 flex-col space-y-4 p-4">
- <div className="flex h-full flex-col justify-between text-sm">
- <div className="flex flex-col space-y-4">
- {mainNavItems.map((item) => (
- <Link key={item.title} href={item.href} className="flex items-center space-x-2 font-medium">
- {item.icon && <Icon iconNode={item.icon} className="h-5 w-5" />}
- <span>{item.title}</span>
- </Link>
- ))}
- </div>
- <div className="flex flex-col space-y-4">
- {rightNavItems.map((item) => (
- <a
- key={item.title}
- href={typeof item.href === 'string' ? item.href : item.href.url}
- target="_blank"
- rel="noopener noreferrer"
- className="flex items-center space-x-2 font-medium"
- >
- {item.icon && <Icon iconNode={item.icon} className="h-5 w-5" />}
- <span>{item.title}</span>
- </a>
- ))}
- </div>
- </div>
- </div>
- </SheetContent>
- </Sheet>
- </div>
- <Link href={dashboard()} prefetch className="flex items-center space-x-2">
- <AppLogo />
- </Link>
- {/* Desktop Navigation */}
- <div className="ml-6 hidden h-full items-center space-x-6 lg:flex">
- <NavigationMenu className="flex h-full items-stretch">
- <NavigationMenuList className="flex h-full items-stretch space-x-2">
- {mainNavItems.map((item, index) => (
- <NavigationMenuItem key={index} className="relative flex h-full items-center">
- <Link
- href={item.href}
- className={cn(
- navigationMenuTriggerStyle(),
- page.url === (typeof item.href === 'string' ? item.href : item.href.url) && activeItemStyles,
- 'h-9 cursor-pointer px-3',
- )}
- >
- {item.icon && <Icon iconNode={item.icon} className="mr-2 h-4 w-4" />}
- {item.title}
- </Link>
- {page.url === item.href && (
- <div className="absolute bottom-0 left-0 h-0.5 w-full translate-y-px bg-black dark:bg-white"></div>
- )}
- </NavigationMenuItem>
- ))}
- </NavigationMenuList>
- </NavigationMenu>
- </div>
- <div className="ml-auto flex items-center space-x-2">
- <div className="relative flex items-center space-x-1">
- <Button variant="ghost" size="icon" className="group h-9 w-9 cursor-pointer">
- <Search className="!size-5 opacity-80 group-hover:opacity-100" />
- </Button>
- <div className="hidden lg:flex">
- {rightNavItems.map((item) => (
- <TooltipProvider key={item.title} delayDuration={0}>
- <Tooltip>
- <TooltipTrigger>
- <a
- href={typeof item.href === 'string' ? item.href : item.href.url}
- target="_blank"
- rel="noopener noreferrer"
- className="group ml-1 inline-flex h-9 w-9 items-center justify-center rounded-md bg-transparent p-0 text-sm font-medium text-accent-foreground ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
- >
- <span className="sr-only">{item.title}</span>
- {item.icon && <Icon iconNode={item.icon} className="size-5 opacity-80 group-hover:opacity-100" />}
- </a>
- </TooltipTrigger>
- <TooltipContent>
- <p>{item.title}</p>
- </TooltipContent>
- </Tooltip>
- </TooltipProvider>
- ))}
- </div>
- </div>
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button variant="ghost" className="size-10 rounded-full p-1">
- <Avatar className="size-8 overflow-hidden rounded-full">
- <AvatarImage src={auth.user.avatar} alt={auth.user.name} />
- <AvatarFallback className="rounded-lg bg-neutral-200 text-black dark:bg-neutral-700 dark:text-white">
- {getInitials(auth.user.name)}
- </AvatarFallback>
- </Avatar>
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent className="w-56" align="end">
- <UserMenuContent user={auth.user} />
- </DropdownMenuContent>
- </DropdownMenu>
- </div>
- </div>
- </div>
- {breadcrumbs.length > 1 && (
- <div className="flex w-full border-b border-sidebar-border/70">
- <div className="mx-auto flex h-12 w-full items-center justify-start px-4 text-neutral-500 md:max-w-7xl">
- <Breadcrumbs breadcrumbs={breadcrumbs} />
- </div>
- </div>
- )}
- </>
- );
- }
|