Files
Game_Calendar_FE/src/components/layout/header/header.tsx
2026-01-30 15:23:59 +03:00

238 lines
6.8 KiB
TypeScript

"use client";
import {
Box,
Flex,
HStack,
IconButton,
Link as ChakraLink,
Stack,
VStack,
Button,
MenuItem,
ClientOnly,
} from "@chakra-ui/react";
import { Link, useRouter } from "@/i18n/navigation";
import { ColorModeButton } from "@/components/ui/color-mode";
import {
PopoverBody,
PopoverContent,
PopoverRoot,
PopoverTrigger,
} from "@/components/ui/overlays/popover";
import { RxHamburgerMenu } from "react-icons/rx";
import { NAV_ITEMS } from "@/config/navigation";
import HeaderLink from "./header-link";
import MobileHeaderLink from "./mobile-header-link";
import LocaleSwitcher from "@/components/ui/locale-switcher";
import { useEffect, useState } from "react";
import { useTranslations } from "next-intl";
import {
MenuContent,
MenuRoot,
MenuTrigger,
} from "@/components/ui/overlays/menu";
import { Avatar } from "@/components/ui/data-display/avatar";
import { Skeleton } from "@/components/ui/feedback/skeleton";
import { signOut, useSession } from "next-auth/react";
import { authConfig } from "@/config/auth";
import { LoginModal } from "@/components/auth/login-modal";
import { LuLogIn } from "react-icons/lu";
export default function Header() {
const t = useTranslations();
const [isSticky, setIsSticky] = useState(false);
const [loginModalOpen, setLoginModalOpen] = useState(false);
const router = useRouter();
const { data: session, status } = useSession();
const isAuthenticated = !!session;
const isLoading = status === "loading";
useEffect(() => {
const handleScroll = () => {
setIsSticky(window.scrollY >= 10);
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
const handleLogout = async () => {
await signOut({ redirect: false });
if (authConfig.isAuthRequired) {
router.replace("/signin");
}
};
// Render user menu or login button based on auth state
const renderAuthSection = () => {
if (isLoading) {
return <Skeleton boxSize="10" rounded="full" />;
}
if (isAuthenticated) {
return (
<MenuRoot positioning={{ placement: "bottom-start" }}>
<MenuTrigger rounded="full" focusRing="none">
<Avatar name={session?.user?.name || "User"} variant="solid" />
</MenuTrigger>
<MenuContent>
<MenuItem onClick={handleLogout} value="sign-out">
{t("auth.sign-out")}
</MenuItem>
</MenuContent>
</MenuRoot>
);
}
// Not authenticated - show login button
return (
<Button
variant="solid"
colorPalette="primary"
size="sm"
onClick={() => setLoginModalOpen(true)}
>
<LuLogIn />
{t("auth.sign-in")}
</Button>
);
};
// Render mobile auth section
const renderMobileAuthSection = () => {
if (isLoading) {
return <Skeleton height="10" width="full" />;
}
if (isAuthenticated) {
return (
<>
<Avatar name={session?.user?.name || "User"} variant="solid" />
<Button
variant="surface"
size="sm"
width="full"
onClick={handleLogout}
>
{t("auth.sign-out")}
</Button>
</>
);
}
return (
<Button
variant="solid"
colorPalette="primary"
size="sm"
width="full"
onClick={() => setLoginModalOpen(true)}
>
<LuLogIn />
{t("auth.sign-in")}
</Button>
);
};
return (
<>
<Box
as="nav"
bg={isSticky ? "rgba(5, 5, 10, 0.6)" : "transparent"}
_dark={{
bg: isSticky ? "rgba(5, 5, 10, 0.6)" : "transparent",
}}
shadow={isSticky ? "lg" : "none"}
backdropFilter={isSticky ? "blur(16px) saturate(180%)" : "none"}
borderBottom="1px solid"
borderColor={isSticky ? "whiteAlpha.100" : "transparent"}
borderBottomRadius={isSticky ? "2xl" : "none"}
transition="all 0.4s cubic-bezier(0.4, 0, 0.2, 1)"
mx={isSticky ? { base: 4, md: 8 } : 0}
mt={isSticky ? 4 : 0}
px={{ base: 4, md: 8 }}
py="4"
position="sticky"
top={0}
zIndex={100}
w={isSticky ? "auto" : "full"}
>
<Flex justify="space-between" align="center" maxW="8xl" mx="auto">
{/* Logo */}
<HStack>
<ChakraLink
as={Link}
href="/home"
fontSize="3xl"
fontWeight="900"
letterSpacing="tight"
bgGradient="to-r"
gradientFrom="brand.300"
gradientTo="brand.500"
bgClip="text"
bgSize="200% auto"
animation="text-gradient 12s linear infinite"
focusRing="none"
textDecor="none"
transition="all 0.3s ease-in-out"
_hover={{
opacity: 0.8,
}}
>
{"Game Calendar"}
</ChakraLink>
</HStack>
{/* DESKTOP NAVIGATION */}
<HStack spaceX={4} display={{ base: "none", lg: "flex" }}>
{NAV_ITEMS.map((item, index) => (
<HeaderLink key={index} item={item} />
))}
</HStack>
<HStack>
<ColorModeButton colorPalette="gray" />
<Box display={{ base: "none", lg: "inline-flex" }} gap={2}>
<LocaleSwitcher />
<ClientOnly fallback={<Skeleton boxSize="10" rounded="full" />}>
{renderAuthSection()}
</ClientOnly>
</Box>
{/* MOBILE NAVIGATION */}
<Stack display={{ base: "inline-flex", lg: "none" }}>
<ClientOnly fallback={<Skeleton boxSize="9" />}>
<PopoverRoot>
<PopoverTrigger as="span">
<IconButton aria-label="Open menu" variant="ghost">
<RxHamburgerMenu />
</IconButton>
</PopoverTrigger>
<PopoverContent width={{ base: "xs", sm: "sm", md: "md" }}>
<PopoverBody>
<VStack mt="2" align="start" spaceY="2" w="full">
{NAV_ITEMS.map((item) => (
<MobileHeaderLink key={item.label} item={item} />
))}
<LocaleSwitcher />
{renderMobileAuthSection()}
</VStack>
</PopoverBody>
</PopoverContent>
</PopoverRoot>
</ClientOnly>
</Stack>
</HStack>
</Flex>
</Box>
{/* Login Modal */}
<LoginModal open={loginModalOpen} onOpenChange={setLoginModalOpen} />
</>
);
}