238 lines
6.8 KiB
TypeScript
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} />
|
|
</>
|
|
);
|
|
}
|