summaryrefslogtreecommitdiff
path: root/components/layout/Header.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-03-26 00:37:41 +0000
commite0dfb55c5457aec489fc084c4567e791b4c65eb1 (patch)
tree68543a65d88f5afb3a0202925804103daa91bc6f /components/layout/Header.tsx
3/25 까지의 대표님 작업사항
Diffstat (limited to 'components/layout/Header.tsx')
-rw-r--r--components/layout/Header.tsx225
1 files changed, 225 insertions, 0 deletions
diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx
new file mode 100644
index 00000000..1b6c45bb
--- /dev/null
+++ b/components/layout/Header.tsx
@@ -0,0 +1,225 @@
+"use client";
+
+import * as React from "react";
+import Link from "next/link";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import {
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ navigationMenuTriggerStyle,
+} from "@/components/ui/navigation-menu";
+import { SearchIcon, BellIcon, Menu } from "lucide-react";
+import { useParams, usePathname } from "next/navigation";
+import { cn } from "@/lib/utils";
+import Image from "next/image";
+import { mainNav, additionalNav, MenuSection, mainNavVendor, additionalNavVendor } from "@/config/menuConfig"; // 메뉴 구성 임포트
+import { MobileMenu } from "./MobileMenu";
+import { CommandMenu } from "./command-menu";
+import { useSession, signOut } from "next-auth/react";
+
+
+export function Header() {
+ const params = useParams();
+ const lng = params.lng as string;
+ const pathname = usePathname();
+ const { data: session } = useSession();
+
+ const userName = session?.user?.name || ""; // 없을 수도 있으니 안전하게 처리
+ const initials = userName
+ .split(" ")
+ .map((word) => word[0]?.toUpperCase())
+ .join("");
+
+
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false); // 모바일 메뉴 상태
+
+ const toggleMobileMenu = () => {
+ setIsMobileMenuOpen(!isMobileMenuOpen);
+ };
+
+ const isPartnerRoute = pathname.includes("/partners");
+
+ const main = isPartnerRoute ? mainNavVendor : mainNav;
+ const additional = isPartnerRoute ? additionalNavVendor : additionalNav;
+
+ const basePath = `/${lng}${isPartnerRoute ? "/partners" : "/evcp"}`;
+
+ return (
+ <>
+ <header className="border-grid sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
+ <div className="container-wrapper">
+ <div className="container flex h-14 items-center">
+
+ <Button
+ onClick={toggleMobileMenu}
+ variant="ghost"
+ className="-ml-2 mr-2 h-8 w-8 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
+ >
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth="1.5"
+ stroke="currentColor"
+ className="!size-6"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M3.75 9h16.5m-16.5 6.75h16.5"
+ />
+ </svg>
+ <span className="sr-only">Toggle Menu</span>
+ </Button>
+
+
+ <div className="mr-4 hidden md:flex">
+
+ {/* 로고 영역 */}
+ <div className="mr-4 flex items-center gap-2 lg:mr-6">
+ <Link href={`/${lng}/evcp`} className="flex items-center gap-2">
+ <Image
+ className="dark:invert"
+ src="/images/vercel.svg"
+ alt="EVCP Logo"
+ width={20}
+ height={20}
+ />
+ <span className="hidden font-bold lg:inline-block">eVCP</span>
+ </Link>
+ </div>
+ {/* 데스크탑 네비게이션 메뉴 */}
+ <NavigationMenu className="flex items-center gap-4 text-sm xl:gap-6">
+ <NavigationMenuList>
+ {main.map((section: MenuSection) => (
+ <NavigationMenuItem key={section.title}>
+ <NavigationMenuTrigger>{section.title}</NavigationMenuTrigger>
+ <NavigationMenuContent>
+ <ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
+ {section.items.map((item) => (
+ <ListItem
+ key={item.title}
+ title={item.title}
+ href={`/${lng}${item.href}`}
+ >
+ {item.description}
+ </ListItem>
+ ))}
+ </ul>
+ </NavigationMenuContent>
+ </NavigationMenuItem>
+ ))}
+
+
+ {/* 추가 네비게이션 항목 */}
+ {additional.map((item) => (
+ <NavigationMenuItem key={item.title}>
+ <Link href={`/${lng}${item.href}`} legacyBehavior passHref>
+ <NavigationMenuLink className={navigationMenuTriggerStyle()}>
+ {item.title}
+ </NavigationMenuLink>
+ </Link>
+ </NavigationMenuItem>
+ ))}
+ </NavigationMenuList>
+ </NavigationMenu>
+
+
+ </div>
+
+
+ {/* 우측 영역 */}
+ <div className="flex flex-1 items-center justify-between gap-2 md:justify-end">
+
+ <CommandMenu />
+
+
+ <div className="flex items-center space-x-4">
+ {/* 알림 버튼 */}
+ <Button variant="ghost" className="relative p-2" aria-label="Notifications">
+ <BellIcon className="h-5 w-5" />
+ {/* 알림 뱃지 예시 */}
+ <span className="absolute -top-1 -right-1 inline-flex h-2 w-2 rounded-full bg-red-500"></span>
+ </Button>
+
+ {/* 사용자 메뉴 (DropdownMenu) */}
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Avatar className="cursor-pointer">
+ <AvatarImage src={`/profiles/${session?.user?.image}`||"/user-avatar.jpg"} alt="User Avatar" />
+ <AvatarFallback>
+ {initials || "?"}
+ </AvatarFallback>
+ </Avatar>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-48" align="end">
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
+ <DropdownMenuSeparator />
+ {/* <DropdownMenuItem asChild>
+ <Link href={`${basePath}/profile`}>Profile</Link>
+ </DropdownMenuItem> */}
+ <DropdownMenuItem asChild>
+ <Link href={`${basePath}/settings`}>Settings</Link>
+ </DropdownMenuItem>
+ <DropdownMenuSeparator />
+ <DropdownMenuItem onSelect={() => signOut({ callbackUrl: `/${lng}/login` })}>
+ Logout
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+
+ {/* 모바일 햄버거 메뉴 버튼 */}
+
+ </div>
+ </div>
+ </div>
+ </div>
+
+ {/* 모바일 메뉴 */}
+ {isMobileMenuOpen && <MobileMenu lng={lng} onClose={toggleMobileMenu} />}
+ </header>
+ </>
+ );
+}
+
+const ListItem = React.forwardRef<
+ React.ElementRef<"a">,
+ React.ComponentPropsWithoutRef<"a">
+>(({ className, title, children, ...props }, ref) => {
+ return (
+ <li>
+ <NavigationMenuLink asChild>
+ <a
+ ref={ref}
+ className={cn(
+ "block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
+ className
+ )}
+ {...props}
+ >
+ <div className="text-sm font-medium leading-none">{title}</div>
+ {children && (
+ <p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
+ {children}
+ </p>
+ )}
+ </a>
+ </NavigationMenuLink>
+ </li>
+ );
+});
+ListItem.displayName = "ListItem"; \ No newline at end of file