"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 | 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[] { return [ // ═══════════════════════════════════════════════════════════════ // 선택 및 기본 정보 // ═══════════════════════════════════════════════════════════════ { id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!v)} aria-label="select all" className="translate-y-0.5" /> ), cell: ({ row }) => ( row.toggleSelected(!!v)} aria-label="select row" className="translate-y-0.5" /> ), size: 40, enableSorting: false, enableHiding: false, }, // ░░░ 계약번호 ░░░ { accessorKey: "contractNumber", header: ({ column }) => , cell: ({ row }) => (
{row.original.contractNumber} {row.original.revision > 0 && ( Rev.{row.original.revision} )}
), size: 150, meta: { excelHeader: "계약번호 (Rev.)" }, }, // ░░░ 계약상태 ░░░ { accessorKey: "status", header: ({ column }) => , cell: ({ row }) => ( {getStatusText(row.original.status)} ), size: 120, meta: { excelHeader: "계약상태" }, }, // ░░░ 계약명 ░░░ { accessorKey: "name", header: ({ column }) => , cell: ({ row }) => (
), size: 200, meta: { excelHeader: "계약명" }, }, // ═══════════════════════════════════════════════════════════════ // 계약 정보 // ═══════════════════════════════════════════════════════════════ { header: "계약 정보", columns: [ { accessorKey: "category", header: ({ column }) => , cell: ({ row }) => ( {getCategoryText(row.original.category)} ), size: 100, meta: { excelHeader: "계약구분" }, }, { accessorKey: "type", header: ({ column }) => , cell: ({ row }) => ( {getTypeText(row.original.type)} ), size: 120, meta: { excelHeader: "계약종류" }, }, { accessorKey: "executionMethod", header: ({ column }) => , cell: ({ row }) => ( {getExecutionMethodText(row.original.executionMethod)} ), size: 100, meta: { excelHeader: "체결방식" }, }, { accessorKey: "contractSourceType", header: ({ column }) => , cell: ({ row }) => ( {getcontractSourceTypeText(row.original.contractSourceType)} ), size: 200, meta: { excelHeader: "업체선정방법" }, }, ] }, // ═══════════════════════════════════════════════════════════════ // 협력업체 정보 // ═══════════════════════════════════════════════════════════════ { header: "협력업체", columns: [ { accessorKey: "vendorName", header: ({ column }) => , cell: ({ row }) => (
{row.original.vendorName || '-'} {row.original.vendorCode ? row.original.vendorCode : "-"}
), size: 150, meta: { excelHeader: "협력업체명" }, }, { accessorKey: "projectName", header: ({ column }) => , cell: ({ row }) => (
{row.original.projectName || '-'} {row.original.projectCode ? row.original.projectCode : "-"}
), size: 150, meta: { excelHeader: "프로젝트명" }, }, ] }, // ═══════════════════════════════════════════════════════════════ // 기간 정보 // ═══════════════════════════════════════════════════════════════ { header: "계약기간", columns: [ { id: "contractPeriod", header: ({ column }) => , cell: ({ row }) => { const startDate = row.original.startDate const endDate = row.original.endDate if (!startDate || !endDate) return - const now = new Date() const isActive = now >= new Date(startDate) && now <= new Date(endDate) const isExpired = now > new Date(endDate) return (
{formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")}
{isActive && ( 진행중 )} {isExpired && ( 만료 )}
) }, size: 200, meta: { excelHeader: "계약기간" }, }, ] }, // ═══════════════════════════════════════════════════════════════ // 금액 정보 // ═══════════════════════════════════════════════════════════════ { header: "금액 정보", columns: [ { accessorKey: "currency", header: ({ column }) => , cell: ({ row }) => ( {row.original.currency || 'KRW'} ), size: 60, meta: { excelHeader: "통화" }, }, { accessorKey: "contractAmount", header: ({ column }) => , cell: ({ row }) => ( {formatCurrency(row.original.contractAmount, row.original.currency)} ), size: 200, meta: { excelHeader: "계약금액" }, }, ] }, // ═══════════════════════════════════════════════════════════════ // 담당자 및 관리 정보 // ═══════════════════════════════════════════════════════════════ { header: "관리 정보", columns: [ { accessorKey: "managerName", header: ({ column }) => , cell: ({ row }) => (
{row.original.managerName || '-'}
), size: 100, meta: { excelHeader: "계약담당자" }, }, { accessorKey: "registeredAt", header: ({ column }) => , cell: ({ row }) => ( {formatDate(row.original.registeredAt, "KR")} ), size: 100, meta: { excelHeader: "계약등록일" }, }, { accessorKey: "signedAt", header: ({ column }) => , cell: ({ row }) => ( {row.original.signedAt ? formatDate(row.original.signedAt, "KR") : '-'} ), size: 100, meta: { excelHeader: "계약체결일" }, }, { accessorKey: "linkedPoNumber", header: ({ column }) => , cell: ({ row }) => ( {row.original.linkedPoNumber || '-'} ), size: 140, meta: { excelHeader: "연계 PO번호" }, }, { accessorKey: "lastUpdatedAt", header: ({ column }) => , cell: ({ row }) => ( {formatDate(row.original.lastUpdatedAt, "KR")} ), size: 100, meta: { excelHeader: "최종수정일" }, }, { accessorKey: "lastUpdatedByName", header: ({ column }) => , cell: ({ row }) => ( {row.original.lastUpdatedByName || '-'} ), size: 100, meta: { excelHeader: "최종수정자" }, }, ] }, // ░░░ 비고 ░░░ { accessorKey: "notes", header: ({ column }) => , cell: ({ row }) => (
{row.original.notes || '-'}
), size: 150, meta: { excelHeader: "비고" }, }, // ═══════════════════════════════════════════════════════════════ // 액션 // ═══════════════════════════════════════════════════════════════ { id: "actions", header: "액션", cell: ({ row }) => ( {row.original.status !== 'Contract Delete' && ( <> setRowAction({ row, type: "view" })}> 상세보기 setRowAction({ row, type: "update" })}> 수정 )} ), size: 50, enableSorting: false, enableHiding: false, }, ] }