From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 7 Nov 2025 08:39:04 +0000
Subject: (최겸) 입찰/견적 수정사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../main/general-contracts-table-columns.tsx       | 571 +++++++++++++++++++++
 1 file changed, 571 insertions(+)
 create mode 100644 lib/general-contracts_old/main/general-contracts-table-columns.tsx

(limited to 'lib/general-contracts_old/main/general-contracts-table-columns.tsx')

diff --git a/lib/general-contracts_old/main/general-contracts-table-columns.tsx b/lib/general-contracts_old/main/general-contracts-table-columns.tsx
new file mode 100644
index 00000000..a08d8b81
--- /dev/null
+++ b/lib/general-contracts_old/main/general-contracts-table-columns.tsx
@@ -0,0 +1,571 @@
+"use client"
+
+import * as React from "react"
+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 { 
+  Eye, Edit, MoreHorizontal
+} from "lucide-react"
+import {
+  DropdownMenu,
+  DropdownMenuContent,
+  DropdownMenuItem,
+  DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { DataTableRowAction } from "@/types/table"
+import { formatDate } from "@/lib/utils"
+
+// 일반계약 리스트 아이템 타입 정의
+export interface GeneralContractListItem {
+  id: number
+  contractNumber: string
+  revision: number
+  status: string
+  category: string
+  type: string
+  executionMethod: string
+  name: string
+  contractSourceType?: string
+  startDate: string
+  endDate: string
+  validityEndDate?: string
+  contractScope?: string
+  specificationType?: string
+  specificationManualText?: string
+  contractAmount?: number | string | null
+  totalAmount?: number | string | null
+  currency?: string
+  registeredAt: string
+  signedAt?: string
+  linkedPoNumber?: string
+  linkedRfqOrItb?: string
+  linkedBidNumber?: string
+  lastUpdatedAt: string
+  notes?: string
+  vendorId?: number
+  vendorName?: string
+  vendorCode?: string
+  projectId?: number
+  projectName?: string
+  projectCode?: string
+  managerName?: string
+  lastUpdatedByName?: string
+}
+
+interface GetColumnsProps {
+  setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<GeneralContractListItem> | null>>
+}
+
+// 상태별 배지 색상
+const getStatusBadgeVariant = (status: string) => {
+  switch (status) {
+    case 'Draft':
+      return 'outline'
+    case 'Request to Review':
+    case 'Confirm to Review':
+      return 'secondary'
+    case 'Contract Accept Request':
+      return 'default'
+    case 'Complete the Contract':
+      return 'default'
+    case 'Reject to Accept Contract':
+    case 'Contract Delete':
+      return 'destructive'
+    default:
+      return 'outline'
+  }
+}
+
+// 상태 텍스트 변환
+const getStatusText = (status: string) => {
+  switch (status) {
+    case 'Draft':
+      return '임시저장'
+    case 'Request to Review':
+      return '조건검토요청'
+    case 'Confirm to Review':
+      return '조건검토완료'
+    case 'Contract Accept Request':
+      return '계약승인요청'
+    case 'Complete the Contract':
+      return '계약체결'
+    case 'Reject to Accept Contract':
+      return '계약승인거절'
+    case 'Contract Delete':
+      return '계약폐기'
+    case 'PCR Request':
+      return 'PCR요청'
+    case 'VO Request':
+      return 'VO요청'
+    case 'PCR Accept':
+      return 'PCR승인'
+    case 'PCR Reject':
+      return 'PCR거절'
+    default:
+      return status
+  }
+}
+
+// 계약구분 텍스트 변환
+const getCategoryText = (category: string) => {
+  switch (category) {
+    case 'unit_price':
+      return '단가계약'
+    case 'general':
+      return '일반계약'
+    case 'sale':
+      return '매각계약'
+    default:
+      return category
+  }
+}
+
+// 계약종류 텍스트 변환
+const getTypeText = (type: string) => {
+  switch (type) {
+    case 'UP':
+      return '자재단가계약'
+    case 'LE':
+      return '임대차계약'
+    case 'IL':
+      return '개별운송계약'
+    case 'AL':
+      return '연간운송계약'
+    case 'OS':
+      return '외주용역계약'
+    case 'OW':
+      return '도급계약'
+    case 'IS':
+      return '검사계약'
+    case 'LO':
+      return 'LOI'
+    case 'FA':
+      return 'FA'
+    case 'SC':
+      return '납품합의계약'
+    case 'OF':
+      return '클레임상계계약'
+    case 'AW':
+      return '사전작업합의'
+    case 'AD':
+      return '사전납품합의'
+    case 'AM':
+      return '설계계약'
+    case 'SC_SELL':
+      return '폐기물매각계약'
+    default:
+      return type
+  }
+}
+
+// 체결방식 텍스트 변환
+const getExecutionMethodText = (method: string) => {
+  switch (method) {
+    case '전자계약':
+      return '전자계약'
+    case '오프라인계약':
+      return '오프라인계약'
+    default:
+      return method
+  }
+}
+
+// 업체선정방법 텍스트 변환
+const getcontractSourceTypeText = (method?: string) => {
+  if (!method) return '-'
+  switch (method) {
+    case 'estimate':
+      return '견적'
+    case 'bid':
+      return '입찰'
+    case 'manual':
+      return '자체생성'
+    default:
+      return method
+  }
+}
+
+// 금액 포맷팅
+const formatCurrency = (amount: string | number | null | undefined, currency = 'KRW') => {
+  if (!amount && amount !== 0) return '-'
+
+  const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount
+  if (isNaN(numAmount)) return '-'
+
+  // 통화 코드가 null이거나 유효하지 않은 경우 기본값 사용
+  const safeCurrency = currency && typeof currency === 'string' ? currency : 'USD'
+
+  return new Intl.NumberFormat('ko-KR', {
+    style: 'currency',
+    currency: safeCurrency,
+    minimumFractionDigits: 0,
+    maximumFractionDigits: 0,
+  }).format(numAmount)
+}
+
+export function getGeneralContractsColumns({ setRowAction }: GetColumnsProps): ColumnDef<GeneralContractListItem>[] {
+  return [
+    // ═══════════════════════════════════════════════════════════════
+    // 선택 및 기본 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      id: "select",
+      header: ({ table }) => (
+        <Checkbox
+          checked={table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate")}
+          onCheckedChange={(v) => table.toggleAllPageRowsSelected(!!v)}
+          aria-label="select all"
+          className="translate-y-0.5"
+        />
+      ),
+      cell: ({ row }) => (
+        <Checkbox
+          checked={row.getIsSelected()}
+          onCheckedChange={(v) => row.toggleSelected(!!v)}
+          aria-label="select row"
+          className="translate-y-0.5"
+        />
+      ),
+      size: 40,
+      enableSorting: false,
+      enableHiding: false,
+    },
+
+    // ░░░ 계약번호 ░░░
+    {
+      accessorKey: "contractNumber",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약번호 (Rev.)" />,
+      cell: ({ row }) => (
+        <div className="font-mono text-sm">
+          {row.original.contractNumber}
+          {row.original.revision > 0 && (
+            <span className="ml-1 text-xs text-muted-foreground">
+              Rev.{row.original.revision}
+            </span>
+          )}
+        </div>
+      ),
+      size: 150,
+      meta: { excelHeader: "계약번호 (Rev.)" },
+    },
+
+    // ░░░ 계약상태 ░░░
+    {
+      accessorKey: "status",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약상태" />,
+      cell: ({ row }) => (
+        <Badge variant={getStatusBadgeVariant(row.original.status)}>
+          {getStatusText(row.original.status)}
+        </Badge>
+      ),
+      size: 120,
+      meta: { excelHeader: "계약상태" },
+    },
+
+    // ░░░ 계약명 ░░░
+    {
+      accessorKey: "name",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약명" />,
+      cell: ({ row }) => (
+        <div className="truncate max-w-[200px]" title={row.original.name}>
+          <Button
+            variant="link"
+            className="p-0 h-auto text-left justify-start"
+            onClick={() => setRowAction({ row, type: "view" })}
+          >
+            {row.original.name}
+          </Button>
+        </div>
+      ),
+      size: 200,
+      meta: { excelHeader: "계약명" },
+    },
+
+    // ═══════════════════════════════════════════════════════════════
+    // 계약 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      header: "계약 정보",
+      columns: [
+        {
+          accessorKey: "category",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약구분" />,
+          cell: ({ row }) => (
+            <Badge variant="outline">
+              {getCategoryText(row.original.category)}
+            </Badge>
+          ),
+          size: 100,
+          meta: { excelHeader: "계약구분" },
+        },
+
+        {
+          accessorKey: "type",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약종류" />,
+          cell: ({ row }) => (
+            <Badge variant="secondary">
+              {getTypeText(row.original.type)}
+            </Badge>
+          ),
+          size: 120,
+          meta: { excelHeader: "계약종류" },
+        },
+
+        {
+          accessorKey: "executionMethod",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="체결방식" />,
+          cell: ({ row }) => (
+            <Badge variant="outline">
+              {getExecutionMethodText(row.original.executionMethod)}
+            </Badge>
+          ),
+          size: 100,
+          meta: { excelHeader: "체결방식" },
+        },
+
+        {
+          accessorKey: "contractSourceType",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="업체선정방법" />,
+          cell: ({ row }) => (
+            <Badge variant="outline">
+              {getcontractSourceTypeText(row.original.contractSourceType)}
+            </Badge>
+          ),
+          size: 200,
+          meta: { excelHeader: "업체선정방법" },
+        },
+      ]
+    },
+
+    // ═══════════════════════════════════════════════════════════════
+    // 협력업체 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      header: "협력업체",
+      columns: [
+        {
+          accessorKey: "vendorName",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="협력업체명" />,
+          cell: ({ row }) => (
+            <div className="flex flex-col">
+              <span className="font-medium">{row.original.vendorName || '-'}</span>
+              <span className="text-xs text-muted-foreground">
+                {row.original.vendorCode ? row.original.vendorCode : "-"}
+              </span>
+            </div>
+          ),
+          size: 150,
+          meta: { excelHeader: "협력업체명" },
+        },
+
+        {
+          accessorKey: "projectName",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트명" />,
+          cell: ({ row }) => (
+            <div className="flex flex-col">
+              <span className="font-medium">{row.original.projectName || '-'}</span>
+              <span className="text-xs text-muted-foreground">
+                {row.original.projectCode ? row.original.projectCode : "-"}
+              </span>
+            </div>
+          ),
+          size: 150,
+          meta: { excelHeader: "프로젝트명" },
+        },
+
+      ]
+    },
+
+    // ═══════════════════════════════════════════════════════════════
+    // 기간 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      header: "계약기간",
+      columns: [
+        {
+          id: "contractPeriod",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약기간" />,
+          cell: ({ row }) => {
+            const startDate = row.original.startDate
+            const endDate = row.original.endDate
+            
+            if (!startDate || !endDate) return <span className="text-muted-foreground">-</span>
+            
+            const now = new Date()
+            const isActive = now >= new Date(startDate) && now <= new Date(endDate)
+            const isExpired = now > new Date(endDate)
+            
+            return (
+              <div className="text-xs">
+                <div className={`${isActive ? 'text-green-600 font-medium' : isExpired ? 'text-red-600' : 'text-gray-600'}`}>
+                  {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")}
+                </div>
+                {isActive && (
+                  <Badge variant="default" className="text-xs mt-1">진행중</Badge>
+                )}
+                {isExpired && (
+                  <Badge variant="destructive" className="text-xs mt-1">만료</Badge>
+                )}
+              </div>
+            )
+          },
+          size: 200,
+          meta: { excelHeader: "계약기간" },
+        },
+      ]
+    },
+
+    // ═══════════════════════════════════════════════════════════════
+    // 금액 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      header: "금액 정보",
+      columns: [
+        {
+          accessorKey: "currency",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="통화" />,
+          cell: ({ row }) => (
+            <span className="font-mono text-sm">{row.original.currency || 'KRW'}</span>
+          ),
+          size: 60,
+          meta: { excelHeader: "통화" },
+        },
+
+        {
+          accessorKey: "contractAmount",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약금액" />,
+          cell: ({ row }) => (
+            <span className="text-sm font-medium">
+              {formatCurrency(row.original.contractAmount, row.original.currency)}
+            </span>
+          ),
+          size: 200,
+          meta: { excelHeader: "계약금액" },
+        },
+      ]
+    },
+
+    // ═══════════════════════════════════════════════════════════════
+    // 담당자 및 관리 정보
+    // ═══════════════════════════════════════════════════════════════
+    {
+      header: "관리 정보",
+      columns: [
+        {
+          accessorKey: "managerName",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약담당자" />,
+          cell: ({ row }) => (
+            <div className="truncate max-w-[100px]" title={row.original.managerName || ''}>
+              {row.original.managerName || '-'}
+            </div>
+          ),
+          size: 100,
+          meta: { excelHeader: "계약담당자" },
+        },
+
+        {
+          accessorKey: "registeredAt",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약등록일" />,
+          cell: ({ row }) => (
+            <span className="text-sm">{formatDate(row.original.registeredAt, "KR")}</span>
+          ),
+          size: 100,
+          meta: { excelHeader: "계약등록일" },
+        },
+
+        {
+          accessorKey: "signedAt",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="계약체결일" />,
+          cell: ({ row }) => (
+            <span className="text-sm">
+              {row.original.signedAt ? formatDate(row.original.signedAt, "KR") : '-'}
+            </span>
+          ),
+          size: 100,
+          meta: { excelHeader: "계약체결일" },
+        },
+
+        {
+          accessorKey: "linkedPoNumber",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="연계 PO번호" />,
+          cell: ({ row }) => (
+            <span className="font-mono text-sm">{row.original.linkedPoNumber || '-'}</span>
+          ),
+          size: 140,
+          meta: { excelHeader: "연계 PO번호" },
+        },
+
+        {
+          accessorKey: "lastUpdatedAt",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정일" />,
+          cell: ({ row }) => (
+            <span className="text-sm">{formatDate(row.original.lastUpdatedAt, "KR")}</span>
+          ),
+          size: 100,
+          meta: { excelHeader: "최종수정일" },
+        },
+
+        {
+          accessorKey: "lastUpdatedByName",
+          header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="최종수정자" />,
+          cell: ({ row }) => (
+            <span className="text-sm">{row.original.lastUpdatedByName || '-'}</span>
+          ),
+          size: 100,
+          meta: { excelHeader: "최종수정자" },
+        },
+      ]
+    },
+
+    // ░░░ 비고 ░░░
+    {
+      accessorKey: "notes",
+      header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="비고" />,
+      cell: ({ row }) => (
+        <div className="truncate max-w-[150px]" title={row.original.notes || ''}>
+          {row.original.notes || '-'}
+        </div>
+      ),
+      size: 150,
+      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>
+              <MoreHorizontal className="h-4 w-4" />
+            </Button>
+          </DropdownMenuTrigger>
+          <DropdownMenuContent align="end">
+            {row.original.status !== 'Contract Delete' && (
+              <>
+                <DropdownMenuItem onClick={() => setRowAction({ row, type: "view" })}>
+                  <Eye className="mr-2 h-4 w-4" />
+                  상세보기
+                </DropdownMenuItem>
+                <DropdownMenuItem onClick={() => setRowAction({ row, type: "update" })}>
+                  <Edit className="mr-2 h-4 w-4" />
+                  수정
+                </DropdownMenuItem>
+              </>
+            )}
+          </DropdownMenuContent>
+        </DropdownMenu>
+      ),
+      size: 50,
+      enableSorting: false,
+      enableHiding: false,
+    },
+  ]
+}
-- 
cgit v1.2.3

