From 9cda8482660a87fd98c9ee43f507d75ff75b4e23 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 28 Nov 2025 03:12:57 +0000 Subject: (최겸) 구매 입찰 피드백 반영(90%) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bidding/create/bidding-create-dialog.tsx | 451 +++++++++++++------- .../bidding/manage/bidding-basic-info-editor.tsx | 472 ++++++++++++++------- .../bidding/manage/bidding-companies-editor.tsx | 8 +- components/bidding/manage/bidding-items-editor.tsx | 35 +- .../bidding/manage/bidding-schedule-editor.tsx | 21 + .../receive/bidding-participants-dialog.tsx | 216 ++++++++++ db/schema/bidding.ts | 6 +- lib/bidding/detail/service.ts | 76 +++- lib/bidding/list/biddings-table-columns.tsx | 20 +- lib/bidding/list/edit-bidding-sheet.tsx | 14 +- lib/bidding/receive/biddings-receive-columns.tsx | 76 ++-- lib/bidding/receive/biddings-receive-table.tsx | 53 ++- .../selection/biddings-selection-columns.tsx | 10 +- lib/bidding/service.ts | 1 - .../vendor/partners-bidding-list-columns.tsx | 11 +- 15 files changed, 1107 insertions(+), 363 deletions(-) create mode 100644 components/bidding/receive/bidding-participants-dialog.tsx diff --git a/components/bidding/create/bidding-create-dialog.tsx b/components/bidding/create/bidding-create-dialog.tsx index f298721b..bb7880f5 100644 --- a/components/bidding/create/bidding-create-dialog.tsx +++ b/components/bidding/create/bidding-create-dialog.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { UseFormReturn } from 'react-hook-form' -import { ChevronRight, Upload, FileText, Eye, User, Building, Calendar, DollarSign, Plus } from 'lucide-react' +import { ChevronRight, Upload, FileText, Eye, User, Building, Calendar, DollarSign, Plus, Check, ChevronsUpDown } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' @@ -26,6 +26,20 @@ import { Textarea } from '@/components/ui/textarea' import { Switch } from '@/components/ui/switch' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command' +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover' +import { cn } from '@/lib/utils' import type { CreateBiddingSchema } from '@/lib/bidding/validation' import { contractTypeLabels, biddingTypeLabels, awardCountLabels, biddingNoticeTypeLabels } from '@/db/schema' @@ -589,37 +603,62 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp control={form.control} name="biddingConditions.paymentTerms" render={({ field }) => ( - + SHI 지급조건 * - - - + + + + + + + + + + + 검색 결과가 없습니다. + + {paymentTermsOptions.map((option) => ( + { + setBiddingConditions(prev => ({ + ...prev, + paymentTerms: option.code + })) + field.onChange(option.code) + }} + > + + {option.code} {option.description && `(${option.description})`} + + ))} + + + + + )} @@ -632,37 +671,62 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp control={form.control} name="biddingConditions.incoterms" render={({ field }) => ( - + SHI 인도조건 * - + + + + + + + + + + + 검색 결과가 없습니다. + + {incotermsOptions.map((option) => ( + { + setBiddingConditions(prev => ({ + ...prev, + incoterms: option.code + })) + field.onChange(option.code) + }} + > + + {option.code} {option.description && `(${option.description})`} + + ))} + + + + + )} @@ -699,31 +763,60 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp control={form.control} name="biddingConditions.taxConditions" render={({ field }) => ( - + SHI 매입부가가치세 * - - - + + + + + + + + + + + 검색 결과가 없습니다. + + {TAX_CONDITIONS.map((condition) => ( + { + setBiddingConditions(prev => ({ + ...prev, + taxConditions: condition.code + })) + field.onChange(condition.code) + }} + > + + {condition.name} + + ))} + + + + + )} @@ -733,37 +826,62 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp control={form.control} name="biddingConditions.shippingPort" render={({ field }) => ( - + SHI 선적지 - + + + + + + + + + + + 검색 결과가 없습니다. + + {shippingPlaces.map((place) => ( + { + setBiddingConditions(prev => ({ + ...prev, + shippingPort: place.code + })) + field.onChange(place.code) + }} + > + + {place.code} {place.description && `(${place.description})`} + + ))} + + + + + )} @@ -776,43 +894,68 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp control={form.control} name="biddingConditions.destinationPort" render={({ field }) => ( - + SHI 하역지 - + + + + + + + + + + + 검색 결과가 없습니다. + + {destinationPlaces.map((place) => ( + { + setBiddingConditions(prev => ({ + ...prev, + destinationPort: place.code + })) + field.onChange(place.code) + }} + > + + {place.code} {place.description && `(${place.description})`} + + ))} + + + + + )} /> - {/* ( @@ -829,6 +972,8 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp })) field.onChange(e.target.value) }} + min="1900-01-01" + max="2100-12-31" /> @@ -849,7 +994,12 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp 계약기간 시작 - + @@ -866,7 +1016,12 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp 계약기간 종료 - + diff --git a/components/bidding/manage/bidding-basic-info-editor.tsx b/components/bidding/manage/bidding-basic-info-editor.tsx index 90923825..27a2c097 100644 --- a/components/bidding/manage/bidding-basic-info-editor.tsx +++ b/components/bidding/manage/bidding-basic-info-editor.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useForm } from 'react-hook-form' -import { ChevronRight, Upload, FileText, Eye, User, Building, Calendar, DollarSign } from 'lucide-react' +import { ChevronRight, Upload, FileText, Eye, User, Building, Calendar, DollarSign, Check, ChevronsUpDown } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' @@ -25,6 +25,20 @@ import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Switch } from '@/components/ui/switch' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from '@/components/ui/command' +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover' +import { cn } from '@/lib/utils' // CreateBiddingInput 타입 정의가 없으므로 CreateBiddingSchema를 확장하여 사용합니다. import { getBiddingById, updateBiddingBasicInfo, getBiddingConditions, getBiddingNotice, updateBiddingConditions, getBiddingNoticeTemplate } from '@/lib/bidding/service' import { getPurchaseGroupCodes } from '@/components/common/selectors/purchase-group-code' @@ -270,7 +284,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB } // Procurement 데이터 로드 - const [paymentTermsData, incotermsData, shippingData, destinationData, purchaseGroupCodes, procurementManagers] = await Promise.all([ + const [paymentTermsData, incotermsData, shippingData, destinationData] = await Promise.all([ getPaymentTermsForSelection().catch(() => []), getIncotermsForSelection().catch(() => []), getPlaceOfShippingForSelection().catch(() => []), @@ -284,14 +298,20 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB DISPLAY_NAME: bidding.bidPicName || '', PURCHASE_GROUP_CODE: bidding.bidPicCode || '', user: { - id: bidding.bidPicUserId || undefined, + id: bidding.bidPicId || undefined, + name: bidding.bidPicName || '', + email: '', + employeeNumber: null, } }) setSelectedSupplyPic({ DISPLAY_NAME: bidding.supplyPicName || '', PROCUREMENT_MANAGER_CODE: bidding.supplyPicCode || '', user: { - id: bidding.supplyPicUserId || undefined, + id: bidding.supplyPicId || undefined, + name: bidding.supplyPicName || '', + email: '', + employeeNumber: null, } }) // // 입찰담당자 및 조달담당자 초기 선택값 설정 @@ -554,7 +574,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB ( 입찰유형 - @@ -575,7 +595,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB ( 계약구분 - @@ -603,7 +623,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB 기타 입찰유형 * - + @@ -656,7 +676,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB ( 낙찰업체 수 - @@ -691,6 +711,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB field.onChange(code.DISPLAY_NAME || '') }} placeholder="입찰담당자 선택" + disabled={readonly} /> @@ -711,6 +732,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB field.onChange(manager.DISPLAY_NAME || '') }} placeholder="조달담당자 선택" + disabled={readonly} /> @@ -723,7 +745,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB 구매조직 * - @@ -747,7 +769,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB ( 통화 - @@ -770,7 +792,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB ( 구매유형 * - @@ -801,7 +823,13 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB 계약기간 시작 - + @@ -814,7 +842,13 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB 계약기간 종료 - + @@ -853,91 +887,173 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB {/* 1행: SHI 지급조건, SHI 매입부가가치세 */}
-
+
SHI 지급조건 * - + + + + + + + + + 검색 결과가 없습니다. + + {paymentTermsOptions.map((option) => ( + { + setBiddingConditions(prev => ({ + ...prev, + paymentTerms: option.code + })) + }} + > + + {option.code} {option.description && `(${option.description})`} + + ))} + + + + +
-
+
SHI 매입부가가치세 * - + + + + + + + + + 검색 결과가 없습니다. + + {TAX_CONDITIONS.map((condition) => ( + { + setBiddingConditions(prev => ({ + ...prev, + taxConditions: condition.code + })) + }} + > + + {condition.name} + + ))} + + + + +
{/* 2행: SHI 인도조건, SHI 인도조건2 */}
-
+
SHI 인도조건 * - + + + + + + + + + 검색 결과가 없습니다. + + {incotermsOptions.map((option) => ( + { + setBiddingConditions(prev => ({ + ...prev, + incoterms: option.code + })) + }} + > + + {option.code} {option.description && `(${option.description})`} + + ))} + + + + +
@@ -951,70 +1067,123 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB incotermsOption: e.target.value })) }} + disabled={readonly} />
{/* 3행: SHI 선적지, SHI 하역지 */}
-
+
SHI 선적지 - + + + + + + + + + 검색 결과가 없습니다. + + {shippingPlaces.map((place) => ( + { + setBiddingConditions(prev => ({ + ...prev, + shippingPort: place.code + })) + }} + > + + {place.code} {place.description && `(${place.description})`} + + ))} + + + + +
-
+
SHI 하역지 - + + + + + + + + + 검색 결과가 없습니다. + + {destinationPlaces.map((place) => ( + { + setBiddingConditions(prev => ({ + ...prev, + destinationPort: place.code + })) + }} + > + + {place.code} {place.description && `(${place.description})`} + + ))} + + + + +
@@ -1045,6 +1214,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB })) }} id="price-adjustment" + disabled={readonly} /> {biddingConditions.isPriceAdjustmentApplicable ? "적용" : "미적용"} @@ -1067,7 +1237,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB })) }} rows={3} - readOnly={readonly} + disabled={readonly} />
@@ -1135,15 +1305,16 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB }} onDropRejected={() => { toast({ - title: "File upload rejected", - description: "Please check file size and type.", + title: "파일 업로드 거부", + description: "파일 크기와 유형을 확인해주세요.", variant: "destructive", }) }} + disabled={readonly} > - + 파일을 드래그하거나 클릭하여 업로드 @@ -1194,6 +1365,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB variant="ghost" size="sm" onClick={() => handleDeleteDocument(doc.id)} + disabled={readonly} > 삭제 @@ -1227,15 +1399,16 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB }} onDropRejected={() => { toast({ - title: "File upload rejected", - description: "Please check file size and type.", + title: "파일 업로드 거부", + description: "파일 크기와 유형을 확인해주세요.", variant: "destructive", }) }} + disabled={readonly} > - + 파일을 드래그하거나 클릭하여 업로드 @@ -1281,6 +1454,7 @@ export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingB variant="ghost" size="sm" onClick={() => handleDeleteDocument(doc.id)} + disabled={readonly} > 삭제 diff --git a/components/bidding/manage/bidding-companies-editor.tsx b/components/bidding/manage/bidding-companies-editor.tsx index f6b3a3f0..6634f528 100644 --- a/components/bidding/manage/bidding-companies-editor.tsx +++ b/components/bidding/manage/bidding-companies-editor.tsx @@ -494,7 +494,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC

