diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-04 09:36:14 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-04 09:36:14 +0000 |
| commit | 92eda21e45d902663052575aaa4c4f80bfa2faea (patch) | |
| tree | 8483702edf82932d4359a597a854fa8e1b48e94b /components/layout | |
| parent | f0213de0d2fb5fcb931b3ddaddcbb6581cab5d28 (diff) | |
(대표님) 벤더 문서 변경사항, data-table 변경, sync 변경
Diffstat (limited to 'components/layout')
| -rw-r--r-- | components/layout/GroupedMenuRender.tsx | 30 | ||||
| -rw-r--r-- | components/layout/Header.tsx | 54 | ||||
| -rw-r--r-- | components/layout/MobileMenu.tsx | 30 |
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> ))} |
