summaryrefslogtreecommitdiff
path: root/lib/bidding/vendor/partners-bidding-list-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/vendor/partners-bidding-list-columns.tsx')
-rw-r--r--lib/bidding/vendor/partners-bidding-list-columns.tsx260
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>
+ ),
+ }),
+ ]
+}