diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-21 06:57:36 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-21 06:57:36 +0000 |
| commit | 02b1cf005cf3e1df64183d20ba42930eb2767a9f (patch) | |
| tree | e932c54d5260b0e6fda2b46be2a6ba1c3ee30434 /lib/menu-list | |
| parent | d78378ecd7ceede1429359f8058c7a99ac34b1b7 (diff) | |
(대표님, 최겸) 설계메뉴추가, 작업사항 업데이트
설계메뉴 - 문서관리
설계메뉴 - 벤더 데이터
gtc 메뉴 업데이트
정보시스템 - 메뉴리스트 및 정보 업데이트
파일 라우트 업데이트
엑셀임포트 개선
기본계약 개선
벤더 가입과정 변경 및 개선
벤더 기본정보 - pq
돌체 오류 수정 및 개선
벤더 로그인 과정 이메일 오류 수정
Diffstat (limited to 'lib/menu-list')
| -rw-r--r-- | lib/menu-list/servcie.ts | 61 | ||||
| -rw-r--r-- | lib/menu-list/table/menu-list-table.tsx | 54 |
2 files changed, 77 insertions, 38 deletions
diff --git a/lib/menu-list/servcie.ts b/lib/menu-list/servcie.ts index 8686bf43..cd414ab4 100644 --- a/lib/menu-list/servcie.ts +++ b/lib/menu-list/servcie.ts @@ -3,7 +3,7 @@ "use server"; import db from "@/db/db"; -import { menuAssignments, users } from "@/db/schema"; +import { menuAssignments, users, pageInformation, notice } from "@/db/schema"; import { eq, and } from "drizzle-orm"; import { revalidatePath, revalidateTag } from "next/cache"; import { mainNav, mainNavVendor, additionalNav, additionalNavVendor } from "@/config/menuConfig"; @@ -27,10 +27,10 @@ function extractMenusFromConfig(): MenuData[] { section.items.forEach(item => { menus.push({ menuPath: item.href, - menuTitle: item.title, - menuDescription: item.description, - menuGroup: item.group, - sectionTitle: section.title, + menuTitle: item.titleKey, + menuDescription: item.descriptionKey, + menuGroup: item.groupKey, + sectionTitle: section.titleKey, domain: "evcp" }); }); @@ -40,8 +40,8 @@ function extractMenusFromConfig(): MenuData[] { additionalNav.forEach(item => { menus.push({ menuPath: item.href, - menuTitle: item.title, - menuDescription: item.description, + menuTitle: item.titleKey, + menuDescription: item.descriptionKey, menuGroup: undefined, sectionTitle: "추가 메뉴", domain: "evcp" @@ -53,10 +53,10 @@ function extractMenusFromConfig(): MenuData[] { section.items.forEach(item => { menus.push({ menuPath: item.href, - menuTitle: item.title, - menuDescription: item.description, - menuGroup: item.group, - sectionTitle: section.title, + menuTitle: item.titleKey, + menuDescription: item.descriptionKey, + menuGroup: item.groupKey, + sectionTitle: section.titleKey, domain: "partners" }); }); @@ -66,8 +66,8 @@ function extractMenusFromConfig(): MenuData[] { additionalNavVendor.forEach(item => { menus.push({ menuPath: item.href, - menuTitle: item.title, - menuDescription: item.description, + menuTitle: item.titleKey, + menuDescription: item.descriptionKey, menuGroup: undefined, sectionTitle: "추가 메뉴", domain: "partners" @@ -84,23 +84,34 @@ export async function initializeMenuAssignments() { const existingMenus = await db.select().from(menuAssignments); const existingPaths = new Set(existingMenus.map(m => m.menuPath)); - // 새로운 메뉴만 추가 + // 새로운 메뉴만 추가 (특정 경로 예외처리) const newMenus = configMenus.filter(menu => !existingPaths.has(menu.menuPath)); console.log(newMenus, newMenus) if (newMenus.length > 0) { - await db.insert(menuAssignments).values( - newMenus.map(menu => ({ - menuPath: menu.menuPath, - menuTitle: menu.menuTitle, - menuDescription: menu.menuDescription || null, - menuGroup: menu.menuGroup || null, - sectionTitle: menu.sectionTitle, - domain: menu.domain, - isActive: true, - })) - ); + // 개별적으로 insert하여 중복 오류 처리 + for (const menu of newMenus) { + try { + await db.insert(menuAssignments).values({ + menuPath: menu.menuPath, + menuTitle: menu.menuTitle, + menuDescription: menu.menuDescription || null, + menuGroup: menu.menuGroup || null, + sectionTitle: menu.sectionTitle, + domain: menu.domain, + isActive: true, + }); + } catch (insertError: any) { + // /partners/vendor-data 경로의 중복 오류는 무시 + if (insertError?.code === '23505' && menu.menuPath === '/partners/vendor-data') { + console.warn(`중복 메뉴 경로 건너뛰기: ${menu.menuPath}`); + continue; + } + // 다른 오류는 다시 throw + throw insertError; + } + } } // 기존 메뉴 정보 업데이트 (title, description 등이 변경될 수 있음) diff --git a/lib/menu-list/table/menu-list-table.tsx b/lib/menu-list/table/menu-list-table.tsx index 097be082..dedbc9bf 100644 --- a/lib/menu-list/table/menu-list-table.tsx +++ b/lib/menu-list/table/menu-list-table.tsx @@ -3,6 +3,8 @@ "use client"; import { useState, useMemo } from "react"; +import { useParams } from "next/navigation"; +import { useTranslation } from "@/i18n/client"; import { Table, TableBody, @@ -25,6 +27,7 @@ import { Button } from "@/components/ui/button"; import { Search, Filter, ExternalLink } from "lucide-react"; import { toast } from "sonner"; import { ManagerSelect } from "./manager-select"; +import { InitializeButton } from "./initialize-button"; import { toggleMenuActive } from "../servcie"; interface MenuAssignment { @@ -59,6 +62,26 @@ interface MenuListTableProps { } export function MenuListTable({ initialMenus, initialUsers }: MenuListTableProps) { + // URL에서 언어 파라미터 가져오기 + const params = useParams(); + const lng = (params?.lng as string) || 'ko'; + const { t } = useTranslation(lng, 'menu'); + + // 안전한 번역 함수 (키가 없을 때 원본 키 반환) + const safeTranslate = (key: string): string => { + try { + const translated = t(key); + // 번역 키가 그대로 반환되는 경우 원본 키 사용 + if (translated === key) { + return key; + } + return translated || key; + } catch (error) { + console.warn(`Translation failed for key: ${key}`, error); + return key; + } + }; + const [searchQuery, setSearchQuery] = useState(""); const [domainFilter, setDomainFilter] = useState<string>("all"); const [sectionFilter, setSectionFilter] = useState<string>("all"); @@ -67,27 +90,31 @@ export function MenuListTable({ initialMenus, initialUsers }: MenuListTableProps // 필터링된 메뉴 데이터 const filteredMenus = useMemo(() => { return initialMenus.filter((menu) => { + const translatedTitle = safeTranslate(menu.menuTitle); + const translatedSection = safeTranslate(menu.sectionTitle); + const translatedDescription = menu.menuDescription ? safeTranslate(menu.menuDescription) : ''; + const matchesSearch = - menu.menuTitle.toLowerCase().includes(searchQuery.toLowerCase()) || + translatedTitle.toLowerCase().includes(searchQuery.toLowerCase()) || menu.menuPath.toLowerCase().includes(searchQuery.toLowerCase()) || - menu.sectionTitle.toLowerCase().includes(searchQuery.toLowerCase()) || - (menu.menuDescription?.toLowerCase().includes(searchQuery.toLowerCase()) ?? false); + translatedSection.toLowerCase().includes(searchQuery.toLowerCase()) || + translatedDescription.toLowerCase().includes(searchQuery.toLowerCase()); const matchesDomain = domainFilter === "all" || menu.domain === domainFilter; - const matchesSection = sectionFilter === "all" || menu.sectionTitle === sectionFilter; + const matchesSection = sectionFilter === "all" || translatedSection === sectionFilter; const matchesStatus = statusFilter === "all" || (statusFilter === "active" && menu.isActive) || (statusFilter === "inactive" && !menu.isActive); return matchesSearch && matchesDomain && matchesSection && matchesStatus; }); - }, [initialMenus, searchQuery, domainFilter, sectionFilter, statusFilter]); + }, [initialMenus, searchQuery, domainFilter, sectionFilter, statusFilter, safeTranslate]); - // 섹션 리스트 추출 + // 섹션 리스트 추출 (번역된 이름으로) const sections = useMemo(() => { - const sectionSet = new Set(initialMenus.map(menu => menu.sectionTitle)); + const sectionSet = new Set(initialMenus.map(menu => safeTranslate(menu.sectionTitle))); return Array.from(sectionSet).sort(); - }, [initialMenus]); + }, [initialMenus, safeTranslate]); // 도메인별 사용자 필터링 const getFilteredUsers = (domain: string) => { @@ -163,12 +190,13 @@ export function MenuListTable({ initialMenus, initialUsers }: MenuListTableProps </div> </div> - {/* 결과 요약 */} + {/* 결과 요약 및 초기화 버튼 */} <div className="flex items-center justify-between text-sm text-muted-foreground"> <span> 총 {filteredMenus.length}개의 메뉴 {searchQuery && ` (${initialMenus.length}개 중 검색 결과)`} </span> + <InitializeButton /> </div> {/* 테이블 */} @@ -207,13 +235,13 @@ export function MenuListTable({ initialMenus, initialUsers }: MenuListTableProps <TableCell> <div className="space-y-1"> <div className="flex items-center gap-2"> - <span className="font-medium">{menu.menuTitle}</span> + <span className="font-medium">{(menu as any).translatedMenuTitle || safeTranslate(menu.menuTitle)}</span> <Badge variant="outline" className="text-xs"> - {menu.sectionTitle} + {(menu as any).translatedSectionTitle || safeTranslate(menu.sectionTitle)} </Badge> {menu.menuGroup && ( <Badge variant="secondary" className="text-xs"> - {menu.menuGroup} + {(menu as any).translatedMenuGroup || safeTranslate(menu.menuGroup)} </Badge> )} </div> @@ -222,7 +250,7 @@ export function MenuListTable({ initialMenus, initialUsers }: MenuListTableProps </div> {menu.menuDescription && ( <div className="text-xs text-muted-foreground"> - {menu.menuDescription} + {(menu as any).translatedMenuDescription || safeTranslate(menu.menuDescription)} </div> )} </div> |
