diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-27 12:06:26 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-27 12:06:26 +0000 |
| commit | 7548e2ad6948f1c6aa102fcac408bc6c9c0f9796 (patch) | |
| tree | 8e66703ec821888ad51dcc242a508813a027bf71 /lib/bidding/vendor/partners-bidding-list-columns.tsx | |
| parent | 7eac558470ef179dad626a8e82db5784fe86a556 (diff) | |
(대표님, 최겸) 기본계약, 입찰, 파일라우트, 계약서명라우트, 인포메이션, 메뉴설정, PQ(메일템플릿 관련)
Diffstat (limited to 'lib/bidding/vendor/partners-bidding-list-columns.tsx')
| -rw-r--r-- | lib/bidding/vendor/partners-bidding-list-columns.tsx | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/lib/bidding/vendor/partners-bidding-list-columns.tsx b/lib/bidding/vendor/partners-bidding-list-columns.tsx new file mode 100644 index 00000000..b54ca967 --- /dev/null +++ b/lib/bidding/vendor/partners-bidding-list-columns.tsx @@ -0,0 +1,260 @@ +'use client' + +import * as React from 'react' +import { createColumnHelper } from '@tanstack/react-table' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { + CheckCircle, + XCircle, + Users, + Eye, + MoreHorizontal, + Calendar, + User +} from 'lucide-react' +import { formatDate } from '@/lib/utils' +import { biddingStatusLabels, contractTypeLabels } from '@/db/schema' +import { PartnersBiddingListItem } from '../detail/service' + +const columnHelper = createColumnHelper<PartnersBiddingListItem>() + +interface PartnersBiddingListColumnsProps { + setRowAction?: (action: { type: string; row: { original: PartnersBiddingListItem } }) => void +} + +export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingListColumnsProps = {}) { + return [ + // 입찰 No. + columnHelper.accessor('biddingNumber', { + header: '입찰 No.', + cell: ({ row }) => { + const biddingNumber = row.original.biddingNumber + const revision = row.original.revision + return ( + <div className="font-mono text-sm"> + <div>{biddingNumber}</div> + {revision > 0 && ( + <div className="text-muted-foreground">Rev.{revision}</div> + )} + </div> + ) + }, + }), + + // 입찰상태 + columnHelper.accessor('status', { + header: '입찰상태', + cell: ({ row }) => { + const status = row.original.status + return ( + <Badge variant={ + status === 'bidding_disposal' ? 'destructive' : + status === 'vendor_selected' ? 'default' : + status === 'bidding_generated' ? 'secondary' : + 'outline' + }> + {biddingStatusLabels[status] || status} + </Badge> + ) + }, + }), + + // 상세 (액션 버튼) + columnHelper.display({ + id: 'actions', + header: '상세', + cell: ({ row }) => { + const handleView = () => { + if (setRowAction) { + setRowAction({ + type: 'view', + row: { original: row.original } + }) + } + } + + return ( + <Button + variant="outline" + size="sm" + onClick={handleView} + className="h-8 w-8 p-0" + > + <Eye className="h-4 w-4" /> + </Button> + ) + }, + }), + + // 품목명 + columnHelper.accessor('itemName', { + header: '품목명', + cell: ({ row }) => ( + <div className="max-w-32 truncate" title={row.original.itemName}> + {row.original.itemName} + </div> + ), + }), + + // 입찰명 + columnHelper.accessor('title', { + header: '입찰명', + cell: ({ row }) => ( + <div className="max-w-48 truncate" title={row.original.title}> + {row.original.title} + </div> + ), + }), + + // 사양설명회 + columnHelper.accessor('isAttendingMeeting', { + header: '사양설명회', + cell: ({ row }) => { + const isAttending = row.original.isAttendingMeeting + if (isAttending === null) { + return <div className="text-muted-foreground text-center">-</div> + } + return isAttending ? ( + <CheckCircle className="h-5 w-5 text-green-600 mx-auto" /> + ) : ( + <XCircle className="h-5 w-5 text-red-600 mx-auto" /> + ) + }, + }), + + // 입찰 참여의사 + columnHelper.accessor('invitationStatus', { + header: '입찰 참여의사', + cell: ({ row }) => { + const status = row.original.invitationStatus + const statusLabels = { + sent: '초대됨', + submitted: '참여', + declined: '불참', + pending: '대기중' + } + return ( + <Badge variant={ + status === 'submitted' ? 'default' : + status === 'declined' ? 'destructive' : + status === 'sent' ? 'secondary' : + 'outline' + }> + {statusLabels[status as keyof typeof statusLabels] || status} + </Badge> + ) + }, + }), + + // 계약구분 + columnHelper.accessor('contractType', { + header: '계약구분', + cell: ({ row }) => ( + <div>{contractTypeLabels[row.original.contractType] || row.original.contractType}</div> + ), + }), + + // 입찰기간 + columnHelper.accessor('submissionStartDate', { + header: '입찰기간', + cell: ({ row }) => { + const startDate = row.original.submissionStartDate + const endDate = row.original.submissionEndDate + if (!startDate || !endDate) { + return <div className="text-muted-foreground">-</div> + } + return ( + <div className="text-sm"> + <div>{formatDate(startDate, 'KR')}</div> + <div className="text-muted-foreground">~</div> + <div>{formatDate(endDate, 'KR')}</div> + </div> + ) + }, + }), + + // 계약기간 + columnHelper.accessor('contractPeriod', { + header: '계약기간', + cell: ({ row }) => ( + <div className="max-w-24 truncate" title={row.original.contractPeriod || ''}> + {row.original.contractPeriod || '-'} + </div> + ), + }), + + // 참여회신 마감일 + columnHelper.accessor('responseDeadline', { + header: '참여회신 마감일', + cell: ({ row }) => { + const deadline = row.original.responseDeadline + if (!deadline) { + return <div className="text-muted-foreground">-</div> + } + return <div className="text-sm">{formatDate(deadline, 'KR')}</div> + }, + }), + + // 입찰제출일 + columnHelper.accessor('submissionDate', { + header: '입찰제출일', + cell: ({ row }) => { + const date = row.original.submissionDate + if (!date) { + return <div className="text-muted-foreground">-</div> + } + return <div className="text-sm">{formatDate(date, 'KR')}</div> + }, + }), + + // 입찰담당자 + columnHelper.accessor('managerName', { + header: '입찰담당자', + cell: ({ row }) => { + const name = row.original.managerName + const email = row.original.managerEmail + if (!name) { + return <div className="text-muted-foreground">-</div> + } + return ( + <div className="flex items-center gap-1"> + <User className="h-4 w-4" /> + <div> + <div className="text-sm">{name}</div> + {email && ( + <div className="text-xs text-muted-foreground truncate max-w-32" title={email}> + {email} + </div> + )} + </div> + </div> + ) + }, + }), + + // 최종수정일 + columnHelper.accessor('updatedAt', { + header: '최종수정일', + cell: ({ row }) => ( + <div className="text-sm">{formatDate(row.original.updatedAt, 'KR')}</div> + ), + }), + + // 최종수정자 + columnHelper.accessor('updatedBy', { + header: '최종수정자', + cell: ({ row }) => ( + <div className="max-w-20 truncate" title={row.original.updatedBy || ''}> + {row.original.updatedBy || '-'} + </div> + ), + }), + ] +} |
