Spaces:
Sleeping
Sleeping
| "use client" | |
| import * as React from "react" | |
| import Link from "next/link" | |
| import { usePathname } from "next/navigation" | |
| import { cn } from "@/lib/utils" | |
| import { Button } from "@/components/ui/button" | |
| import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" | |
| import { Menu, Sparkles } from "lucide-react" | |
| import { useSession, signOut } from "next-auth/react" | |
| import { ModeToggle } from "@/components/mode-toggle" | |
| const navItems = [ | |
| { name: "Services", href: "/services" }, | |
| { name: "Portfolio", href: "/portfolio" }, | |
| { name: "Blog", href: "/blog" }, | |
| { name: "About", href: "/about" }, | |
| { name: "Contact", href: "/contact" }, | |
| ] | |
| function AuthButtons({ mobile, onAction }: { mobile?: boolean; onAction?: () => void }) { | |
| const { data: session } = useSession() | |
| if (session) { | |
| return ( | |
| <div className={cn("flex gap-2", mobile ? "flex-col" : "items-center")}> | |
| <span className="text-sm text-muted-foreground truncate max-w-[140px]"> | |
| {session.user?.email} | |
| </span> | |
| <Button | |
| variant="outline" | |
| size="sm" | |
| className="glass-card hover:bg-primary/10" | |
| onClick={() => { | |
| signOut({ callbackUrl: "/admin/login" }) | |
| onAction?.() | |
| }} | |
| > | |
| Logout | |
| </Button> | |
| <Button asChild variant="default" size="sm" onClick={onAction} className="shine-effect"> | |
| <Link href="/admin/dashboard">Dashboard</Link> | |
| </Button> | |
| </div> | |
| ) | |
| } | |
| return ( | |
| <div className={cn("flex gap-2", mobile ? "flex-col" : "items-center")}> | |
| <Button asChild size="sm" onClick={onAction} className="shine-effect"> | |
| <Link href="/quote">Get a Quote</Link> | |
| </Button> | |
| <Button asChild variant="ghost" size="sm" onClick={onAction} className="hover:bg-primary/10"> | |
| <Link href="/admin/login">Login</Link> | |
| </Button> | |
| </div> | |
| ) | |
| } | |
| export function Header() { | |
| const pathname = usePathname() | |
| const [isOpen, setIsOpen] = React.useState(false) | |
| const [scrolled, setScrolled] = React.useState(false) | |
| React.useEffect(() => { | |
| const handleScroll = () => { | |
| setScrolled(window.scrollY > 20) | |
| } | |
| window.addEventListener('scroll', handleScroll) | |
| return () => window.removeEventListener('scroll', handleScroll) | |
| }, []) | |
| return ( | |
| <header | |
| className={cn( | |
| "sticky top-0 z-50 w-full transition-all duration-500 animate-fade-in-down", | |
| scrolled | |
| ? "glass-card border-b border-border/20 shadow-lg" | |
| : "bg-transparent" | |
| )} | |
| > | |
| <div className="container flex h-16 items-center justify-between"> | |
| {/* Logo */} | |
| <Link href="/" className="flex items-center gap-2 group"> | |
| <div className="relative"> | |
| <span className="text-xl font-bold tracking-tight transition-all duration-300 group-hover:scale-105 inline-block"> | |
| Nexa<span className="gradient-text">Flow</span> | |
| </span> | |
| {/* Animated underline on hover */} | |
| <div className="absolute -bottom-1 left-0 w-0 h-0.5 bg-gradient-to-r from-primary to-accent group-hover:w-full transition-all duration-300 rounded-full" /> | |
| </div> | |
| <Sparkles className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 group-hover:rotate-12 transition-all duration-300" suppressHydrationWarning /> | |
| </Link> | |
| {/* Desktop Navigation */} | |
| <nav className="hidden md:flex items-center gap-1"> | |
| {navItems.map((item) => { | |
| const isActive = pathname === item.href | |
| return ( | |
| <Link | |
| key={item.href} | |
| href={item.href} | |
| className={cn( | |
| "relative px-5 py-2.5 text-sm font-semibold rounded-2xl transition-all duration-300 group", | |
| isActive | |
| ? "text-primary glass-card" | |
| : "text-muted-foreground hover:text-primary hover:bg-primary/5" | |
| )} | |
| > | |
| <span className="relative z-10">{item.name}</span> | |
| {/* Active indicator */} | |
| {isActive && ( | |
| <div className="absolute bottom-1 left-1/2 -translate-x-1/2 w-1 h-1 rounded-full bg-primary animate-pulse" /> | |
| )} | |
| {/* Hover effect */} | |
| <div className={cn( | |
| "absolute inset-0 rounded-2xl bg-primary/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300", | |
| isActive && "opacity-100" | |
| )} /> | |
| </Link> | |
| ) | |
| })} | |
| </nav> | |
| {/* Desktop Actions */} | |
| <div className="flex items-center gap-3"> | |
| <div className="hidden md:flex items-center gap-2"> | |
| <ModeToggle /> | |
| <AuthButtons /> | |
| </div> | |
| {/* Mobile Menu */} | |
| <div className="md:hidden flex items-center gap-2"> | |
| <ModeToggle /> | |
| <Sheet open={isOpen} onOpenChange={setIsOpen}> | |
| <SheetTrigger asChild> | |
| <Button variant="ghost" size="icon" className="glass-card hover:bg-primary/10"> | |
| <Menu className="h-5 w-5" suppressHydrationWarning /> | |
| <span className="sr-only">Toggle menu</span> | |
| </Button> | |
| </SheetTrigger> | |
| <SheetContent side="right" className="w-80 max-w-[80vw] glass-card border-l border-border/20 p-0"> | |
| <div className="flex flex-col gap-6 p-6"> | |
| <Link | |
| href="/" | |
| onClick={() => setIsOpen(false)} | |
| className="text-xl font-bold hover:text-primary transition-colors flex items-center gap-2 group" | |
| > | |
| Nexa<span className="gradient-text">Flow</span> | |
| <Sparkles className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-opacity" suppressHydrationWarning /> | |
| </Link> | |
| <nav className="flex flex-col gap-2"> | |
| {navItems.map((item, i) => { | |
| const isActive = pathname === item.href | |
| return ( | |
| <Link | |
| key={item.href} | |
| href={item.href} | |
| onClick={() => setIsOpen(false)} | |
| className={cn( | |
| "px-4 py-3 text-base font-medium rounded-xl transition-all duration-300 animate-fade-in-up", | |
| isActive | |
| ? "text-primary glass-card" | |
| : "text-muted-foreground hover:text-primary hover:bg-primary/5" | |
| )} | |
| style={{ animationDelay: `${i * 0.05}s` }} | |
| > | |
| {item.name} | |
| </Link> | |
| ) | |
| })} | |
| </nav> | |
| <div className="pt-4 border-t border-border/50 animate-fade-in-up animate-delay-3"> | |
| <AuthButtons mobile onAction={() => setIsOpen(false)} /> | |
| </div> | |
| </div> | |
| </SheetContent> | |
| </Sheet> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| ) | |
| } | |