From 8642ee064ddf96f1db2b948b4cc8bbbd6cfee820 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 12 Nov 2025 10:42:36 +0000
Subject: (최겸) 구매 일반계약, 입찰 수정
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/bidding/failure/biddings-failure-columns.tsx | 130 +++++++++++++++--------
 1 file changed, 88 insertions(+), 42 deletions(-)

(limited to 'lib/bidding/failure/biddings-failure-columns.tsx')

diff --git a/lib/bidding/failure/biddings-failure-columns.tsx b/lib/bidding/failure/biddings-failure-columns.tsx
index 8a888079..3046dbc0 100644
--- a/lib/bidding/failure/biddings-failure-columns.tsx
+++ b/lib/bidding/failure/biddings-failure-columns.tsx
@@ -5,8 +5,9 @@ import { type ColumnDef } from "@tanstack/react-table"
 import { Badge } from "@/components/ui/badge"
 import { Button } from "@/components/ui/button"
 import {
-  Eye, Calendar, FileX, DollarSign, AlertTriangle, RefreshCw
+  Eye, Calendar, FileX, DollarSign, AlertTriangle, RefreshCw, FileText
 } from "lucide-react"
+import { Checkbox } from "@/components/ui/checkbox"
 import {
   Tooltip,
   TooltipContent,
@@ -27,6 +28,7 @@ import {
 } from "@/db/schema"
 import { formatDate } from "@/lib/utils"
 import { DataTableRowAction } from "@/types/table"
+import { downloadFile } from "@/lib/file-download"
 
 type BiddingFailureItem = {
   id: number
@@ -55,6 +57,15 @@ type BiddingFailureItem = {
   disposalUpdatedAt: Date | null // 폐찰수정일
   disposalUpdatedBy: string | null // 폐찰수정자
 
+  // 폐찰 정보
+  closureReason: string | null // 폐찰사유
+  closureDocuments: {
+    id: number
+    fileName: string
+    originalFileName: string
+    filePath: string
+  }[] // 폐찰 첨부파일들
+
   // 기타 정보
   createdBy: string | null
   createdAt: Date | null
@@ -94,6 +105,25 @@ const formatCurrency = (amount: string | number | null, currency = 'KRW') => {
 export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): ColumnDef<BiddingFailureItem>[] {
 
   return [
+    // ░░░ 선택 ░░░
+    {
+      id: "select",
+      header: "",
+      cell: ({ row }) => (
+        <Checkbox
+          checked={row.getIsSelected()}
+          onCheckedChange={(value) => {
+            // single select 모드에서는 다른 행들의 선택을 해제
+            row.toggleSelected(!!value)
+          }}
+          aria-label="행 선택"
+        />
+      ),
+      size: 50,
+      enableSorting: false,
+      enableHiding: false,
+    },
+
     // ░░░ 입찰번호 ░░░
     {
       accessorKey: "biddingNumber",
@@ -188,7 +218,7 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
       accessorKey: "biddingRegistrationDate",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="입찰등록일" />,
       cell: ({ row }) => (
-        <span className="text-sm">{formatDate(row.original.biddingRegistrationDate, "KR")}</span>
+        <span className="text-sm">{row.original.biddingRegistrationDate ? formatDate(row.original.biddingRegistrationDate, "KR") : '-'}</span>
       ),
       size: 100,
       meta: { excelHeader: "입찰등록일" },
@@ -216,7 +246,7 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
       cell: ({ row }) => (
         <div className="flex items-center gap-1">
           <AlertTriangle className="h-4 w-4 text-red-500" />
-          <span className="text-sm">{formatDate(row.original.disposalDate, "KR")}</span>
+          <span className="text-sm">{row.original.disposalDate ? formatDate(row.original.disposalDate, "KR") : '-'}</span>
         </div>
       ),
       size: 100,
@@ -230,7 +260,7 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
       cell: ({ row }) => (
         <div className="flex items-center gap-1">
           <FileX className="h-4 w-4 text-red-500" />
-          <span className="text-sm">{formatDate(row.original.disposalUpdatedAt, "KR")}</span>
+          <span className="text-sm">{row.original.disposalUpdatedAt ? formatDate(row.original.disposalUpdatedAt, "KR") : '-'}</span>
         </div>
       ),
       size: 100,
@@ -248,6 +278,57 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
       meta: { excelHeader: "폐찰수정자" },
     },
 
+    // ░░░ 폐찰사유 ░░░
+    {
+      id: "closureReason",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="폐찰사유" />,
+      cell: ({ row }) => (
+        <div className="max-w-[200px] truncate" title={row.original.closureReason || undefined}>
+          <span className="text-sm">{row.original.closureReason || '-'}</span>
+        </div>
+      ),
+      size: 150,
+      meta: { excelHeader: "폐찰사유" },
+    },
+
+    // ░░░ 폐찰첨부파일 ░░░
+    {
+      id: "closureDocuments",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="폐찰첨부파일" />,
+      cell: ({ row }) => {
+        const documents = row.original.closureDocuments || []
+
+        if (documents.length === 0) {
+          return <span className="text-sm text-muted-foreground">-</span>
+        }
+
+        return (
+          <div className="flex flex-wrap gap-1 max-w-[200px]">
+            {documents.map((doc) => (
+              <Button
+                key={doc.id}
+                variant="link"
+                size="sm"
+                className="p-0 h-auto text-xs underline"
+                onClick={async () => {
+                  try {
+                    await downloadFile(doc.filePath, doc.originalFileName)
+                  } catch (error) {
+                    console.error('파일 다운로드 실패:', error)
+                  }
+                }}
+              >
+                <FileText className="mr-1 h-3 w-3" />
+                {doc.originalFileName}
+              </Button>
+            ))}
+          </div>
+        )
+      },
+      size: 200,
+      meta: { excelHeader: "폐찰첨부파일" },
+    },
+
     // ░░░ P/R번호 ░░░
     {
       accessorKey: "prNumber",
@@ -267,7 +348,7 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
         <span className="text-sm">{row.original.createdBy || '-'}</span>
       ),
       size: 100,
-      meta: { excelHeader: "등록자" },
+      meta: { excelHeader: "최종수정자" },
     },
 
     // ░░░ 등록일시 ░░░
@@ -275,46 +356,11 @@ export function getBiddingsFailureColumns({ setRowAction }: GetColumnsProps): Co
       accessorKey: "createdAt",
       header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="등록일시" />,
       cell: ({ row }) => (
-        <span className="text-sm">{formatDate(row.original.createdAt, "KR")}</span>
+        <span className="text-sm">{row.original.createdAt ? formatDate(row.original.createdAt, "KR") : '-'}</span>
       ),
       size: 100,
-      meta: { excelHeader: "등록일시" },
+      meta: { excelHeader: "최종일시" },
     },
 
-    // ═══════════════════════════════════════════════════════════════
-    // 액션
-    // ═══════════════════════════════════════════════════════════════
-    {
-      id: "actions",
-      header: "액션",
-      cell: ({ row }) => (
-        <DropdownMenu>
-          <DropdownMenuTrigger asChild>
-            <Button variant="ghost" className="h-8 w-8 p-0">
-              <span className="sr-only">메뉴 열기</span>
-              <FileX className="h-4 w-4" />
-            </Button>
-          </DropdownMenuTrigger>
-          <DropdownMenuContent align="end">
-            <DropdownMenuItem onClick={() => setRowAction({ row, type: "view" })}>
-              <Eye className="mr-2 h-4 w-4" />
-              상세보기
-            </DropdownMenuItem>
-            <DropdownMenuItem onClick={() => setRowAction({ row, type: "history" })}>
-              <Calendar className="mr-2 h-4 w-4" />
-              이력보기
-            </DropdownMenuItem>
-            <DropdownMenuSeparator />
-            <DropdownMenuItem onClick={() => setRowAction({ row, type: "rebid" })}>
-              <RefreshCw className="mr-2 h-4 w-4" />
-              재입찰
-            </DropdownMenuItem>
-          </DropdownMenuContent>
-        </DropdownMenu>
-      ),
-      size: 50,
-      enableSorting: false,
-      enableHiding: false,
-    },
   ]
 }
-- 
cgit v1.2.3

