Files
pagodera/components/Header.tsx
T
Evandro Fonseca Junior 62ab5b2608 feat(admin): centralize subscriptions under /admin?tab=subscriptions
Replaced the dedicated /admin/subscriptions page with a redirect to the central admin panel’s subscriptions tab. Removed session checks, imports, and the AdminSubscriptionDashboard since routing and access are now handled centrally. This streamlines admin UX and keeps access control unified.
2025-11-09 01:50:50 -03:00

200 lines
7.0 KiB
TypeScript

"use client";
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import { WordRotate } from "@/components/ui/word-rotate";
import { ThemeToggle } from "./theme-toggle";
import { LanguageSwitcher } from "./language-switcher";
import { MobileMenu } from "./mobile-menu";
import { NotificationBell } from "./notifications/NotificationBell";
import { MessagesBell } from "./messages/MessagesBell";
import { LoginDialog } from "./auth/login-dialog";
import { RegisterDialog } from "./auth/register-dialog";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { cn } from "@/lib/utils";
import { useTranslations, useLocale } from "next-intl";
import { useSession } from "@/lib/hooks/use-session";
import Link from "next/link";
import { useState, useEffect } from "react";
import { LogOut, User } from "lucide-react";
import { Badge } from "@/components/ui/badge";
export default function Header() {
const t = useTranslations("header.nav");
const locale = useLocale();
const { user, isAuthenticated, isLoading, logout } = useSession();
const [loginDialogOpen, setLoginDialogOpen] = useState(false);
const [registerDialogOpen, setRegisterDialogOpen] = useState(false);
const [mounted, setMounted] = useState(false);
// Evitar hidratação mismatch
useEffect(() => {
setMounted(true);
}, []);
const links = [
{ href: `/${locale}/library`, label: "Library" },
{ href: `/${locale}/market`, label: t("market") },
{ href: `/${locale}/sobre`, label: t("about") },
{ href: `/${locale}/precos`, label: t("prices") },
{ href: `/${locale}/apoiar`, label: t("support"), highlight: true },
];
const navLinks = [
...links,
...(isAuthenticated && user?.userId === 1
? [{ href: `/${locale}/admin`, label: "Admin" }]
: []),
];
const handleLogout = async () => {
await logout();
};
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/60 backdrop-blur supports-backdrop-filter:bg-background/60">
<div className="mx-auto flex h-16 w-full max-w-6xl items-center justify-between px-4">
{/* Logo */}
<div className="flex items-center gap-2">
<Link href="/">
<WordRotate
words={["Tibia Trade"]}
className="text-lg font-semibold tracking-tight hover:text-primary transition-colors"
/>
</Link>
</div>
{/* Desktop Navigation */}
<NavigationMenu className="hidden md:flex">
<NavigationMenuList>
{navLinks.map(({ href, label, highlight }) => (
<NavigationMenuItem key={href}>
<NavigationMenuLink asChild>
<Link
href={href}
className={cn(
navigationMenuTriggerStyle(),
"bg-transparent",
highlight &&
"bg-primary text-primary-foreground hover:bg-primary/90"
)}
>
{label}
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
{/* Desktop Controls */}
<div className="hidden md:flex items-center gap-2">
<ThemeToggle />
<LanguageSwitcher />
{/* Messages */}
{mounted && isAuthenticated && <MessagesBell />}
{/* Notifications */}
{mounted && isAuthenticated && <NotificationBell />}
{mounted && isAuthenticated && user?.subscriptionTier && (
<Badge variant="outline" className="uppercase">
{user.subscriptionTier.slug}
</Badge>
)}
{/* Auth Section */}
{!mounted ? (
// Placeholder durante SSR para evitar mismatch
<div className="w-20 h-8 animate-pulse bg-muted rounded" />
) : isLoading ? (
<div className="w-20 h-8 animate-pulse bg-muted rounded" />
) : isAuthenticated && user ? (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarFallback>
{user.username.charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel>
<div className="flex flex-col space-y-1">
<p className="text-sm font-medium leading-none">
{user.username}
</p>
{user.characterName && (
<p className="text-xs leading-none text-primary font-medium">
{user.characterName}
</p>
)}
<p className="text-xs leading-none text-muted-foreground">
{user.email}
</p>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`/${locale}/perfil`} className="cursor-pointer">
<User className="mr-2 h-4 w-4" />
Perfil
</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer text-destructive"
onClick={handleLogout}
>
<LogOut className="mr-2 h-4 w-4" />
Sair
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<div className="flex items-center gap-2">
<Button
variant="outline"
onClick={() => setRegisterDialogOpen(true)}
size="sm"
>
Registrar
</Button>
<Button onClick={() => setLoginDialogOpen(true)} size="sm">
Entrar
</Button>
</div>
)}
</div>
{/* Mobile Menu */}
<div className="md:hidden">
<MobileMenu links={navLinks} />
</div>
</div>
{/* Login Dialog */}
<LoginDialog open={loginDialogOpen} onOpenChange={setLoginDialogOpen} />
{/* Register Dialog */}
<RegisterDialog open={registerDialogOpen} onOpenChange={setRegisterDialogOpen} />
</header>
);
}