From 4e63d8427d26d0d1b366ddc53650e15f3481fc75 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Tue, 24 Jun 2025 01:44:03 +0000
Subject: (대표님/최겸) 20250624 작업사항 10시43분
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/evaluation-targets-columns.tsx           | 351 ++++++++-------------
 1 file changed, 125 insertions(+), 226 deletions(-)

(limited to 'lib/evaluation-target-list/table/evaluation-targets-columns.tsx')

diff --git a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
index 93807ef9..e2163cad 100644
--- a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
+++ b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
@@ -4,16 +4,17 @@ import { type ColumnDef } from "@tanstack/react-table";
 import { Checkbox } from "@/components/ui/checkbox";
 import { Badge } from "@/components/ui/badge";
 import { Button } from "@/components/ui/button";
-import { Pencil, Eye, MessageSquare, Check, X } from "lucide-react";
+import { Pencil, Check, X } from "lucide-react";
 import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header";
 import { EvaluationTargetWithDepartments } from "@/db/schema";
-import { EditEvaluationTargetSheet } from "./update-evaluation-target";
+import type { DataTableRowAction } from "@/types/table";
+import { formatDate } from "@/lib/utils";
 
 interface GetColumnsProps {
   setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<EvaluationTargetWithDepartments> | null>>;
 }
 
