diff options
Diffstat (limited to 'lib/bidding/pre-quote/table')
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({ |
