From 02b1cf005cf3e1df64183d20ba42930eb2767a9f Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Thu, 21 Aug 2025 06:57:36 +0000
Subject: (대표님, 최겸) 설계메뉴추가, 작업사항 업데이트 설계메뉴 - 문서관리
 설계메뉴 - 벤더 데이터 gtc 메뉴 업데이트 정보시스템 - 메뉴리스트 및 정보
 업데이트 파일 라우트 업데이트 엑셀임포트 개선 기본계약 개선 벤더 가입과정
 변경 및 개선 벤더 기본정보 - pq 돌체 오류 수정 및 개선 벤더 로그인 과정
 이메일 오류 수정
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../vendor-table/basic-contract-columns.tsx        | 96 ++++++++++++++++++----
 1 file changed, 78 insertions(+), 18 deletions(-)

(limited to 'lib/basic-contract/vendor-table/basic-contract-columns.tsx')

diff --git a/lib/basic-contract/vendor-table/basic-contract-columns.tsx b/lib/basic-contract/vendor-table/basic-contract-columns.tsx
index c9e8da53..1b11285c 100644
--- a/lib/basic-contract/vendor-table/basic-contract-columns.tsx
+++ b/lib/basic-contract/vendor-table/basic-contract-columns.tsx
@@ -32,14 +32,65 @@ import { BasicContractView } from "@/db/schema"
 
 interface GetColumnsProps {
   setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractView> | null>>
+  locale?: string
+  t: (key: string) => string // 번역 함수
 }
 
+// 기본 번역값들 (fallback)
+const fallbackTranslations = {
+  ko: {
+    download: "다운로드",
+    selectAll: "전체 선택", 
+    selectRow: "행 선택",
+    fileInfoMissing: "파일 정보가 없습니다.",
+    fileDownloadError: "파일 다운로드 중 오류가 발생했습니다.",
+    statusValues: {
+      PENDING: "서명대기",
+      COMPLETED: "서명완료"
+    }
+  },
+  en: {
+    download: "Download",
+    selectAll: "Select all",
+    selectRow: "Select row", 
+    fileInfoMissing: "File information is missing.",
+    fileDownloadError: "An error occurred while downloading the file.",
+    statusValues: {
+      PENDING: "Pending",
+      COMPLETED: "Completed"
+    }
+  }
+};
+
+// 안전한 번역 함수
+const safeTranslate = (t: (key: string) => string, key: string, locale: string = 'ko', fallback?: string): string => {
+  try {
+    const translated = t(key);
+    // 번역 키가 그대로 반환되는 경우 (번역 실패) fallback 사용
+    if (translated === key && fallback) {
+      return fallback;
+    }
+    return translated || fallback || key;
+  } catch (error) {
+    console.warn(`Translation failed for key: ${key}`, error);
+    return fallback || key;
+  }
+};
+
 /**
  * 파일 다운로드 함수
  */
-const handleFileDownload = async (filePath: string | null, fileName: string | null) => {
+const handleFileDownload = async (
+  filePath: string | null, 
+  fileName: string | null,
+  t: (key: string) => string,
+  locale: string = 'ko'
+) => {
+  const fallback = fallbackTranslations[locale as keyof typeof fallbackTranslations] || fallbackTranslations.ko;
+  
   if (!filePath || !fileName) {
-    toast.error("파일 정보가 없습니다.");
+    const message = safeTranslate(t, "basicContracts.fileInfoMissing", locale, fallback.fileInfoMissing);
+    toast.error(message);
     return;
   }
 
@@ -57,14 +108,17 @@ const handleFileDownload = async (filePath: string | null, fileName: string | nu
     }
   } catch (error) {
     console.error("파일 다운로드 오류:", error);
-    toast.error("파일 다운로드 중 오류가 발생했습니다.");
+    const message = safeTranslate(t, "basicContracts.fileDownloadError", locale, fallback.fileDownloadError);
+    toast.error(message);
   }
 };
 
 /**
  * tanstack table 컬럼 정의 (중첩 헤더 버전)
  */
