diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-11-10 11:25:19 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2025-11-10 11:25:19 +0900 |
| commit | a5501ad1d1cb836d2b2f84e9b0f06049e22c901e (patch) | |
| tree | 667ed8c5d6ec35b109190e9f976d66ae54def4ce /lib/general-contracts_old/main/general-contracts-table-columns.tsx | |
| parent | b0fe980376fcf1a19ff4b90851ca8b01f378fdc0 (diff) | |
| parent | f8a38907911d940cb2e8e6c9aa49488d05b2b578 (diff) | |
Merge remote-tracking branch 'origin/dujinkim' into master_homemaster
Diffstat (limited to 'lib/general-contracts_old/main/general-contracts-table-columns.tsx')
| -rw-r--r-- | lib/general-contracts_old/main/general-contracts-table-columns.tsx | 571 |
1 files changed, 571 insertions, 0 deletions
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,
+ },
+ ]
+}
|