{!readonly && ( - @@ -532,6 +532,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC handleVendorSelect(vendor)} + disabled={readonly} /> {vendor.vendorName} @@ -565,6 +566,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC onCheckedChange={(checked) => handleTogglePriceAdjustmentQuestion(vendor.id, checked as boolean) } + disabled={readonly} /> {vendor.isPriceAdjustmentApplicableQuestion ? '예' : '아니오'} @@ -577,6 +579,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC size="sm" onClick={() => handleRemoveVendor(vendor.id)} className="text-red-600 hover:text-red-800" + disabled={readonly} > @@ -607,6 +610,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC variant="outline" onClick={handleOpenAddContactFromVendor} className="flex items-center gap-2" + disabled={readonly} > 업체 담당자 추가 @@ -614,6 +618,7 @@ export function BiddingCompaniesEditor({ biddingId, readonly = false }: BiddingC diff --git a/components/bidding/manage/bidding-items-editor.tsx b/components/bidding/manage/bidding-items-editor.tsx index ef0aa568..9d858f40 100644 --- a/components/bidding/manage/bidding-items-editor.tsx +++ b/components/bidding/manage/bidding-items-editor.tsx @@ -807,7 +807,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems setRepresentativeItem(item.id)} - disabled={items.length <= 1 && item.isRepresentative} + disabled={(items.length <= 1 && item.isRepresentative) || readonly} title="대표 아이템" /> @@ -831,6 +831,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems } }} placeholder="프로젝트 선택" + disabled={readonly} /> @@ -942,21 +943,25 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems updatePRItem(item.id, { quantity: e.target.value })} className="h-8 text-xs" required + disabled={readonly} /> ) : ( updatePRItem(item.id, { totalWeight: e.target.value })} className="h-8 text-xs" required + disabled={readonly} /> )} @@ -966,6 +971,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems value={item.quantityUnit || 'EA'} onValueChange={(value) => updatePRItem(item.id, { quantityUnit: value })} required + disabled={readonly} > @@ -984,6 +990,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems value={item.weightUnit || 'KG'} onValueChange={(value) => updatePRItem(item.id, { weightUnit: value })} required + disabled={readonly} > @@ -1004,6 +1011,9 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems onChange={(e) => updatePRItem(item.id, { requestedDeliveryDate: e.target.value })} className="h-8 text-xs" required + disabled={readonly} + min="1900-01-01" + max="2100-12-31" /> @@ -1015,12 +1025,14 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems value={item.priceUnit || ''} onChange={(e) => updatePRItem(item.id, { priceUnit: e.target.value })} className="h-8 text-xs" + disabled={readonly} /> updatePRItem(item.id, { materialWeight: e.target.value })} className="h-8 text-xs" + disabled={readonly} /> @@ -1057,6 +1070,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems value={formatNumberWithCommas(item.targetUnitPrice)} onChange={(e) => updatePRItem(item.id, { targetUnitPrice: parseNumberFromCommas(e.target.value) })} className="h-8 text-xs" + disabled={readonly} /> @@ -1072,6 +1086,7 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems updatePRItem(item.id, { budgetCurrency: value })} + disabled={readonly} > @@ -1116,12 +1133,14 @@ export function BiddingItemsEditor({ biddingId, readonly = false }: BiddingItems value={formatNumberWithCommas(item.actualAmount)} onChange={(e) => updatePRItem(item.id, { actualAmount: parseNumberFromCommas(e.target.value) })} className="h-8 text-xs" + disabled={readonly} /> + @@ -381,7 +386,12 @@ export function EditBiddingSheet({ 계약 종료일 - + diff --git a/lib/bidding/receive/biddings-receive-columns.tsx b/lib/bidding/receive/biddings-receive-columns.tsx index 4bde849c..9650574a 100644 --- a/lib/bidding/receive/biddings-receive-columns.tsx +++ b/lib/bidding/receive/biddings-receive-columns.tsx @@ -58,6 +58,7 @@ type BiddingReceiveItem = { interface GetColumnsProps { setRowAction: React.Dispatch | null>> + onParticipantClick?: (biddingId: number, participantType: 'expected' | 'participated' | 'declined' | 'pending') => void } // 상태별 배지 색상 @@ -89,7 +90,7 @@ const formatCurrency = (amount: string | number | null, currency = 'KRW') => { }).format(numAmount) } -export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): ColumnDef[] { +export function getBiddingsReceiveColumns({ setRowAction, onParticipantClick }: GetColumnsProps): ColumnDef[] { return [ // ░░░ 선택 ░░░ @@ -195,24 +196,17 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co if (!startDate || !endDate) return - - const now = new Date() const startObj = new Date(startDate) const endObj = new Date(endDate) - - const isActive = now >= startObj && now <= endObj - const isPast = now > endObj // UI 표시용 KST 변환 const formatKst = (d: Date) => new Date(d.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') return (
-
+
{formatKst(startObj)} ~ {formatKst(endObj)}
- {isActive && ( - 진행중 - )}
) }, @@ -251,10 +245,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co id: "participantExpected", header: ({ column }) => , cell: ({ row }) => ( -
- - {row.original.participantExpected} -
+ ), size: 100, meta: { excelHeader: "참여예정협력사" }, @@ -265,10 +267,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co id: "participantParticipated", header: ({ column }) => , cell: ({ row }) => ( -
- - {row.original.participantParticipated} -
+ ), size: 100, meta: { excelHeader: "참여협력사" }, @@ -279,10 +289,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co id: "participantDeclined", header: ({ column }) => , cell: ({ row }) => ( -
- - {row.original.participantDeclined} -
+ ), size: 100, meta: { excelHeader: "포기협력사" }, @@ -293,10 +311,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co id: "participantPending", header: ({ column }) => , cell: ({ row }) => ( -
- - {row.original.participantPending} -
+ ), size: 100, meta: { excelHeader: "미제출협력사" }, diff --git a/lib/bidding/receive/biddings-receive-table.tsx b/lib/bidding/receive/biddings-receive-table.tsx index 5bda921e..2b141d5e 100644 --- a/lib/bidding/receive/biddings-receive-table.tsx +++ b/lib/bidding/receive/biddings-receive-table.tsx @@ -22,7 +22,9 @@ import { contractTypeLabels, } from "@/db/schema" // import { SpecificationMeetingDialog, PrDocumentsDialog } from "../list/bidding-detail-dialogs" -import { openBiddingAction, earlyOpenBiddingAction } from "@/lib/bidding/actions" +import { openBiddingAction } from "@/lib/bidding/actions" +import { BiddingParticipantsDialog } from "@/components/bidding/receive/bidding-participants-dialog" +import { getAllBiddingCompanies } from "@/lib/bidding/detail/service" type BiddingReceiveItem = { id: number @@ -69,17 +71,49 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { const [isCompact, setIsCompact] = React.useState(false) // const [specMeetingDialogOpen, setSpecMeetingDialogOpen] = React.useState(false) // const [prDocumentsDialogOpen, setPrDocumentsDialogOpen] = React.useState(false) - // const [selectedBidding, setSelectedBidding] = React.useState(null) + const [selectedBidding, setSelectedBidding] = React.useState(null) const [rowAction, setRowAction] = React.useState | null>(null) const [isOpeningBidding, setIsOpeningBidding] = React.useState(false) + // 협력사 다이얼로그 관련 상태 + const [participantsDialogOpen, setParticipantsDialogOpen] = React.useState(false) + const [selectedParticipantType, setSelectedParticipantType] = React.useState<'expected' | 'participated' | 'declined' | 'pending' | null>(null) + const [selectedBiddingId, setSelectedBiddingId] = React.useState(null) + const [participantCompanies, setParticipantCompanies] = React.useState([]) + const [isLoadingParticipants, setIsLoadingParticipants] = React.useState(false) + const router = useRouter() const { data: session } = useSession() + // 협력사 클릭 핸들러 + const handleParticipantClick = React.useCallback(async (biddingId: number, participantType: 'expected' | 'participated' | 'declined' | 'pending') => { + setSelectedBiddingId(biddingId) + setSelectedParticipantType(participantType) + setIsLoadingParticipants(true) + setParticipantsDialogOpen(true) + + try { + // 협력사 데이터 로드 (모든 초대된 협력사) + const companies = await getAllBiddingCompanies(biddingId) + + console.log('Loaded companies:', companies) + + // 필터링 없이 모든 데이터 그대로 표시 + // invitationStatus가 그대로 다이얼로그에 표시됨 + setParticipantCompanies(companies) + } catch (error) { + console.error('Failed to load participant companies:', error) + toast.error('협력사 목록을 불러오는데 실패했습니다.') + setParticipantCompanies([]) + } finally { + setIsLoadingParticipants(false) + } + }, []) + const columns = React.useMemo( - () => getBiddingsReceiveColumns({ setRowAction }), - [setRowAction] + () => getBiddingsReceiveColumns({ setRowAction, onParticipantClick: handleParticipantClick }), + [setRowAction, handleParticipantClick] ) // rowAction 변경 감지하여 해당 다이얼로그 열기 @@ -96,7 +130,7 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { break } } - }, [rowAction]) + }, [rowAction, router]) const filterFields: DataTableFilterField[] = [ { @@ -248,6 +282,15 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) { onOpenChange={handlePrDocumentsDialogClose} bidding={selectedBidding} /> */} + + {/* 참여 협력사 다이얼로그 */} + ) } diff --git a/lib/bidding/selection/biddings-selection-columns.tsx b/lib/bidding/selection/biddings-selection-columns.tsx index 355d5aaa..87c489e3 100644 --- a/lib/bidding/selection/biddings-selection-columns.tsx +++ b/lib/bidding/selection/biddings-selection-columns.tsx @@ -175,23 +175,17 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps): if (!startDate || !endDate) return - - const now = new Date() const startObj = new Date(startDate) const endObj = new Date(endDate) - const isPast = now > endObj - const isClosed = isPast - + // 비교로직만 유지, 색상표기/마감뱃지 제거 // UI 표시용 KST 변환 const formatKst = (d: Date) => new Date(d.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') return (
-
+
{formatKst(startObj)} ~ {formatKst(endObj)}
- {isClosed && ( - 마감 - )}
) }, diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts index 8fd1d368..1ae23e81 100644 --- a/lib/bidding/service.ts +++ b/lib/bidding/service.ts @@ -3635,7 +3635,6 @@ export async function getBiddingsForSelection(input: GetBiddingsSchema) { // 'bidding_opened', 'bidding_closed', 'evaluation_of_bidding', 'vendor_selected' 상태만 조회 basicConditions.push( or( - eq(biddings.status, 'bidding_opened'), eq(biddings.status, 'bidding_closed'), eq(biddings.status, 'evaluation_of_bidding'), eq(biddings.status, 'vendor_selected') diff --git a/lib/bidding/vendor/partners-bidding-list-columns.tsx b/lib/bidding/vendor/partners-bidding-list-columns.tsx index 64b4bebf..a122e87b 100644 --- a/lib/bidding/vendor/partners-bidding-list-columns.tsx +++ b/lib/bidding/vendor/partners-bidding-list-columns.tsx @@ -348,11 +348,18 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL if (!startDate || !endDate) { return
-
} + + const startObj = new Date(startDate) + const endObj = new Date(endDate) + + // UI 표시용 KST 변환 + const formatKst = (d: Date) => new Date(d.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') + return (
-
{new Date(startDate).toISOString().slice(0, 16).replace('T', ' ')}
+
{formatKst(startObj)}
~
-
{new Date(endDate).toISOString().slice(0, 16).replace('T', ' ')}
+
{formatKst(endObj)}
) }, -- cgit v1.2.3