-// 상태별 색상 매핑
+// ✅ 모든 헬퍼 함수들을 컴포넌트 외부로 이동 (매번 재생성 방지)
 const getStatusBadgeVariant = (status: string) => {
   switch (status) {
     case "PENDING":
@@ -27,7 +28,15 @@ const getStatusBadgeVariant = (status: string) => {
   }
 };
 
-// 의견 일치 여부 배지
+const getStatusText = (status: string) => {
+  const statusMap = {
+    PENDING: "검토 중",
+    CONFIRMED: "확정",
+    EXCLUDED: "제외"
+  };
+  return statusMap[status] || status;
+};
+
 const getConsensusBadge = (consensusStatus: boolean | null) => {
   if (consensusStatus === null) {
     return <Badge variant="outline">검토 중</Badge>;
@@ -38,16 +47,14 @@ const getConsensusBadge = (consensusStatus: boolean | null) => {
   return <Badge variant="destructive">의견 불일치</Badge>;
 };
 
-// 구분 배지
 const getDivisionBadge = (division: string) => {
   return (
-    <Badge variant={division === "PLANT" ? "default" : "secondary"}>
-      {division === "PLANT" ? "해양" : "조선"}
+    <Badge variant={division === "OCEAN" ? "default" : "secondary"}>
+      {division === "OCEAN" ? "해양" : "조선"}
     </Badge>
   );
 };
 
-// 자재구분 배지  
 const getMaterialTypeBadge = (materialType: string) => {
   const typeMap = {
     EQUIPMENT: "기자재",
@@ -57,7 +64,6 @@ const getMaterialTypeBadge = (materialType: string) => {
   return <Badge variant="outline">{typeMap[materialType] || materialType}</Badge>;
 };
 
-// 내외자 배지
 const getDomesticForeignBadge = (domesticForeign: string) => {
   return (
     <Badge variant={domesticForeign === "DOMESTIC" ? "default" : "secondary"}>
@@ -66,24 +72,27 @@ const getDomesticForeignBadge = (domesticForeign: string) => {
   );
 };
 
-// 평가 상태 배지
-const getApprovalBadge = (isApproved: boolean | null) => {
-  if (isApproved === null) {
-    return <Badge variant="outline" className="text-xs">대기중</Badge>;
-  }
-  if (isApproved === true) {
-    return <Badge variant="default" className="bg-green-600 text-xs">승인</Badge>;
+// ✅ 평가 대상 여부 표시 함수
+const getEvaluationTargetBadge = (isTarget: boolean | null) => {
+  if (isTarget === null) {
+    return <Badge variant="outline">미정</Badge>;
   }
-  return <Badge variant="destructive" className="text-xs">거부</Badge>;
+  return isTarget ? (
+    <Badge variant="default" className="bg-blue-600">
+      <Check className="size-3 mr-1" />
+      평가 대상
+    </Badge>
+  ) : (
+    <Badge variant="secondary">
+      <X className="size-3 mr-1" />
+      평가 제외
+    </Badge>
+  );
 };
 
-export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): ColumnDef<EvaluationTargetWithDepartments>[] {
+export function getEvaluationTargetsColumns({ setRowAction }: GetColumnsProps): ColumnDef<EvaluationTargetWithDepartments>[] {
   return [
-    // ═══════════════════════════════════════════════════════════════
-    // 기본 정보
-    // ═══════════════════════════════════════════════════════════════
-
-    // Checkbox 
+    // ✅ Checkbox
     {
       id: "select",
       header: ({ table }) => (
@@ -107,15 +116,13 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
       enableHiding: false,
     },
 
-    // ░░░ 평가년도 ░░░
+    // ✅ 기본 정보
     {
       accessorKey: "evaluationYear",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가년도" />,
       cell: ({ row }) => <span className="font-medium">{row.getValue("evaluationYear")}</span>,
       size: 100,
     },
-
-    // ░░░ 구분 ░░░
     {
       accessorKey: "division",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="구분" />,
@@ -127,24 +134,25 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="상태" />,
       cell: ({ row }) => {
         const status = row.getValue<string>("status");
-        const statusMap = {
-          PENDING: "검토 중",
-          CONFIRMED: "확정",
-          EXCLUDED: "제외"
-        };
         return (
           <Badge variant={getStatusBadgeVariant(status)}>
-            {statusMap[status] || status}
+            {getStatusText(status)}
           </Badge>
         );
       },
       size: 100,
     },
+    {
+      accessorKey: "consensusStatus",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="의견 일치" />,
+      cell: ({ row }) => getConsensusBadge(row.getValue("consensusStatus")),
+      size: 100,
+    },
 
-    // ░░░ 벤더 코드 ░░░
-
+    // ✅ 벤더 정보 그룹
     {
-      header: "협력업체 정보",
+      id: "vendorInfo",
+      header: "벤더 정보",
       columns: [
         {
           accessorKey: "vendorCode",
@@ -154,8 +162,6 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
           ),
           size: 120,
         },
-
-        // ░░░ 벤더명 ░░░
         {
           accessorKey: "vendorName",
           header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="벤더명" />,
@@ -166,267 +172,182 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
           ),
           size: 200,
         },
-
-        // ░░░ 내외자 ░░░
         {
           accessorKey: "domesticForeign",
           header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="내외자" />,
           cell: ({ row }) => getDomesticForeignBadge(row.getValue("domesticForeign")),
           size: 80,
         },
-
+        {
+          accessorKey: "materialType",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재구분" />,
+          cell: ({ row }) => getMaterialTypeBadge(row.getValue("materialType")),
+          size: 120,
+        },
       ]
     },
 
-    // ░░░ 자재구분 ░░░
-    {
-      accessorKey: "materialType",
-      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="자재구분" />,
-      cell: ({ row }) => getMaterialTypeBadge(row.getValue("materialType")),
-      size: 120,
-    },
-
-    // ░░░ 상태 ░░░
-
-
-    // ░░░ 의견 일치 여부 ░░░
-    {
-      accessorKey: "consensusStatus",
-      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="의견 일치" />,
-      cell: ({ row }) => getConsensusBadge(row.getValue("consensusStatus")),
-      size: 100,
-    },
-
-    // ═══════════════════════════════════════════════════════════════
-    // 주문 부서 그룹
-    // ═══════════════════════════════════════════════════════════════
+    // ✅ 발주 담당자
     {
-      header: "발주 평가 담당자",
+      id: "orderReviewer",
+      header: "발주 담당자",
       columns: [
-        {
-          accessorKey: "orderDepartmentName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
-          cell: ({ row }) => {
-            const departmentName = row.getValue<string>("orderDepartmentName");
-            return departmentName ? (
-              <div className="truncate max-w-[120px]" title={departmentName}>
-                {departmentName}
-              </div>
-            ) : (
-              <span className="text-muted-foreground">-</span>
-            );
-          },
-          size: 120,
-        },
         {
           accessorKey: "orderReviewerName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
           cell: ({ row }) => {
             const reviewerName = row.getValue<string>("orderReviewerName");
             return reviewerName ? (
-              <div className="truncate max-w-[100px]" title={reviewerName}>
+              <div className="truncate max-w-[120px]" title={reviewerName}>
                 {reviewerName}
               </div>
             ) : (
               <span className="text-muted-foreground">-</span>
             );
           },
-          size: 100,
+          size: 120,
         },
         {
           accessorKey: "orderIsApproved",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
-          cell: ({ row }) => getApprovalBadge(row.getValue("orderIsApproved")),
-          size: 80,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+          cell: ({ row }) => {
+            const isApproved = row.getValue<boolean>("orderIsApproved");
+            return getEvaluationTargetBadge(isApproved);
+          },
+          size: 120,
         },
-      ],
+      ]
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // 조달 부서 그룹
-    // ═══════════════════════════════════════════════════════════════
+    // ✅ 조달 담당자
     {
-      header: "조달 평가 담당자",
+      id: "procurementReviewer",
+      header: "조달 담당자",
       columns: [
-        {
-          accessorKey: "procurementDepartmentName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
-          cell: ({ row }) => {
-            const departmentName = row.getValue<string>("procurementDepartmentName");
-            return departmentName ? (
-              <div className="truncate max-w-[120px]" title={departmentName}>
-                {departmentName}
-              </div>
-            ) : (
-              <span className="text-muted-foreground">-</span>
-            );
-          },
-          size: 120,
-        },
         {
           accessorKey: "procurementReviewerName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
           cell: ({ row }) => {
             const reviewerName = row.getValue<string>("procurementReviewerName");
             return reviewerName ? (
-              <div className="truncate max-w-[100px]" title={reviewerName}>
+              <div className="truncate max-w-[120px]" title={reviewerName}>
                 {reviewerName}
               </div>
             ) : (
               <span className="text-muted-foreground">-</span>
             );
           },
-          size: 100,
+          size: 120,
         },
         {
           accessorKey: "procurementIsApproved",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
-          cell: ({ row }) => getApprovalBadge(row.getValue("procurementIsApproved")),
-          size: 80,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+          cell: ({ row }) => {
+            const isApproved = row.getValue<boolean>("procurementIsApproved");
+            return getEvaluationTargetBadge(isApproved);
+          },
+          size: 120,
         },
-      ],
+      ]
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // 품질 부서 그룹
-    // ═══════════════════════════════════════════════════════════════
+    // ✅ 품질 담당자
     {
-      header: "품질 평가 담당자",
+      id: "qualityReviewer",
+      header: "품질 담당자",
       columns: [
-        {
-          accessorKey: "qualityDepartmentName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
-          cell: ({ row }) => {
-            const departmentName = row.getValue<string>("qualityDepartmentName");
-            return departmentName ? (
-              <div className="truncate max-w-[120px]" title={departmentName}>
-                {departmentName}
-              </div>
-            ) : (
-              <span className="text-muted-foreground">-</span>
-            );
-          },
-          size: 120,
-        },
         {
           accessorKey: "qualityReviewerName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
           cell: ({ row }) => {
             const reviewerName = row.getValue<string>("qualityReviewerName");
             return reviewerName ? (
-              <div className="truncate max-w-[100px]" title={reviewerName}>
+              <div className="truncate max-w-[120px]" title={reviewerName}>
                 {reviewerName}
               </div>
             ) : (
               <span className="text-muted-foreground">-</span>
             );
           },
-          size: 100,
+          size: 120,
         },
         {
           accessorKey: "qualityIsApproved",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
-          cell: ({ row }) => getApprovalBadge(row.getValue("qualityIsApproved")),
-          size: 80,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+          cell: ({ row }) => {
+            const isApproved = row.getValue<boolean>("qualityIsApproved");
+            return getEvaluationTargetBadge(isApproved);
+          },
+          size: 120,
         },
-      ],
+      ]
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // 설계 부서 그룹
-    // ═══════════════════════════════════════════════════════════════
+    // ✅ 설계 담당자
     {
-      header: "설계 평가 담당자",
+      id: "designReviewer",
+      header: "설계 담당자",
       columns: [
-        {
-          accessorKey: "designDepartmentName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
-          cell: ({ row }) => {
-            const departmentName = row.getValue<string>("designDepartmentName");
-            return departmentName ? (
-              <div className="truncate max-w-[120px]" title={departmentName}>
-                {departmentName}
-              </div>
-            ) : (
-              <span className="text-muted-foreground">-</span>
-            );
-          },
-          size: 120,
-        },
         {
           accessorKey: "designReviewerName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
           cell: ({ row }) => {
             const reviewerName = row.getValue<string>("designReviewerName");
             return reviewerName ? (
-              <div className="truncate max-w-[100px]" title={reviewerName}>
+              <div className="truncate max-w-[120px]" title={reviewerName}>
                 {reviewerName}
               </div>
             ) : (
               <span className="text-muted-foreground">-</span>
             );
           },
-          size: 100,
+          size: 120,
         },
         {
           accessorKey: "designIsApproved",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
-          cell: ({ row }) => getApprovalBadge(row.getValue("designIsApproved")),
-          size: 80,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+          cell: ({ row }) => {
+            const isApproved = row.getValue<boolean>("designIsApproved");
+            return getEvaluationTargetBadge(isApproved);
+          },
+          size: 120,
         },
-      ],
+      ]
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // CS 부서 그룹
-    // ═══════════════════════════════════════════════════════════════
+    // ✅ CS 담당자
     {
-      header: "CS 평가 담당자",
+      id: "csReviewer",
+      header: "CS 담당자",
       columns: [
-        {
-          accessorKey: "csDepartmentName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="부서명" />,
-          cell: ({ row }) => {
-            const departmentName = row.getValue<string>("csDepartmentName");
-            return departmentName ? (
-              <div className="truncate max-w-[120px]" title={departmentName}>
-                {departmentName}
-              </div>
-            ) : (
-              <span className="text-muted-foreground">-</span>
-            );
-          },
-          size: 120,
-        },
         {
           accessorKey: "csReviewerName",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자" />,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="담당자명" />,
           cell: ({ row }) => {
             const reviewerName = row.getValue<string>("csReviewerName");
             return reviewerName ? (
-              <div className="truncate max-w-[100px]" title={reviewerName}>
+              <div className="truncate max-w-[120px]" title={reviewerName}>
                 {reviewerName}
               </div>
             ) : (
               <span className="text-muted-foreground">-</span>
             );
           },
-          size: 100,
+          size: 120,
         },
         {
           accessorKey: "csIsApproved",
-          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가" />,
-          cell: ({ row }) => getApprovalBadge(row.getValue("csIsApproved")),
-          size: 80,
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="평가 대상" />,
+          cell: ({ row }) => {
+            const isApproved = row.getValue<boolean>("csIsApproved");
+            return getEvaluationTargetBadge(isApproved);
+          },
+          size: 120,
         },
-      ],
+      ]
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // 관리 정보
-    // ═══════════════════════════════════════════════════════════════
-
-    // ░░░ 관리자 의견 ░░░
+    // ✅ 의견 및 결과
     {
       accessorKey: "adminComment",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="관리자 의견" />,
@@ -442,8 +363,6 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
       },
       size: 150,
     },
-
-    // ░░░ 종합 의견 ░░░
     {
       accessorKey: "consolidatedComment",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="종합 의견" />,
@@ -459,69 +378,49 @@ export function getEvaluationTargetsColumns({setRowAction}:GetColumnsProps): Col
       },
       size: 150,
     },
-
-    // ░░░ 확정일 ░░░
     {
       accessorKey: "confirmedAt",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="확정일" />,
       cell: ({ row }) => {
         const confirmedAt = row.getValue<Date>("confirmedAt");
-        return confirmedAt ? (
-          <span className="text-sm">
-            {new Intl.DateTimeFormat("ko-KR", {
-              year: "numeric",
-              month: "2-digit",
-              day: "2-digit",
-            }).format(new Date(confirmedAt))}
-          </span>
-        ) : (
-          <span className="text-muted-foreground">-</span>
-        );
+        return <span className="text-sm">{formatDate(confirmedAt, "KR")}</span>;
       },
       size: 100,
     },
-
-    // ░░░ 생성일 ░░░
     {
       accessorKey: "createdAt",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="생성일" />,
       cell: ({ row }) => {
         const createdAt = row.getValue<Date>("createdAt");
-        return createdAt ? (
-          <span className="text-sm">
-            {new Intl.DateTimeFormat("ko-KR", {
-              year: "numeric",
-              month: "2-digit",
-              day: "2-digit",
-            }).format(new Date(createdAt))}
-          </span>
-        ) : (
-          <span className="text-muted-foreground">-</span>
-        );
+        return <span className="text-sm">{formatDate(createdAt, "KR")}</span>;
       },
       size: 100,
     },
 
-    // ░░░ Actions ░░░
+    // ✅ Actions - 가장 안전하게 처리
     {
       id: "actions",
       enableHiding: false,
       size: 40,
       minSize: 40,
       cell: ({ row }) => {
-             return (
+        // ✅ 함수를 직접 정의해서 매번 새로 생성되지 않도록 처리
+        const handleEdit = () => {
+          setRowAction({ row, type: "update" });
+        };
+
+        return (
           <div className="flex items-center gap-1">
             <Button
               variant="ghost"
               size="icon"
               className="size-8"
-              onClick={() => setRowAction({ row, type: "update" })}
+              onClick={handleEdit}
               aria-label="수정"
               title="수정"
             >
               <Pencil className="size-4" />
             </Button>
-
           </div>
         );
       },
-- 
cgit v1.2.3

