From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Tue, 25 Mar 2025 15:55:45 +0900
Subject: initial commit
---
components/layout/Footer.tsx | 16 +++
components/layout/Header.tsx | 225 ++++++++++++++++++++++++++++++++
components/layout/MobileMenu.tsx | 88 +++++++++++++
components/layout/command-menu.tsx | 139 ++++++++++++++++++++
components/layout/createEmotionCashe.ts | 5 +
components/layout/mode-switcher.tsx | 35 +++++
components/layout/providers.tsx | 38 ++++++
components/layout/sidebar-nav.tsx | 44 +++++++
8 files changed, 590 insertions(+)
create mode 100644 components/layout/Footer.tsx
create mode 100644 components/layout/Header.tsx
create mode 100644 components/layout/MobileMenu.tsx
create mode 100644 components/layout/command-menu.tsx
create mode 100644 components/layout/createEmotionCashe.ts
create mode 100644 components/layout/mode-switcher.tsx
create mode 100644 components/layout/providers.tsx
create mode 100644 components/layout/sidebar-nav.tsx
(limited to 'components/layout')
diff --git a/components/layout/Footer.tsx b/components/layout/Footer.tsx
new file mode 100644
index 00000000..f7d6906d
--- /dev/null
+++ b/components/layout/Footer.tsx
@@ -0,0 +1,16 @@
+import { siteConfig } from "@/config/site"
+
+export function SiteFooter() {
+ return (
+
+ )
+}
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 (
+ <>
+
+
+
+
+
+
+
+
+
+ {/* 로고 영역 */}
+
+
+
+ eVCP
+
+
+ {/* 데스크탑 네비게이션 메뉴 */}
+
+
+ {main.map((section: MenuSection) => (
+
+ {section.title}
+
+
+ {section.items.map((item) => (
+
+ {item.description}
+
+ ))}
+
+
+
+ ))}
+
+
+ {/* 추가 네비게이션 항목 */}
+ {additional.map((item) => (
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+
+
+
+
+ {/* 우측 영역 */}
+
+
+
+
+
+
+ {/* 알림 버튼 */}
+
+
+ {/* 사용자 메뉴 (DropdownMenu) */}
+
+
+
+
+
+ {initials || "?"}
+
+
+
+
+ My Account
+
+ {/*
+ Profile
+ */}
+
+ Settings
+
+
+ signOut({ callbackUrl: `/${lng}/login` })}>
+ Logout
+
+
+
+
+ {/* 모바일 햄버거 메뉴 버튼 */}
+
+
+
+
+
+
+ {/* 모바일 메뉴 */}
+ {isMobileMenuOpen && }
+
+ >
+ );
+}
+
+const ListItem = React.forwardRef<
+ React.ElementRef<"a">,
+ React.ComponentPropsWithoutRef<"a">
+>(({ className, title, children, ...props }, ref) => {
+ return (
+
+
+
+ {title}
+ {children && (
+
+ {children}
+
+ )}
+
+
+
+ );
+});
+ListItem.displayName = "ListItem";
\ No newline at end of file
diff --git a/components/layout/MobileMenu.tsx b/components/layout/MobileMenu.tsx
new file mode 100644
index 00000000..d2e6b927
--- /dev/null
+++ b/components/layout/MobileMenu.tsx
@@ -0,0 +1,88 @@
+// components/MobileMenu.tsx
+
+"use client";
+
+import * as React from "react";
+import Link from "next/link";
+import { useRouter,usePathname } from "next/navigation";
+import { MenuSection, mainNav, additionalNav, MenuItem, mainNavVendor, additionalNavVendor } from "@/config/menuConfig";
+import { cn } from "@/lib/utils";
+import { Drawer, DrawerContent,DrawerTitle,DrawerTrigger } from "@/components/ui/drawer";
+import { Button } from "@/components/ui/button";
+
+interface MobileMenuProps {
+ lng: string;
+ onClose: () => void;
+}
+
+export function MobileMenu({ lng, onClose }: MobileMenuProps) {
+ const router = useRouter();
+
+
+ const handleLinkClick = (href: string) => {
+ router.push(href);
+ onClose();
+ };
+ const pathname = usePathname();
+ const isPartnerRoute = pathname.includes("/partners");
+
+ const main = isPartnerRoute ? mainNavVendor : mainNav;
+ const additional = isPartnerRoute ? additionalNavVendor : additionalNav;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/layout/command-menu.tsx b/components/layout/command-menu.tsx
new file mode 100644
index 00000000..5537a042
--- /dev/null
+++ b/components/layout/command-menu.tsx
@@ -0,0 +1,139 @@
+"use client"
+
+import * as React from "react"
+import { useRouter,usePathname } from "next/navigation"
+import { type DialogProps } from "@radix-ui/react-dialog"
+import { Circle, File, Laptop, Moon, Sun } from "lucide-react"
+import { useTheme } from "next-themes"
+
+import { MenuSection, mainNav, additionalNav, MenuItem, mainNavVendor, additionalNavVendor } from "@/config/menuConfig";
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import {
+ CommandDialog,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+} from "@/components/ui/command"
+import { DialogTitle } from "@/components/ui/dialog"
+
+export function CommandMenu({ ...props }: DialogProps) {
+ const router = useRouter()
+ const [open, setOpen] = React.useState(false)
+ const { setTheme } = useTheme()
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if ((e.key === "k" && (e.metaKey || e.ctrlKey)) || e.key === "/") {
+ if (
+ (e.target instanceof HTMLElement && e.target.isContentEditable) ||
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ ) {
+ return
+ }
+
+ e.preventDefault()
+ setOpen((open) => !open)
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => document.removeEventListener("keydown", down)
+ }, [])
+
+ const runCommand = React.useCallback((command: () => unknown) => {
+ setOpen(false)
+ command()
+ }, [])
+
+
+const pathname = usePathname();
+const isPartnerRoute = pathname.includes("/partners");
+
+ const main = isPartnerRoute ? mainNavVendor : mainNav;
+ const additional = isPartnerRoute ? additionalNavVendor : additionalNav;
+
+
+ return (
+ <>
+
+
+ Search Menu
+
+
+ No results found.
+
+ {main.map((group) => (
+
+ {group.items.map((navItem) => (
+ {
+ runCommand(() => router.push(navItem.href as string))
+ }}
+ >
+
+
+
+ {navItem.title}
+
+ ))}
+
+ ))}
+
+ {additional
+ // .filter((navitem) => !navitem.external)
+ .map((navItem) => (
+ {
+ runCommand(() => router.push(navItem.href as string))
+ }}
+ >
+
+ {navItem.title}
+
+ ))}
+
+
+
+
+
+ runCommand(() => setTheme("light"))}>
+
+ Light
+
+ runCommand(() => setTheme("dark"))}>
+
+ Dark
+
+ runCommand(() => setTheme("system"))}>
+
+ System
+
+
+
+
+ >
+ )
+}
diff --git a/components/layout/createEmotionCashe.ts b/components/layout/createEmotionCashe.ts
new file mode 100644
index 00000000..ae8bc3b5
--- /dev/null
+++ b/components/layout/createEmotionCashe.ts
@@ -0,0 +1,5 @@
+import createCache from '@emotion/cache';
+
+export default function createEmotionCache() {
+ return createCache({ key: 'css' });
+}
\ No newline at end of file
diff --git a/components/layout/mode-switcher.tsx b/components/layout/mode-switcher.tsx
new file mode 100644
index 00000000..d27b6a73
--- /dev/null
+++ b/components/layout/mode-switcher.tsx
@@ -0,0 +1,35 @@
+"use client"
+
+import * as React from "react"
+import { MoonIcon, SunIcon } from "lucide-react"
+import { useTheme } from "next-themes"
+
+import { META_THEME_COLORS } from "@/config/site"
+import { useMetaColor } from "@/hooks/use-meta-color"
+import { Button } from "@/components/ui/button"
+
+export function ModeSwitcher() {
+ const { setTheme, resolvedTheme } = useTheme()
+ const { setMetaColor } = useMetaColor()
+
+ const toggleTheme = React.useCallback(() => {
+ setTheme(resolvedTheme === "dark" ? "light" : "dark")
+ setMetaColor(
+ resolvedTheme === "dark"
+ ? META_THEME_COLORS.light
+ : META_THEME_COLORS.dark
+ )
+ }, [resolvedTheme, setTheme, setMetaColor])
+
+ return (
+
+ )
+}
diff --git a/components/layout/providers.tsx b/components/layout/providers.tsx
new file mode 100644
index 00000000..1c645531
--- /dev/null
+++ b/components/layout/providers.tsx
@@ -0,0 +1,38 @@
+"use client"
+
+import * as React from "react"
+import { Provider as JotaiProvider } from "jotai"
+import { ThemeProvider as NextThemesProvider } from "next-themes"
+import { NuqsAdapter } from "nuqs/adapters/next/app"
+import { SessionProvider } from "next-auth/react";
+import { CacheProvider } from '@emotion/react';
+
+import { TooltipProvider } from "@/components/ui/tooltip"
+import createEmotionCache from './createEmotionCashe';
+
+
+const cache = createEmotionCache();
+
+
+export function ThemeProvider({
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/layout/sidebar-nav.tsx b/components/layout/sidebar-nav.tsx
new file mode 100644
index 00000000..addcfefd
--- /dev/null
+++ b/components/layout/sidebar-nav.tsx
@@ -0,0 +1,44 @@
+"use client"
+
+import Link from "next/link"
+import { usePathname } from "next/navigation"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+interface SidebarNavProps extends React.HTMLAttributes {
+ items: {
+ href: string
+ title: string
+ }[]
+}
+
+export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
+ const pathname = usePathname()
+
+ return (
+
+ )
+}
--
cgit v1.2.3