underrate's picture
Initial commit
34ed5b1 verified
Raw
History Blame Contribute Delete
9.43 kB
"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>
)
}