From 213b995271edfbe7604d07ba4b71fcc20038a894 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 17 Nov 2025 10:00:07 +0000 Subject: (최겸) 구매 입찰 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(procurement)/rfq-last/[id]/vendor/page.tsx | 4 +- app/[lng]/partners/(partners)/settings/page.tsx | 3 ++ components/auth/simple-reauth-modal.tsx | 21 +++++++--- .../bidding/create/bidding-create-dialog.tsx | 4 +- .../bidding/manage/bidding-basic-info-editor.tsx | 4 +- .../bidding/manage/bidding-companies-editor.tsx | 44 ++++++++++++++++--- .../manage/bidding-detail-vendor-create-dialog.tsx | 16 ++++++- db/schema/bidding.ts | 2 + .../detail/table/bidding-detail-vendor-columns.tsx | 2 +- .../detail/table/bidding-detail-vendor-table.tsx | 3 +- .../bidding-detail-vendor-toolbar-actions.tsx | 10 ++--- lib/bidding/list/biddings-table-columns.tsx | 1 + lib/bidding/receive/biddings-receive-table.tsx | 2 +- lib/bidding/selection/actions.ts | 49 ++++++++++++++++------ .../selection/biddings-selection-columns.tsx | 10 ++--- lib/bidding/service.ts | 7 +++- .../vendor/partners-bidding-list-columns.tsx | 2 +- 17 files changed, 138 insertions(+), 46 deletions(-) diff --git a/app/[lng]/evcp/(evcp)/(procurement)/rfq-last/[id]/vendor/page.tsx b/app/[lng]/evcp/(evcp)/(procurement)/rfq-last/[id]/vendor/page.tsx index d390eab7..18e17704 100644 --- a/app/[lng]/evcp/(evcp)/(procurement)/rfq-last/[id]/vendor/page.tsx +++ b/app/[lng]/evcp/(evcp)/(procurement)/rfq-last/[id]/vendor/page.tsx @@ -81,7 +81,7 @@ export default async function VendorPage(props: VendorPageProps) { {/* 응답 상태 요약 카드 */} -
+ {/*
-
+
*/} {/* 벤더 목록 테이블 */} diff --git a/app/[lng]/partners/(partners)/settings/page.tsx b/app/[lng]/partners/(partners)/settings/page.tsx index 727be46b..80f17c7c 100644 --- a/app/[lng]/partners/(partners)/settings/page.tsx +++ b/app/[lng]/partners/(partners)/settings/page.tsx @@ -135,6 +135,9 @@ export default function SettingsAccountPage() { { + router.refresh() + }} userEmail={userEmail} /> diff --git a/components/auth/simple-reauth-modal.tsx b/components/auth/simple-reauth-modal.tsx index f00674e3..80f03ad9 100644 --- a/components/auth/simple-reauth-modal.tsx +++ b/components/auth/simple-reauth-modal.tsx @@ -36,13 +36,15 @@ type ReAuthFormValues = z.infer interface SimpleReAuthModalProps { isOpen: boolean onSuccess: () => void + onClose?: () => void userEmail: string } -export function SimpleReAuthModal({ - isOpen, - onSuccess, - userEmail +export function SimpleReAuthModal({ + isOpen, + onSuccess, + onClose, + userEmail }: SimpleReAuthModalProps) { const [isLoading, setIsLoading] = React.useState(false) const [attemptCount, setAttemptCount] = React.useState(0) @@ -114,11 +116,18 @@ export function SimpleReAuthModal({ if (!isOpen) { form.reset() setAttemptCount(0) + if (onClose) { + // 모달이 닫힐 때 정리 작업 + } } - }, [isOpen, form]) + }, [isOpen, form, onClose]) return ( - {}}> + { + if (!open && onClose) { + onClose() + } + }}> diff --git a/components/bidding/create/bidding-create-dialog.tsx b/components/bidding/create/bidding-create-dialog.tsx index 30550ca4..bdb00a01 100644 --- a/components/bidding/create/bidding-create-dialog.tsx +++ b/components/bidding/create/bidding-create-dialog.tsx @@ -609,7 +609,7 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp {/* 4행: 하도급법적용여부, SHI 지급조건 */}
- ( @@ -634,7 +634,7 @@ export function BiddingCreateDialog({ form, onSuccess }: BiddingCreateDialogProp )} - /> + /> */}
-
+ {/*
연동제 적용 가능
@@ -1106,7 +1106,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp })) }} /> -
+
*/}
{/* 5행: 스페어파트 옵션 */} diff --git a/components/bidding/manage/bidding-companies-editor.tsx b/components/bidding/manage/bidding-companies-editor.tsx index 1ce8b014..4992c2ab 100644 --- a/components/bidding/manage/bidding-companies-editor.tsx +++ b/components/bidding/manage/bidding-companies-editor.tsx @@ -5,13 +5,14 @@ import { Building, User, Plus, Trash2 } from 'lucide-react' import { toast } from 'sonner' import { Button } from '@/components/ui/button' -import { - getBiddingVendors, +import { + getBiddingVendors, getBiddingCompanyContacts, createBiddingCompanyContact, deleteBiddingCompanyContact, getVendorContactsByVendorId, - updateBiddingCompanyPriceAdjustmentQuestion + updateBiddingCompanyPriceAdjustmentQuestion, + getBiddingById } from '@/lib/bidding/service' import { deleteBiddingCompany } from '@/lib/bidding/pre-quote/service' import { BiddingDetailVendorCreateDialog } from './bidding-detail-vendor-create-dialog' @@ -87,7 +88,11 @@ export function BiddingCompaniesEditor({ biddingId }: BiddingCompaniesEditorProp const [isLoadingContacts, setIsLoadingContacts] = React.useState(false) // 각 업체별 첫 번째 담당자 정보 저장 (vendorId -> 첫 번째 담당자) const [vendorFirstContacts, setVendorFirstContacts] = React.useState>(new Map()) - + + // 입찰 정보 (단수/복수 낙찰 확인용) + const [biddingInfo, setBiddingInfo] = React.useState(null) + const [isLoadingBiddingInfo, setIsLoadingBiddingInfo] = React.useState(false) + // 담당자 추가 다이얼로그 const [addContactDialogOpen, setAddContactDialogOpen] = React.useState(false) const [newContact, setNewContact] = React.useState({ @@ -141,6 +146,29 @@ export function BiddingCompaniesEditor({ biddingId }: BiddingCompaniesEditorProp } }, [biddingId]) + // 입찰 정보 로딩 + const loadBiddingInfo = React.useCallback(async () => { + setIsLoadingBiddingInfo(true) + try { + const result = await getBiddingById(biddingId) + if (result) { + setBiddingInfo(result) + } else { + console.error('Failed to load bidding info') + setBiddingInfo(null) + } + } catch (error) { + console.error('Failed to load bidding info:', error) + setBiddingInfo(null) + } finally { + setIsLoadingBiddingInfo(false) + } + }, [biddingId]) + + // 단수 입찰 여부 확인 및 업체 추가 제한 + const isSingleAwardBidding = biddingInfo?.awardCount === 'single' + const canAddVendor = !isSingleAwardBidding || vendors.length === 0 + // 데이터 로딩 React.useEffect(() => { const loadVendors = async () => { @@ -177,7 +205,7 @@ export function BiddingCompaniesEditor({ biddingId }: BiddingCompaniesEditorProp console.error(`Failed to load contact for vendor ${vendor.companyId}:`, error) } }) - + await Promise.all(contactPromises) setVendorFirstContacts(firstContactsMap) } else { @@ -194,7 +222,8 @@ export function BiddingCompaniesEditor({ biddingId }: BiddingCompaniesEditorProp } loadVendors() - }, [biddingId]) + loadBiddingInfo() + }, [biddingId, loadBiddingInfo]) // 업체 선택 핸들러 (단일 선택) const handleVendorSelect = async (vendor: QuotationVendor) => { @@ -482,6 +511,7 @@ export function BiddingCompaniesEditor({ biddingId }: BiddingCompaniesEditorProp

입찰에 참여하는 업체들을 관리합니다. 업체를 선택하면 하단에 담당자 목록이 표시됩니다.

+

단수 입찰의 경우 1개 업체만 등록 가능합니다.

@@ -256,7 +256,7 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps): 상세보기 - {row.original.status === 'bidding_opened' && ( + {/* {row.original.status === 'bidding_opened' && ( <> setRowAction({ row, type: "close_bidding" })}> @@ -264,7 +264,7 @@ export function getBiddingsSelectionColumns({ setRowAction }: GetColumnsProps): 입찰마감 - )} + )} */} {row.original.status === 'bidding_closed' && ( <> diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts index b60fc73d..cbeeb24a 100644 --- a/lib/bidding/service.ts +++ b/lib/bidding/service.ts @@ -35,7 +35,7 @@ import { } from 'drizzle-orm' import { revalidatePath } from 'next/cache' import { filterColumns } from '@/lib/filter-columns' -import { GetBiddingsSchema } from './validation' +import { GetBiddingsSchema, CreateBiddingSchema } from './validation' @@ -865,6 +865,9 @@ export interface CreateBiddingInput extends CreateBiddingSchema { meetingFiles: File[] } | null + // noticeType 필드 명시적 추가 (CreateBiddingSchema에 포함되어 있지만 타입 추론 문제 해결) + noticeType?: 'standard' | 'facility' | 'unit_price' + // PR 아이템들 (선택사항) prItems?: Array<{ id: string @@ -1174,6 +1177,7 @@ export async function createBidding(input: CreateBiddingInput, userId: string) { description: input.description, contractType: input.contractType, + noticeType: input.noticeType || 'standard', biddingType: input.biddingType, awardCount: input.awardCount, contractStartDate: input.contractStartDate ? parseDate(input.contractStartDate) : new Date(), @@ -1494,6 +1498,7 @@ export async function updateBidding(input: UpdateBiddingInput, userId: string) { if (input.content !== undefined) updateData.content = input.content if (input.contractType !== undefined) updateData.contractType = input.contractType + if (input.noticeType !== undefined) updateData.noticeType = input.noticeType if (input.biddingType !== undefined) updateData.biddingType = input.biddingType if (input.awardCount !== undefined) updateData.awardCount = input.awardCount if (input.contractStartDate !== undefined) updateData.contractStartDate = parseDate(input.contractStartDate) diff --git a/lib/bidding/vendor/partners-bidding-list-columns.tsx b/lib/bidding/vendor/partners-bidding-list-columns.tsx index 5870067a..6e8591c2 100644 --- a/lib/bidding/vendor/partners-bidding-list-columns.tsx +++ b/lib/bidding/vendor/partners-bidding-list-columns.tsx @@ -67,7 +67,7 @@ export function getPartnersBiddingListColumns({ setRowAction }: PartnersBiddingL return (
{biddingNumber}
-
Rev. {revision ?? 0}
+ {/*
Rev. {revision ?? 0}
*/} {originalBiddingNumber && (
원: {originalBiddingNumber}
)} -- cgit v1.2.3