-export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicContractView>[] {
+export function getColumns({ setRowAction, locale = 'ko', t }: GetColumnsProps): ColumnDef<BasicContractView>[] {
+  const fallback = fallbackTranslations[locale as keyof typeof fallbackTranslations] || fallbackTranslations.ko;
+  
   // ----------------------------------------------------------------
   // 1) select 컬럼 (체크박스)
   // ----------------------------------------------------------------
@@ -77,7 +131,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
           (table.getIsSomePageRowsSelected() && "indeterminate")
         }
         onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
-        aria-label="Select all"
+        aria-label={safeTranslate(t, "basicContracts.selectAll", locale, fallback.selectAll)}
         className="translate-y-0.5"
       />
     ),
@@ -85,7 +139,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
       <Checkbox
         checked={row.getIsSelected()}
         onCheckedChange={(value) => row.toggleSelected(!!value)}
-        aria-label="Select row"
+        aria-label={safeTranslate(t, "basicContracts.selectRow", locale, fallback.selectRow)}
         className="translate-y-0.5"
       />
     ),
@@ -105,18 +159,19 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
       // PENDING 상태일 때는 원본 PDF 파일 (signedFilePath), COMPLETED일 때는 서명된 파일 (signedFilePath)
       const filePath = contract.signedFilePath;
       const fileName = contract.signedFileName;
+      const downloadText = safeTranslate(t, "basicContracts.download", locale, fallback.download);
       
       return (
         <Button
           variant="ghost"
           size="icon"
-          onClick={() => handleFileDownload(filePath, fileName)}
-          title={`${fileName} 다운로드`}
+          onClick={() => handleFileDownload(filePath, fileName, t, locale)}
+          title={`${fileName} ${downloadText}`}
           className="hover:bg-muted"
           disabled={!filePath || !fileName}
         >
           <Paperclip className="h-4 w-4" />
-          <span className="sr-only">다운로드</span>
+          <span className="sr-only">{downloadText}</span>
         </Button>
       );
     },
@@ -124,7 +179,6 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
     enableSorting: false,
   }
 
-
   // ----------------------------------------------------------------
   // 4) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성
   // ----------------------------------------------------------------
@@ -152,22 +206,28 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
         type: cfg.type,
       },
       cell: ({ row, cell }) => {
-        // 날짜 형식 처리
+        // 날짜 형식 처리 - 로케일 적용
         if (cfg.id === "createdAt" || cfg.id === "updatedAt" || cfg.id === "completedAt") {
           const dateVal = cell.getValue() as Date
-          return formatDateTime(dateVal)
+          return formatDateTime(dateVal, locale)
         }
         
-        // Status 컬럼에 Badge 적용
+        // Status 컬럼에 Badge 적용 - 다국어 적용
         if (cfg.id === "status") {
           const status = row.getValue(cfg.id) as string
           const isPending = status === "PENDING"
+          const statusText = safeTranslate(
+            t, 
+            `basicContracts.statusValues.${status}`, 
+            locale, 
+            fallback.statusValues[status as keyof typeof fallback.statusValues] || status
+          );
           
           return (
             <Badge 
               variant={!isPending ? "default" : "secondary"}
             >
-              {status}
+              {statusText}
             </Badge>
           )
         }
@@ -175,8 +235,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
         // 나머지 컬럼은 그대로 값 표시
         return row.getValue(cfg.id) ?? ""
       },
-    minSize: 80,
-
+      minSize: 80,
     }
 
     groupMap[groupName].push(childCol)
@@ -194,10 +253,11 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
       // 그룹 없음 → 그냥 최상위 레벨 컬럼
       nestedColumns.push(...colDefs)
     } else {
-      // 상위 컬럼
+      // 상위 컬럼 - 그룹명 다국어 적용
+      const translatedGroupName = t(`basicContracts.groups.${groupName}`) || groupName;
       nestedColumns.push({
         id: groupName,
-        header: groupName, // "Basic Info", "Metadata" 등
+        header: translatedGroupName,
         columns: colDefs,
       })
     }
-- 
cgit v1.2.3

