summaryrefslogtreecommitdiff
path: root/lib/bidding/pre-quote/table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bidding/pre-quote/table')
-rw-r--r--lib/bidding/pre-quote/table/bidding-pre-quote-content.tsx8
-rw-r--r--lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx30
-rw-r--r--lib/bidding/pre-quote/table/bidding-pre-quote-vendor-columns.tsx86
-rw-r--r--lib/bidding/pre-quote/table/bidding-pre-quote-vendor-table.tsx17
4 files changed, 96 insertions, 45 deletions
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-content.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-content.tsx
index 692d12ea..91b80bd3 100644
--- a/lib/bidding/pre-quote/table/bidding-pre-quote-content.tsx
+++ b/lib/bidding/pre-quote/table/bidding-pre-quote-content.tsx
@@ -2,7 +2,7 @@
import * as React from 'react'
import { Bidding } from '@/db/schema'
-import { QuotationDetails, QuotationVendor } from '@/lib/bidding/detail/service'
+import { QuotationDetails } from '@/lib/bidding/detail/service'
import { getBiddingCompanies } from '../service'
import { BiddingPreQuoteVendorTableContent } from './bidding-pre-quote-vendor-table'
@@ -10,7 +10,6 @@ import { BiddingPreQuoteVendorTableContent } from './bidding-pre-quote-vendor-ta
interface BiddingPreQuoteContentProps {
bidding: Bidding
quotationDetails: QuotationDetails | null
- quotationVendors: QuotationVendor[]
biddingCompanies: any[]
prItems: any[]
}
@@ -18,7 +17,6 @@ interface BiddingPreQuoteContentProps {
export function BiddingPreQuoteContent({
bidding,
quotationDetails,
- quotationVendors,
biddingCompanies: initialBiddingCompanies,
prItems
}: BiddingPreQuoteContentProps) {
@@ -42,15 +40,11 @@ export function BiddingPreQuoteContent({
<BiddingPreQuoteVendorTableContent
biddingId={bidding.id}
bidding={bidding}
- vendors={quotationVendors}
biddingCompanies={biddingCompanies}
onRefresh={handleRefresh}
onOpenItemsDialog={() => {}}
onOpenTargetPriceDialog={() => {}}
onOpenSelectionReasonDialog={() => {}}
- onEdit={undefined}
- onDelete={undefined}
- onSelectWinner={undefined}
/>
</div>
)
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx
index 84824c1e..1b0598b7 100644
--- a/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx
+++ b/lib/bidding/pre-quote/table/bidding-pre-quote-invitation-dialog.tsx
@@ -3,6 +3,8 @@
import * as React from 'react'
import { Button } from '@/components/ui/button'
import { Checkbox } from '@/components/ui/checkbox'
+import { Input } from '@/components/ui/input'
+import { Label } from '@/components/ui/label'
import {
Dialog,
DialogContent,
@@ -16,7 +18,7 @@ import { BiddingCompany } from './bidding-pre-quote-vendor-columns'
import { sendPreQuoteInvitations } from '../service'
import { useToast } from '@/hooks/use-toast'
import { useTransition } from 'react'
-import { Mail, Building2 } from 'lucide-react'
+import { Mail, Building2, Calendar } from 'lucide-react'
interface BiddingPreQuoteInvitationDialogProps {
open: boolean
@@ -34,6 +36,7 @@ export function BiddingPreQuoteInvitationDialog({
const { toast } = useToast()
const [isPending, startTransition] = useTransition()
const [selectedCompanyIds, setSelectedCompanyIds] = React.useState<number[]>([])
+ const [preQuoteDeadline, setPreQuoteDeadline] = React.useState('')
// 초대 가능한 업체들 (pending 상태인 업체들)
const invitableCompanies = companies.filter(company =>
@@ -67,7 +70,10 @@ export function BiddingPreQuoteInvitationDialog({
}
startTransition(async () => {
- const response = await sendPreQuoteInvitations(selectedCompanyIds)
+ const response = await sendPreQuoteInvitations(
+ selectedCompanyIds,
+ preQuoteDeadline || undefined
+ )
if (response.success) {
toast({
@@ -75,6 +81,7 @@ export function BiddingPreQuoteInvitationDialog({
description: response.message,
})
setSelectedCompanyIds([])
+ setPreQuoteDeadline('')
onOpenChange(false)
onSuccess()
} else {
@@ -91,6 +98,7 @@ export function BiddingPreQuoteInvitationDialog({
onOpenChange(open)
if (!open) {
setSelectedCompanyIds([])
+ setPreQuoteDeadline('')
}
}
@@ -114,6 +122,24 @@ export function BiddingPreQuoteInvitationDialog({
</div>
) : (
<>
+ {/* 견적마감일 설정 */}
+ <div className="mb-6 p-4 border rounded-lg bg-muted/30">
+ <Label htmlFor="preQuoteDeadline" className="text-sm font-medium mb-2 flex items-center gap-2">
+ <Calendar className="w-4 h-4" />
+ 견적 마감일 (선택사항)
+ </Label>
+ <Input
+ id="preQuoteDeadline"
+ type="datetime-local"
+ value={preQuoteDeadline}
+ onChange={(e) => setPreQuoteDeadline(e.target.value)}
+ className="w-full"
+ />
+ <p className="text-xs text-muted-foreground mt-1">
+ 설정하지 않으면 마감일 없이 초대가 발송됩니다.
+ </p>
+ </div>
+
{/* 전체 선택 */}
<div className="flex items-center space-x-2 mb-4 pb-2 border-b">
<Checkbox
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-columns.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-columns.tsx
index 7e84f178..5c6f41ce 100644
--- a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-columns.tsx
+++ b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-columns.tsx
@@ -6,7 +6,7 @@ import { Checkbox } from "@/components/ui/checkbox"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
- MoreHorizontal, Edit, Trash2, UserPlus, Paperclip
+ MoreHorizontal, Edit, Trash2, Paperclip
} from "lucide-react"
import {
DropdownMenu,
@@ -27,7 +27,9 @@ export interface BiddingCompany {
respondedAt: Date | null
preQuoteAmount: string | null
preQuoteSubmittedAt: Date | null
+ preQuoteDeadline: Date | null
isPreQuoteSelected: boolean
+ isPreQuoteParticipated: boolean | null
isAttendingMeeting: boolean | null
notes: string | null
contactPerson: string | null
@@ -56,7 +58,6 @@ export interface BiddingCompany {
interface GetBiddingCompanyColumnsProps {
onEdit: (company: BiddingCompany) => void
onDelete: (company: BiddingCompany) => void
- onInvite: (company: BiddingCompany) => void
onViewPriceAdjustment?: (company: BiddingCompany) => void
onViewItemDetails?: (company: BiddingCompany) => void
onViewAttachments?: (company: BiddingCompany) => void
@@ -65,7 +66,6 @@ interface GetBiddingCompanyColumnsProps {
export function getBiddingPreQuoteVendorColumns({
onEdit,
onDelete,
- onInvite,
onViewPriceAdjustment,
onViewItemDetails,
onViewAttachments
@@ -109,11 +109,28 @@ export function getBiddingPreQuoteVendorColumns({
header: '초대 상태',
cell: ({ row }) => {
const status = row.original.invitationStatus
- const variant = status === 'accepted' ? 'default' :
- status === 'declined' ? 'destructive' : 'outline'
+ let variant: any
+ let label: string
- const label = status === 'accepted' ? '수락' :
- status === 'declined' ? '거절' : '대기중'
+ if (status === 'accepted') {
+ variant = 'default'
+ label = '수락'
+ } else if (status === 'declined') {
+ variant = 'destructive'
+ label = '거절'
+ } else if (status === 'pending') {
+ variant = 'outline'
+ label = '대기중'
+ } else if (status === 'sent') {
+ variant = 'outline'
+ label = '요청됨'
+ } else if (status === 'submitted') {
+ variant = 'outline'
+ label = '제출됨'
+ } else {
+ variant = 'outline'
+ label = status || '-'
+ }
return <Badge variant={variant}>{label}</Badge>
},
@@ -150,6 +167,31 @@ export function getBiddingPreQuoteVendorColumns({
),
},
{
+ accessorKey: 'preQuoteDeadline',
+ header: '사전견적 마감일',
+ cell: ({ row }) => {
+ const deadline = row.original.preQuoteDeadline
+ if (!deadline) {
+ return <div className="text-muted-foreground text-sm">-</div>
+ }
+
+ const now = new Date()
+ const deadlineDate = new Date(deadline)
+ const isExpired = deadlineDate < now
+
+ return (
+ <div className={`text-sm ${isExpired ? 'text-red-600' : ''}`}>
+ <div>{deadlineDate.toLocaleDateString('ko-KR')}</div>
+ {isExpired && (
+ <Badge variant="destructive" className="text-xs mt-1">
+ 마감
+ </Badge>
+ )}
+ </div>
+ )
+ },
+ },
+ {
accessorKey: 'attachments',
header: '첨부파일',
cell: ({ row }) => {
@@ -174,6 +216,21 @@ export function getBiddingPreQuoteVendorColumns({
},
},
{
+ accessorKey: 'isPreQuoteParticipated',
+ header: '사전견적 참여의사',
+ cell: ({ row }) => {
+ const participated = row.original.isPreQuoteParticipated
+ if (participated === null) {
+ return <Badge variant="outline">미결정</Badge>
+ }
+ return (
+ <Badge variant={participated ? 'default' : 'destructive'}>
+ {participated ? '참여' : '미참여'}
+ </Badge>
+ )
+ },
+ },
+ {
accessorKey: 'isPreQuoteSelected',
header: '본입찰 선정',
cell: ({ row }) => (
@@ -307,15 +364,6 @@ export function getBiddingPreQuoteVendorColumns({
),
},
{
- accessorKey: 'notes',
- header: '특이사항',
- cell: ({ row }) => (
- <div className="text-sm max-w-32 truncate" title={row.original.notes || ''}>
- {row.original.notes || '-'}
- </div>
- ),
- },
- {
id: 'actions',
header: '액션',
cell: ({ row }) => {
@@ -334,12 +382,6 @@ export function getBiddingPreQuoteVendorColumns({
<Edit className="mr-2 h-4 w-4" />
수정
</DropdownMenuItem> */}
- {company.invitationStatus === 'pending' && (
- <DropdownMenuItem onClick={() => onInvite(company)}>
- <UserPlus className="mr-2 h-4 w-4" />
- 초대 발송
- </DropdownMenuItem>
- )}
<DropdownMenuItem
onClick={() => onDelete(company)}
className="text-destructive"
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-table.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-table.tsx
index a1319821..7ea05721 100644
--- a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-table.tsx
+++ b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-table.tsx
@@ -23,7 +23,6 @@ import { getPrItemsForBidding } from '../service'
interface BiddingPreQuoteVendorTableContentProps {
biddingId: number
bidding: Bidding
- vendors: any[] // 사용하지 않음
biddingCompanies: BiddingCompany[]
onRefresh: () => void
onOpenItemsDialog: () => void
@@ -31,7 +30,6 @@ interface BiddingPreQuoteVendorTableContentProps {
onOpenSelectionReasonDialog: () => void
onEdit?: (company: BiddingCompany) => void
onDelete?: (company: BiddingCompany) => void
- onSelectWinner?: (company: BiddingCompany) => void
}
const filterFields: DataTableFilterField<BiddingCompany>[] = [
@@ -80,6 +78,7 @@ const advancedFilterFields: DataTableAdvancedFilterField<BiddingCompany>[] = [
options: [
{ label: '수락', value: 'accepted' },
{ label: '거절', value: 'declined' },
+ { label: '요청됨', value: 'sent' },
{ label: '대기중', value: 'pending' },
],
},
@@ -88,15 +87,13 @@ const advancedFilterFields: DataTableAdvancedFilterField<BiddingCompany>[] = [
export function BiddingPreQuoteVendorTableContent({
biddingId,
bidding,
- vendors,
biddingCompanies,
onRefresh,
onOpenItemsDialog,
onOpenTargetPriceDialog,
onOpenSelectionReasonDialog,
onEdit,
- onDelete,
- onSelectWinner
+ onDelete
}: BiddingPreQuoteVendorTableContentProps) {
const { toast } = useToast()
const [isPending, startTransition] = useTransition()
@@ -137,13 +134,6 @@ export function BiddingPreQuoteVendorTableContent({
setIsEditDialogOpen(true)
}
- const handleInvite = (company: BiddingCompany) => {
- // TODO: 초대 발송 로직 구현
- toast({
- title: '알림',
- description: `${company.companyName} 업체에 초대를 발송했습니다.`,
- })
- }
const handleViewPriceAdjustment = async (company: BiddingCompany) => {
startTransition(async () => {
@@ -190,12 +180,11 @@ export function BiddingPreQuoteVendorTableContent({
() => getBiddingPreQuoteVendorColumns({
onEdit: onEdit || handleEdit,
onDelete: onDelete || handleDelete,
- onInvite: handleInvite,
onViewPriceAdjustment: handleViewPriceAdjustment,
onViewItemDetails: handleViewItemDetails,
onViewAttachments: handleViewAttachments
}),
- [onEdit, onDelete, handleEdit, handleDelete, handleInvite, handleViewPriceAdjustment, handleViewItemDetails, handleViewAttachments]
+ [onEdit, onDelete, handleEdit, handleDelete, handleViewPriceAdjustment, handleViewItemDetails, handleViewAttachments]
)
const { table } = useDataTable({