From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001 From: dujinkim 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 --- lib/bidding/list/biddings-table-columns.tsx | 649 ++++++++++------------------ 1 file changed, 224 insertions(+), 425 deletions(-) (limited to 'lib/bidding/list/biddings-table-columns.tsx') diff --git a/lib/bidding/list/biddings-table-columns.tsx b/lib/bidding/list/biddings-table-columns.tsx index d6044e93..10966e0e 100644 --- a/lib/bidding/list/biddings-table-columns.tsx +++ b/lib/bidding/list/biddings-table-columns.tsx @@ -5,18 +5,10 @@ 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 { getUserCodeByEmail } from "@/lib/bidding/service" import { - Eye, Edit, MoreHorizontal, FileText, Users, Calendar, - Building, Package, DollarSign, Clock, CheckCircle, XCircle, - AlertTriangle + Eye, Edit, MoreHorizontal, FileX } from "lucide-react" -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip" + import { DropdownMenu, DropdownMenuContent, @@ -30,14 +22,13 @@ import { DataTableRowAction } from "@/types/table" // BiddingListItem에 manager 정보 추가 type BiddingListItemWithManagerCode = BiddingListItem & { - managerName?: string | null - managerCode?: string | null + bidPicName?: string | null + supplyPicName?: string | null } import { biddingStatusLabels, contractTypeLabels, biddingTypeLabels, - awardCountLabels } from "@/db/schema" import { formatDate } from "@/lib/utils" @@ -68,23 +59,6 @@ const getStatusBadgeVariant = (status: string) => { } } -// 금액 포맷팅 -const formatCurrency = (amount: string | number | null, currency = 'KRW') => { - if (!amount) return '-' - - const numAmount = typeof amount === 'string' ? parseFloat(amount) : amount - if (isNaN(numAmount)) return '-' - - return new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: currency, - minimumFractionDigits: 0, - maximumFractionDigits: 0, - }).format(numAmount) -} - - - export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef[] { return [ @@ -132,442 +106,256 @@ export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef meta: { excelHeader: "입찰 No." }, }, + // ░░░ 원입찰번호 ░░░ + { + accessorKey: "originalBiddingNumber", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.originalBiddingNumber || '-'} +
+ ), + size: 120, + meta: { excelHeader: "원입찰번호" }, + }, + // ░░░ 프로젝트명 ░░░ + { + accessorKey: "projectName", + header: ({ column }) => , + cell: ({ row }) => ( +
+ {row.original.projectName || '-'} +
+ ), + size: 150, + meta: { excelHeader: "프로젝트명" }, + }, + // ░░░ 입찰명 ░░░ + { + accessorKey: "title", + header: ({ column }) => , + cell: ({ row }) => ( +
+ +
+ ), + size: 200, + meta: { excelHeader: "입찰명" }, + }, + // ░░░ 계약구분 ░░░ + { + accessorKey: "contractType", + header: ({ column }) => , + cell: ({ row }) => ( + + {contractTypeLabels[row.original.contractType]} + + ), + size: 100, + meta: { excelHeader: "계약구분" }, + }, // ░░░ 입찰상태 ░░░ { accessorKey: "status", - header: ({ column }) => , + header: ({ column }) => , cell: ({ row }) => ( {biddingStatusLabels[row.original.status]} ), size: 120, - meta: { excelHeader: "입찰상태" }, + meta: { excelHeader: "진행상태" }, }, - - // ░░░ 긴급여부 ░░░ + // ░░░ 입찰유형 ░░░ { - accessorKey: "isUrgent", - header: ({ column }) => , - cell: ({ row }) => { - const isUrgent = row.original.isUrgent + accessorKey: "biddingType", + header: ({ column }) => , + cell: ({ row }) => ( + + {biddingTypeLabels[row.original.biddingType]} + + ), + size: 100, + meta: { excelHeader: "입찰유형" }, + }, - return isUrgent ? ( -
- - - 긴급 - -
- ) : ( -
- - 일반 -
- ) - }, - size: 90, - meta: { excelHeader: "긴급여부" }, + // ░░░ 통화 ░░░ + { + accessorKey: "currency", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.currency} + ), + size: 60, + meta: { excelHeader: "통화" }, }, - // ░░░ 사전견적 ░░░ + // ░░░ 예산 ░░░ { - id: "preQuote", - header: ({ column }) => , - cell: ({ row }) => { - const hasPreQuote = ['request_for_quotation', 'received_quotation'].includes(row.original.status) - const preQuoteDate = row.original.preQuoteDate - - return hasPreQuote ? ( -
- - {preQuoteDate && ( - - {formatDate(preQuoteDate, "KR")} - - )} -
- ) : ( - - ) - }, - size: 90, - meta: { excelHeader: "사전견적" }, + accessorKey: "budget", + header: ({ column }) => , + cell: ({ row }) => ( + + {row.original.budget} + + ), + size: 120, + meta: { excelHeader: "예산" }, }, + // ░░░ 내정가 ░░░ + { + accessorKey: "targetPrice", + header: ({ column }) => , + cell: ({ row }) => ( + + {row.original.targetPrice} + + ), + size: 120, + meta: { excelHeader: "내정가" }, + }, // ░░░ 입찰담당자 ░░░ { - accessorKey: "managerName", + accessorKey: "bidPicName", header: ({ column }) => , cell: ({ row }) => { - const name = row.original.managerName || "-"; - const managerCode = row.original.managerCode || ""; - return name === "-" ? "-" : `${name}(${managerCode})`; + const name = row.original.bidPicName || "-"; + return name; }, size: 100, meta: { excelHeader: "입찰담당자" }, }, - - // ═══════════════════════════════════════════════════════════════ - // 프로젝트 정보 - // ═══════════════════════════════════════════════════════════════ + + // ░░░ 입찰등록일 ░░░ { - header: "프로젝트 정보", - columns: [ - { - accessorKey: "projectName", - header: ({ column }) => , - cell: ({ row }) => ( -
- {row.original.projectName || '-'} -
- ), - size: 150, - meta: { excelHeader: "프로젝트명" }, - }, - - { - accessorKey: "itemName", - header: ({ column }) => , - cell: ({ row }) => ( -
- {row.original.itemName || '-'} -
- ), - size: 150, - meta: { excelHeader: "품목명" }, - }, - - { - accessorKey: "title", - header: ({ column }) => , - cell: ({ row }) => ( -
- -
- ), - size: 200, - meta: { excelHeader: "입찰명" }, - }, - ] + accessorKey: "biddingRegistrationDate", + header: ({ column }) => , + cell: ({ row }) => ( + {formatDate(row.original.biddingRegistrationDate , "KR")} + ), + size: 100, + meta: { excelHeader: "입찰등록일" }, }, - // ═══════════════════════════════════════════════════════════════ - // 계약 정보 - // ═══════════════════════════════════════════════════════════════ { - header: "계약 정보", - columns: [ - { - accessorKey: "contractType", - header: ({ column }) => , - cell: ({ row }) => ( - - {contractTypeLabels[row.original.contractType]} - - ), - size: 100, - meta: { excelHeader: "계약구분" }, - }, - - { - accessorKey: "biddingType", - header: ({ column }) => , - cell: ({ row }) => ( - - {biddingTypeLabels[row.original.biddingType]} - - ), - size: 100, - meta: { excelHeader: "입찰유형" }, - }, - - { - accessorKey: "awardCount", - header: ({ column }) => , - cell: ({ row }) => ( - - {awardCountLabels[row.original.awardCount]} - - ), - size: 80, - meta: { excelHeader: "낙찰수" }, - }, - - { - id: "contractPeriod", - header: ({ column }) => , - cell: ({ row }) => { - const startDate = row.original.contractStartDate - const endDate = row.original.contractEndDate - - if (!startDate || !endDate) { - return - - } - - return ( -
- {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")} -
- ) - }, - size: 120, - meta: { excelHeader: "계약기간" }, - }, - ] + id: "submissionPeriod", + header: ({ column }) => , + cell: ({ row }) => { + const startDate = row.original.submissionStartDate + const endDate = row.original.submissionEndDate + + if (!startDate || !endDate) return - + + const now = new Date() + const isActive = now >= new Date(startDate) && now <= new Date(endDate) + const isPast = now > new Date(endDate) + + return ( +
+
+ {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")} +
+ {isActive && ( + 진행중 + )} +
+ ) + }, + size: 140, + meta: { excelHeader: "입찰서제출기간" }, }, - - // ═══════════════════════════════════════════════════════════════ - // 일정 정보 - // ═══════════════════════════════════════════════════════════════ + // ░░░ 사양설명회 ░░░ { - header: "일정 정보", - columns: [ - { - id: "submissionPeriod", - header: ({ column }) => , - cell: ({ row }) => { - const startDate = row.original.submissionStartDate - const endDate = row.original.submissionEndDate - - if (!startDate || !endDate) return - - - const now = new Date() - const isActive = now >= new Date(startDate) && now <= new Date(endDate) - const isPast = now > new Date(endDate) - - return ( -
-
- {formatDate(startDate, "KR")} ~ {formatDate(endDate, "KR")} -
- {isActive && ( - 진행중 - )} -
- ) - }, - size: 140, - meta: { excelHeader: "입찰서제출기간" }, - }, - - { - accessorKey: "hasSpecificationMeeting", - header: ({ column }) => , - cell: ({ row }) => { - const hasMeeting = row.original.hasSpecificationMeeting - - return ( - - ) - }, - size: 100, - meta: { excelHeader: "사양설명회" }, - }, - ] + accessorKey: "hasSpecificationMeeting", + header: ({ column }) => , + cell: ({ row }) => { + const hasMeeting = row.original.hasSpecificationMeeting + + return ( + + ) + }, + size: 100, + meta: { excelHeader: "사양설명회" }, }, - // ═══════════════════════════════════════════════════════════════ - // 가격 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "가격 정보", - columns: [ - { - accessorKey: "currency", - header: ({ column }) => , - cell: ({ row }) => ( - {row.original.currency} - ), - size: 60, - meta: { excelHeader: "통화" }, - }, + // ░░░ 등록자 ░░░ - { - accessorKey: "budget", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.budget} - - ), - size: 120, - meta: { excelHeader: "예산" }, - }, - - { - accessorKey: "targetPrice", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.targetPrice} - - ), - size: 120, - meta: { excelHeader: "내정가" }, - }, - - { - accessorKey: "finalBidPrice", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.finalBidPrice} - - ), - size: 120, - meta: { excelHeader: "최종입찰가" }, - }, - ] + { + accessorKey: "updatedBy", + header: ({ column }) => , + cell: ({ row }) => ( + {row.original.updatedBy || '-'} + ), + size: 100, + meta: { excelHeader: "등록자" }, }, - - // ═══════════════════════════════════════════════════════════════ - // 참여 현황 - // ═══════════════════════════════════════════════════════════════ + // 등록일시 { - header: "참여 현황", - columns: [ - { - id: "participantExpected", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.participantExpected} - - ), - size: 80, - meta: { excelHeader: "참여예정" }, - }, - - { - id: "participantParticipated", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.participantParticipated} - - ), - size: 60, - meta: { excelHeader: "참여" }, - }, - - { - id: "participantDeclined", - header: ({ column }) => , - cell: ({ row }) => ( - - {row.original.participantDeclined} - - ), - size: 60, - meta: { excelHeader: "포기" }, - }, - ] + accessorKey: "updatedAt", + header: ({ column }) => , + cell: ({ row }) => ( + {formatDate(row.original.updatedAt, "KR")} + ), + size: 100, + meta: { excelHeader: "등록일시" }, }, - // ═══════════════════════════════════════════════════════════════ // PR 정보 // ═══════════════════════════════════════════════════════════════ - { - header: "PR 정보", - columns: [ - { - accessorKey: "prNumber", - header: ({ column }) => , - cell: ({ row }) => ( - {row.original.prNumber || '-'} - ), - size: 100, - meta: { excelHeader: "PR No." }, - }, - - { - accessorKey: "hasPrDocument", - header: ({ column }) => , - cell: ({ row }) => { - const hasPrDoc = row.original.hasPrDocument + // { + // header: "PR 정보", + // columns: [ + // { + // accessorKey: "prNumber", + // header: ({ column }) => , + // cell: ({ row }) => ( + // {row.original.prNumber || '-'} + // ), + // size: 100, + // meta: { excelHeader: "PR No." }, + // }, + + // { + // accessorKey: "hasPrDocument", + // header: ({ column }) => , + // cell: ({ row }) => { + // const hasPrDoc = row.original.hasPrDocument - return ( - - ) - }, - size: 80, - meta: { excelHeader: "PR 문서" }, - }, - ] - }, - - // ═══════════════════════════════════════════════════════════════ - // 메타 정보 - // ═══════════════════════════════════════════════════════════════ - { - header: "메타 정보", - columns: [ - { - accessorKey: "preQuoteDate", - header: ({ column }) => , - cell: ({ row }) => ( - {formatDate(row.original.preQuoteDate, "KR")} - ), - size: 90, - meta: { excelHeader: "사전견적일" }, - }, - - { - accessorKey: "biddingRegistrationDate", - header: ({ column }) => , - cell: ({ row }) => ( - {formatDate(row.original.biddingRegistrationDate , "KR")} - ), - size: 100, - meta: { excelHeader: "입찰등록일" }, - }, - - { - accessorKey: "updatedAt", - header: ({ column }) => , - cell: ({ row }) => ( - {formatDate(row.original.updatedAt, "KR")} - ), - size: 100, - meta: { excelHeader: "최종수정일" }, - }, - - { - accessorKey: "updatedBy", - header: ({ column }) => , - cell: ({ row }) => ( - {row.original.updatedBy || '-'} - ), - size: 100, - meta: { excelHeader: "최종수정자" }, - }, - ] - }, + // return ( + // + // ) + // }, + // size: 80, + // meta: { excelHeader: "PR 문서" }, + // }, + // ] + // }, // ░░░ 비고 ░░░ { @@ -611,6 +399,17 @@ export function getBiddingsColumns({ setRowAction }: GetColumnsProps): ColumnDef (수정 불가) )} + + setRowAction({ row, type: "bid_closure" })} + disabled={row.original.status !== 'bidding_disposal'} + > + + 폐찰하기 + {row.original.status !== 'bidding_disposal' && ( + (유찰 시에만 가능) + )} + {/* setRowAction({ row, type: "copy" })}> -- cgit v1.2.3