summaryrefslogtreecommitdiff
path: root/components/layout
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout')
-rw-r--r--components/layout/GroupedMenuRender.tsx30
-rw-r--r--components/layout/Header.tsx54
-rw-r--r--components/layout/MobileMenu.tsx30
3 files changed, 62 insertions, 52 deletions
diff --git a/components/layout/GroupedMenuRender.tsx b/components/layout/GroupedMenuRender.tsx
index 9006c85d..b56135eb 100644
--- a/components/layout/GroupedMenuRender.tsx
+++ b/components/layout/GroupedMenuRender.tsx
@@ -13,10 +13,11 @@ type GroupedMenuItems = {
interface GroupedMenuRendererProps {
items: MenuItem[];
lng: string;
- activeMenus?: Record<string, boolean>; // 활성 메뉴 상태 추가
+ activeMenus?: Record<string, boolean>;
+ t: (key: string) => string; // 번역 함수 추가
}
-const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRendererProps) => {
+const GroupedMenuRenderer = ({ items, lng, activeMenus = {}, t }: GroupedMenuRendererProps) => {
// 활성 메뉴만 필터링 (activeMenus가 빈 객체면 모든 메뉴 표시)
const filteredItems = Object.keys(activeMenus).length > 0
? filterActiveAdditionalMenus(items, activeMenus)
@@ -25,11 +26,14 @@ const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRender
// 그룹별로 아이템 분류
const groupItems = (items: MenuItem[]): GroupedMenuItems => {
return items.reduce((groups, item) => {
- const group = item.group || 'default';
- if (!groups[group]) {
- groups[group] = [];
+ // groupKey가 있으면 번역된 그룹명 사용, 없으면 'default'
+ const groupKey = item.groupKey || 'default';
+ const groupName = item.groupKey ? t(item.groupKey) : 'default';
+
+ if (!groups[groupName]) {
+ groups[groupName] = [];
}
- groups[group].push(item);
+ groups[groupName].push(item);
return groups;
}, {} as GroupedMenuItems);
};
@@ -42,7 +46,7 @@ const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRender
return (
<div className="p-4 w-[600px]">
<p className="text-sm text-muted-foreground text-center py-8">
- 사용 가능한 메뉴가 없습니다.
+ {t('common.no_available_menus')}
</p>
</div>
);
@@ -53,7 +57,7 @@ const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRender
{groups.map((groupName, index) => {
// 빈 그룹은 건너뛰기
if (groupedItems[groupName].length === 0) return null;
-
+
return (
<div key={groupName} className={cn("mb-4", index < groups.length - 1 && "pb-2 border-b border-border/30")}>
{groupName !== 'default' && (
@@ -61,7 +65,7 @@ const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRender
)}
<div className="grid grid-cols-2 gap-3">
{groupedItems[groupName].map((item) => (
- <MenuListItem key={item.title} item={item} lng={lng} />
+ <MenuListItem key={item.titleKey} item={item} lng={lng} t={t} />
))}
</div>
</div>
@@ -71,7 +75,7 @@ const GroupedMenuRenderer = ({ items, lng, activeMenus = {} }: GroupedMenuRender
);
};
-const MenuListItem = ({ item, lng }: { item: MenuItem; lng: string }) => {
+const MenuListItem = ({ item, lng, t }: { item: MenuItem; lng: string; t: (key: string) => string }) => {
return (
<NavigationMenuLink asChild>
<Link
@@ -84,10 +88,10 @@ const MenuListItem = ({ item, lng }: { item: MenuItem; lng: string }) => {
)}
>
<div className="space-y-1">
- <div className="text-sm font-medium leading-none">{item.title}</div>
- {item.description && (
+ <div className="text-sm font-medium leading-none">{t(item.titleKey)}</div>
+ {item.descriptionKey && (
<p className="line-clamp-2 text-xs leading-snug text-muted-foreground">
- {item.description}
+ {t(item.descriptionKey)}
</p>
)}
</div>
diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx
index 0b04c0c3..0e9e2abe 100644
--- a/components/layout/Header.tsx
+++ b/components/layout/Header.tsx
@@ -36,7 +36,8 @@ import {
MenuSection,
MenuItem,
mainNavVendor,
- additionalNavVendor
+ additionalNavVendor,
+ domainBrandingKeys
} from "@/config/menuConfig";
import { MobileMenu } from "./MobileMenu";
import { CommandMenu } from "./command-menu";
@@ -44,6 +45,7 @@ import { useSession, signOut } from "next-auth/react";
import GroupedMenuRenderer from "./GroupedMenuRender";
import { useActiveMenus, filterActiveMenus, filterActiveAdditionalMenus } from "@/hooks/use-active-menus";
import { NotificationDropdown } from "./NotificationDropdown";
+import { useTranslation } from '@/i18n/client'
export function Header() {
const params = useParams();
@@ -51,8 +53,8 @@ export function Header() {
const pathname = usePathname();
const { data: session } = useSession();
const { activeMenus, isLoading } = useActiveMenus();
+ const { t } = useTranslation(lng, 'menu');
- console.log(session)
const userName = session?.user?.name || "";
const domain = session?.user?.domain || "";
@@ -74,7 +76,7 @@ export function Header() {
main: mainNavVendor,
additional: additionalNavVendor,
logoHref: `/${lng}/partners`,
- brandName: "eVCP Partners",
+ brandNameKey: domainBrandingKeys.partners,
basePath: `/${lng}/partners`
};
}
@@ -84,7 +86,7 @@ export function Header() {
main: procurementNav,
additional: additional2Nav,
logoHref: `/${lng}/procurement`,
- brandName: "eVCP 구매관리",
+ brandNameKey: domainBrandingKeys.procurement,
basePath: `/${lng}/procurement`
};
}
@@ -94,7 +96,7 @@ export function Header() {
main: salesNav,
additional: additional2Nav,
logoHref: `/${lng}/sales`,
- brandName: "eVCP 기술영업",
+ brandNameKey: domainBrandingKeys.sales,
basePath: `/${lng}/sales`
};
}
@@ -104,7 +106,7 @@ export function Header() {
main: engineeringNav,
additional: additional2Nav,
logoHref: `/${lng}/engineering`,
- brandName: "eVCP 설계관리",
+ brandNameKey: domainBrandingKeys.engineering,
basePath: `/${lng}/engineering`
};
}
@@ -114,12 +116,12 @@ export function Header() {
main: mainNav,
additional: additionalNav,
logoHref: `/${lng}/evcp`,
- brandName: "eVCP 삼성중공업",
+ brandNameKey: domainBrandingKeys.evcp,
basePath: `/${lng}/evcp`
};
};
- const { main: originalMain, additional: originalAdditional, logoHref, brandName, basePath } = getDomainConfig(pathname);
+ const { main: originalMain, additional: originalAdditional, logoHref, brandNameKey, basePath } = getDomainConfig(pathname);
// 활성 메뉴만 필터링 (로딩 중이거나 에러 시에는 모든 메뉴 표시)
const main = isLoading ? originalMain : filterActiveMenus(originalMain, activeMenus);
@@ -150,10 +152,10 @@ export function Header() {
d="M3.75 9h16.5m-16.5 6.75h16.5"
/>
</svg>
- <span className="sr-only">Toggle Menu</span>
+ <span className="sr-only">{t('menu.toggle_menu')}</span>
</Button>
- {/* 로고 영역 - 도메인별 브랜딩 */}
+ {/* 로고 영역 - 도메인별 브랜딩 (번역된) */}
<div className="mr-4 flex-shrink-0 flex items-center gap-2 lg:mr-6">
<Link href={logoHref} className="flex items-center gap-2">
<Image
@@ -164,20 +166,20 @@ export function Header() {
height={20}
/>
<span className="hidden font-bold lg:inline-block">
- {brandName}
+ {t(brandNameKey)}
</span>
</Link>
</div>
- {/* 네비게이션 메뉴 - 도메인별 활성화된 메뉴만 표시 */}
+ {/* 네비게이션 메뉴 - 도메인별 활성화된 메뉴만 표시 (번역된) */}
<div className="hidden md:block flex-1 min-w-0">
<NavigationMenu className="relative z-50">
<div className="w-full overflow-x-auto pb-1">
<NavigationMenuList className="flex-nowrap w-max">
{main.map((section: MenuSection) => (
- <NavigationMenuItem key={section.title}>
+ <NavigationMenuItem key={section.titleKey}>
<NavigationMenuTrigger className="px-2 xl:px-3 text-sm whitespace-nowrap">
- {section.title}
+ {t(section.titleKey)}
</NavigationMenuTrigger>
{/* 그룹핑이 필요한 메뉴의 경우 GroupedMenuRenderer 사용 */}
@@ -187,6 +189,7 @@ export function Header() {
items={section.items}
lng={lng}
activeMenus={activeMenus}
+ t={t}
/>
</NavigationMenuContent>
) : (
@@ -194,11 +197,11 @@ export function Header() {
<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}
+ key={item.titleKey}
+ title={t(item.titleKey)}
href={`/${lng}${item.href}`}
>
- {item.description}
+ {item.descriptionKey && t(item.descriptionKey)}
</ListItem>
))}
</ul>
@@ -207,9 +210,9 @@ export function Header() {
</NavigationMenuItem>
))}
- {/* 추가 네비게이션 항목 - 도메인별 활성화된 것만 */}
+ {/* 추가 네비게이션 항목 - 도메인별 활성화된 것만 (번역된) */}
{additional.map((item) => (
- <NavigationMenuItem key={item.title}>
+ <NavigationMenuItem key={item.titleKey}>
<Link href={`/${lng}${item.href}`} legacyBehavior passHref>
<NavigationMenuLink
className={cn(
@@ -217,7 +220,7 @@ export function Header() {
"px-2 xl:px-3 text-sm whitespace-nowrap"
)}
>
- {item.title}
+ {t(item.titleKey)}
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
@@ -233,14 +236,14 @@ export function Header() {
<div className="hidden md:block md:w-auto">
<CommandMenu />
</div>
- <Button variant="ghost" size="icon" className="md:hidden" aria-label="Search">
+ <Button variant="ghost" size="icon" className="md:hidden" aria-label={t('common.search')}>
<SearchIcon className="h-5 w-5" />
</Button>
{/* 알림 버튼 */}
<NotificationDropdown />
- {/* 사용자 메뉴 */}
+ {/* 사용자 메뉴 (번역된) */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Avatar className="cursor-pointer h-8 w-8">
@@ -251,14 +254,14 @@ export function Header() {
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48" align="end">
- <DropdownMenuLabel>My Account</DropdownMenuLabel>
+ <DropdownMenuLabel>{t('user.my_account')}</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
- <Link href={`${basePath}/settings`}>Settings</Link>
+ <Link href={`${basePath}/settings`}>{t('user.settings')}</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => signOut({ callbackUrl: `/${lng}/${domain}` })}>
- Logout
+ {t('user.logout')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -274,6 +277,7 @@ export function Header() {
activeMenus={activeMenus}
domainMain={originalMain}
domainAdditional={originalAdditional}
+ t={t}
/>
)}
</header>
diff --git a/components/layout/MobileMenu.tsx b/components/layout/MobileMenu.tsx
index dc02d2e3..6cced283 100644
--- a/components/layout/MobileMenu.tsx
+++ b/components/layout/MobileMenu.tsx
@@ -17,17 +17,19 @@ interface MobileMenuProps {
activeMenus?: Record<string, boolean>;
domainMain?: MenuSection[]; // 헤더에서 계산된 도메인별 메인 메뉴
domainAdditional?: MenuItem[]; // 헤더에서 계산된 도메인별 추가 메뉴
+ t: (key: string) => string; // 번역 함수 추가
}
export function MobileMenu({
- lng,
- onClose,
+ lng,
+ onClose,
activeMenus = {},
domainMain = [],
- domainAdditional = []
+ domainAdditional = [],
+ t
}: MobileMenuProps) {
const router = useRouter();
-
+
const handleLinkClick = (href: string) => {
router.push(href);
onClose();
@@ -37,7 +39,7 @@ export function MobileMenu({
const main = Object.keys(activeMenus).length > 0
? filterActiveMenus(domainMain, activeMenus)
: domainMain;
-
+
const additional = Object.keys(activeMenus).length > 0
? filterActiveAdditionalMenus(domainAdditional, activeMenus)
: domainAdditional;
@@ -55,25 +57,25 @@ export function MobileMenu({
{main.map((section: MenuSection) => (
// 섹션에 아이템이 있는 경우에만 표시
section.items.length > 0 && (
- <li key={section.title}>
- <h3 className="text-md font-medium">{section.title}</h3>
+ <li key={section.titleKey}>
+ <h3 className="text-md font-medium">{t(section.titleKey)}</h3>
<ul className="mt-2 space-y-2">
{section.items.map((item: MenuItem) => (
- <li key={item.title}>
+ <li key={item.titleKey}>
<Link
href={`/${lng}${item.href}`}
className="text-indigo-600"
onClick={() => handleLinkClick(item.href)}
>
- {item.title}
+ {t(item.titleKey)}
{item.label && (
<span className="ml-2 rounded-md bg-[#adfa1d] px-1.5 py-0.5 text-xs text-[#000000]">
{item.label}
</span>
)}
</Link>
- {item.description && (
- <p className="text-xs text-gray-500">{item.description}</p>
+ {item.descriptionKey && (
+ <p className="text-xs text-gray-500">{t(item.descriptionKey)}</p>
)}
</li>
))}
@@ -81,16 +83,16 @@ export function MobileMenu({
</li>
)
))}
-
+
{/* 추가 네비게이션 항목 - 도메인별 활성화된 메뉴만 표시 */}
{additional.map((item: MenuItem) => (
- <li key={item.title}>
+ <li key={item.titleKey}>
<Link
href={`/${lng}${item.href}`}
className="block text-sm text-indigo-600"
onClick={() => handleLinkClick(`/${lng}${item.href}`)}
>
- {item.title}
+ {t(item.titleKey)}
</Link>
</li>
))}