From 7548e2ad6948f1c6aa102fcac408bc6c9c0f9796 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 27 Aug 2025 12:06:26 +0000
Subject: (대표님, 최겸) 기본계약, 입찰, 파일라우트, 계약서명라우트,
 인포메이션, 메뉴설정, PQ(메일템플릿 관련)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../status/basic-contract-columns.tsx              | 363 ++++++++++++++-------
 1 file changed, 240 insertions(+), 123 deletions(-)

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

diff --git a/lib/basic-contract/status/basic-contract-columns.tsx b/lib/basic-contract/status/basic-contract-columns.tsx
index cc9d9bff..8ae8fa1e 100644
--- a/lib/basic-contract/status/basic-contract-columns.tsx
+++ b/lib/basic-contract/status/basic-contract-columns.tsx
@@ -8,26 +8,34 @@ import { formatDateTime } from "@/lib/utils"
 import { Badge } from "@/components/ui/badge"
 import { Checkbox } from "@/components/ui/checkbox"
 import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
-import { 
-  FileActionsDropdown, 
-  FileNameLink 
-} from "@/components/ui/file-actions"
+import { Button } from "@/components/ui/button"
+import { MoreHorizontal, Eye, FileText, Calendar } from "lucide-react"
+import {
+  DropdownMenu,
+  DropdownMenuContent,
+  DropdownMenuItem,
+  DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { useRouter } from "next/navigation"
+import { BasicContractTemplateStatsView } from "@/db/schema"
+
+type NextRouter = ReturnType<typeof useRouter>;
 
-import { basicContractColumnsConfig } from "@/config/basicContractColumnsConfig"
-import { BasicContractView } from "@/db/schema"
 
 interface GetColumnsProps {
-  setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractView> | null>>
+  setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BasicContractTemplateStatsView> | null>>
+  router: NextRouter;
+
 }
 
 /**
- * 공용 파일 다운로드 유틸리티를 사용하는 간소화된 컬럼 정의
+ * BasicContractTemplateStatsView용 컬럼 정의
  */
-export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicContractView>[] {
+export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef<BasicContractTemplateStatsView>[] {
   // ----------------------------------------------------------------
   // 1) select 컬럼 (체크박스)
   // ----------------------------------------------------------------
-  const selectColumn: ColumnDef<BasicContractView> = {
+  const selectColumn: ColumnDef<BasicContractTemplateStatsView> = {
     id: "select",
     header: ({ table }) => (
       <Checkbox
@@ -54,143 +62,252 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<BasicCo
   }
 
   // ----------------------------------------------------------------
-  // 2) 파일 다운로드 컬럼 (공용 컴포넌트 사용)
+  // 2) Actions 컬럼
   // ----------------------------------------------------------------
-  const downloadColumn: ColumnDef<BasicContractView> = {
-    id: "download",
-    header: "",
+  const actionsColumn: ColumnDef<BasicContractTemplateStatsView> = {
+    id: "actions",
+    header: "작업",
     cell: ({ row }) => {
-      const template = row.original;
-      
-      if (!template.filePath || !template.fileName) {
-        return null;
-      }
+      const template = row.original
+      const detailUrl = `/evcp/basic-contract/${template.templateId}`;
       
       return (
-        <FileActionsDropdown
-          filePath={template.filePath}
-          fileName={template.fileName}
-          variant="ghost"
-          size="icon"
-        />
-      );
+        <DropdownMenu>
+          <DropdownMenuTrigger asChild>
+            <Button variant="ghost" className="h-8 w-8 p-0">
+              <span className="sr-only">Open menu</span>
+              <MoreHorizontal className="h-4 w-4" />
+            </Button>
+          </DropdownMenuTrigger>
+          <DropdownMenuContent align="end">
+            <DropdownMenuItem onClick={() => router.push(detailUrl)}>
+              <Eye className="mr-2 h-4 w-4" />
+              상세 보기
+            </DropdownMenuItem>
+            <DropdownMenuItem onClick={() => setRowAction({ type: "view", row })}>
+              <FileText className="mr-2 h-4 w-4" />
+              템플릿 보기
+            </DropdownMenuItem>
+          </DropdownMenuContent>
+        </DropdownMenu>
+      )
     },
-    maxSize: 30,
     enableSorting: false,
+    enableHiding: false,
+    maxSize: 80,
   }
 
   // ----------------------------------------------------------------
-  // 3) 일반 컬럼들을 "그룹"별로 묶어 중첩 columns 생성
+  // 3) 기본 정보 컬럼들
   // ----------------------------------------------------------------
-  const groupMap: Record<string, ColumnDef<BasicContractView>[]> = {}
-
-  basicContractColumnsConfig.forEach((cfg) => {
-    const groupName = cfg.group || "_noGroup"
-
-    if (!groupMap[groupName]) {
-      groupMap[groupName] = []
-    }
+  const basicInfoColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+    {
+      accessorKey: "templateName",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="템플릿명" />
+      ),
+      cell: ({ row }) => {
+        const name = row.getValue("templateName") as string
+        return (
+          <div className="font-medium">
+            {name}
+          </div>
+        )
+      },
+      minSize: 200,
+    },
+    {
+      accessorKey: "revision",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="리비전" />
+      ),
+      cell: ({ row }) => {
+        const revision = row.getValue("revision") as number
+        return (
+          <Badge variant="outline">
+            v{revision}
+          </Badge>
+        )
+      },
+      minSize: 80,
+    },
+  ]
 
-    const childCol: ColumnDef<BasicContractView> = {
-      accessorKey: cfg.id,
-      enableResizing: true,
+  // ----------------------------------------------------------------
+  // 4) 발송/처리 현황 컬럼들
+  // ----------------------------------------------------------------
+  const processStatusColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+    {
+      accessorKey: "totalSentCount",
       header: ({ column }) => (
-        <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+        <DataTableColumnHeaderSimple column={column} title="발송건수" />
       ),
-      meta: {
-        excelHeader: cfg.excelHeader,
-        group: cfg.group,
-        type: cfg.type,
+      cell: ({ row }) => {
+        const count = row.getValue("totalSentCount") as number
+        return (
+          <div className="text-center font-medium">
+            {count.toLocaleString()}
+          </div>
+        )
       },
-      cell: ({ row, cell }) => {
-        // 날짜 형식 처리
-        if (cfg.id === "createdAt" || cfg.id === "updatedAt") {
-          const dateVal = cell.getValue() as Date
-          return formatDateTime(dateVal, "KR")
-        }
+      minSize: 100,
+    },
+    {
+      accessorKey: "overdueCount",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="지연건수" />
+      ),
+      cell: ({ row }) => {
+        const count = row.getValue("overdueCount") as number
+        const total = row.getValue("totalSentCount") as number
+        const isHigh = total > 0 && (count / total) > 0.2
         
-        // Status 컬럼에 Badge 적용 (확장)
-        if (cfg.id === "status") {
-          const status = row.getValue(cfg.id) as string
-          
-          let variant: "default" | "secondary" | "destructive" | "outline" = "secondary";
-          let label = status;
-          
-          switch (status) {
-            case "ACTIVE":
-              variant = "default";
-              label = "활성";
-              break;
-            case "INACTIVE":
-              variant = "secondary";
-              label = "비활성";
-              break;
-            case "PENDING":
-              variant = "outline";
-              label = "대기중";
-              break;
-            case "COMPLETED":
-              variant = "default";
-              label = "완료";
-              break;
-            default:
-              variant = "secondary";
-              label = status;
-          }
-          
-          return <Badge variant={variant}>{label}</Badge>
-        }
-
-        // ✅ 파일 이름 컬럼 (공용 컴포넌트 사용)
-        if (cfg.id === "fileName") {
-          const fileName = cell.getValue() as string;
-          const filePath = row.original.filePath;
-          
-          if (fileName && filePath) {
-            return (
-              <FileNameLink
-                filePath={filePath}
-                fileName={fileName}
-                maxLength={200}
-                showIcon={true}
-              />
-            );
-          }
-          return fileName || "";
-        }
-
-        // 나머지 컬럼은 그대로 값 표시
-        return row.getValue(cfg.id) ?? ""
+        return (
+          <div className={`text-center font-medium ${isHigh ? 'text-red-600' : 'text-gray-900'}`}>
+            {count.toLocaleString()}
+          </div>
+        )
       },
-      minSize: 80,
-    }
+      minSize: 100,
+    },
+    {
+      accessorKey: "unsignedCount",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="미서명건수" />
+      ),
+      cell: ({ row }) => {
+        const count = row.getValue("unsignedCount") as number
+        return (
+          <div className="text-center font-medium text-orange-600">
+            {count.toLocaleString()}
+          </div>
+        )
+      },
+      minSize: 120,
+    },
+  ]
 
-    groupMap[groupName].push(childCol)
-  })
+  // ----------------------------------------------------------------
+  // 5) 법무검토 현황 컬럼들
+  // ----------------------------------------------------------------
+  const legalReviewColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+    {
+      accessorKey: "legalRequestCount",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="법무요청" />
+      ),
+      cell: ({ row }) => {
+        const count = row.getValue("legalRequestCount") as number
+        return (
+          <div className="text-center font-medium text-purple-600">
+            {count.toLocaleString()}
+          </div>
+        )
+      },
+      minSize: 100,
+    },
+    {
+      accessorKey: "legalCompletedCount",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="법무완료" />
+      ),
+      cell: ({ row }) => {
+        const count = row.getValue("legalCompletedCount") as number
+        return (
+          <div className="text-center font-medium text-indigo-600">
+            {count.toLocaleString()}
+          </div>
+        )
+      },
+      minSize: 100,
+    },
+    {
+      accessorKey: "contractCompletedCount",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="계약완료" />
+      ),
+      cell: ({ row }) => {
+        const count = row.getValue("contractCompletedCount") as number
+        return (
+          <div className="text-center font-medium text-indigo-600">
+            {count.toLocaleString()}
+          </div>
+        )
+      },
+      minSize: 100,
+    },
+  ]
 
   // ----------------------------------------------------------------
