generated from fahricansecer/boilerplate-fe
230 lines
6.5 KiB
TypeScript
230 lines
6.5 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(255, 255, 255, 0.6)" : "white"}
|
|
_dark={{
|
|
bg: isSticky ? "rgba(1, 1, 1, 0.6)" : "black",
|
|
}}
|
|
shadow={isSticky ? "sm" : "none"}
|
|
backdropFilter="blur(12px) saturate(180%)"
|
|
border="1px solid"
|
|
borderColor={isSticky ? "whiteAlpha.300" : "transparent"}
|
|
borderBottomRadius={isSticky ? "xl" : "none"}
|
|
transition="all 0.4s ease-in-out"
|
|
px={{ base: 4, md: 8 }}
|
|
py="3"
|
|
position="sticky"
|
|
top={0}
|
|
zIndex={10}
|
|
w="full"
|
|
>
|
|
<Flex justify="space-between" align="center" maxW="8xl" mx="auto">
|
|
{/* Logo */}
|
|
<HStack>
|
|
<ChakraLink
|
|
as={Link}
|
|
href="/home"
|
|
fontSize="lg"
|
|
fontWeight="bold"
|
|
color={{ base: "primary.500", _dark: "primary.300" }}
|
|
focusRing="none"
|
|
textDecor="none"
|
|
transition="all 0.3s ease-in-out"
|
|
_hover={{
|
|
color: { base: "primary.900", _dark: "primary.50" },
|
|
}}
|
|
>
|
|
{"FCS "}
|
|
</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} />
|
|
</>
|
|
);
|
|
}
|