-  // 4) groupMap에서 실제 상위 컬럼(그룹)을 만들기
+  // 6) 성과 지표 컬럼들
   // ----------------------------------------------------------------
-  const nestedColumns: ColumnDef<BasicContractView>[] = []
+  const performanceColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+    {
+      accessorKey: "avgProcessingDays",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="평균처리일" />
+      ),
+      cell: ({ row }) => {
+        const days = row.getValue("avgProcessingDays") as number | null
+        if (!days) return "-"
+        
+        const rounded = Math.round(days * 10) / 10
+        const isGood = rounded <= 5
+        const isWarning = rounded > 5 && rounded <= 10
+        
+        let colorClass = "text-gray-900"
+        if (isGood) colorClass = "text-green-600"
+        else if (isWarning) colorClass = "text-orange-600"
+        else colorClass = "text-red-600"
+        
+        return (
+          <div className={`text-center font-medium ${colorClass}`}>
+            {rounded}일
+          </div>
+        )
+      },
+      minSize: 100,
+    },
+  ]
 
-  Object.entries(groupMap).forEach(([groupName, colDefs]) => {
-    if (groupName === "_noGroup") {
-      nestedColumns.push(...colDefs)
-    } else {
-      nestedColumns.push({
-        id: groupName,
-        header: groupName,
-        columns: colDefs,
-      })
-    }
-  })
+  // ----------------------------------------------------------------
+  // 7) 시간 정보 컬럼들
+  // ----------------------------------------------------------------
+  const timeInfoColumns: ColumnDef<BasicContractTemplateStatsView>[] = [
+    {
+      accessorKey: "templateCreatedAt",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="생성일" />
+      ),
+      cell: ({ row }) => {
+        const date = row.getValue("templateCreatedAt") as Date
+        return formatDateTime(date, "KR")
+      },
+      minSize: 120,
+    },
+    {
+      accessorKey: "lastActivityDate",
+      header: ({ column }) => (
+        <DataTableColumnHeaderSimple column={column} title="최근활동" />
+      ),
+      cell: ({ row }) => {
+        const date = row.getValue("lastActivityDate") as Date | null
+        return date ? formatDateTime(date, "KR") : "-"
+      },
+      minSize: 120,
+    },
+  ]
 
   // ----------------------------------------------------------------
-  // 5) 최종 컬럼 배열
+  // 8) 최종 컬럼 배열 (평면 구조)
   // ----------------------------------------------------------------
   return [
     selectColumn,
-    downloadColumn, // ✅ 공용 파일 액션 컴포넌트 사용
-    ...nestedColumns,
+    ...basicInfoColumns,
+    ...processStatusColumns,
+    ...legalReviewColumns,
+    ...performanceColumns,
+    ...timeInfoColumns,
+    actionsColumn,
   ]
-}
+}
\ No newline at end of file
-- 
cgit v1.2.3

