'use client' import * as React from 'react' // 브라우저 환경 체크 const isBrowser = typeof window !== 'undefined' import { useRouter } from 'next/navigation' import { useTransition } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Label } from '@/components/ui/label' import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible' import { ArrowLeft, User, Users, Send, CheckCircle, XCircle, Save, FileText, Package, Trash2, Calendar, ChevronDown } from 'lucide-react' import { format } from 'date-fns' import { formatDate } from '@/lib/utils' import { getBiddingDetailsForPartners, submitPartnerResponse, updatePartnerBiddingParticipation, saveBiddingDraft, getPriceAdjustmentFormByBiddingCompanyId, getPartnerBiddingItemQuotations } from '../detail/service' import { cancelBiddingResponse } from '../detail/bidding-actions' import { getPrItemsForBidding } from '@/lib/bidding/pre-quote/service' import { getBiddingConditions } from '@/lib/bidding/service' import { PrItemsPricingTable } from './components/pr-items-pricing-table' import { SimpleFileUpload } from './components/simple-file-upload' import { getTaxConditionName } from "@/lib/tax-conditions/types" import { biddingStatusLabels, contractTypeLabels, biddingTypeLabels } from '@/db/schema' import { useToast } from '@/hooks/use-toast' import { useSession } from 'next-auth/react' import { getBiddingNotice } from '../service' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' interface PartnersBiddingDetailProps { biddingId: number companyId: number } interface BiddingDetail { id: number biddingNumber: string revision: number | null projectName: string | null itemName: string | null title: string description: string | null contractType: string biddingType: string awardCount: string | null contractStartDate: Date | string | null contractEndDate: Date | string | null preQuoteDate: Date | string | null biddingRegistrationDate: Date | string | null submissionStartDate: Date | string | null submissionEndDate: Date | string | null currency: string budget: number | null targetPrice: number | null status: string bidPicName: string | null // 입찰담당자 bidPicPhone?: string | null // 입찰담당자 전화번호 supplyPicName: string | null // 조달담당자 biddingCompanyId: number biddingId: number invitationStatus: string finalQuoteAmount: number | null finalQuoteSubmittedAt: Date | null isFinalSubmission: boolean isWinner: boolean isAttendingMeeting: boolean | null isBiddingParticipated: boolean | null additionalProposals: string | null responseSubmittedAt: Date | null priceAdjustmentResponse: boolean | null // 연동제 적용 여부 isPreQuoteParticipated: boolean | null // 입찰 참여 여부 isPriceAdjustmentApplicableQuestion: boolean // 연동제 적용요건 문의 여부 } interface PrItem { id: number biddingId: number itemNumber: string | null projectId: number | null projectInfo: string | null itemInfo: string | null shi: string | null materialGroupNumber: string | null materialGroupInfo: string | null materialNumber: string | null materialInfo: string | null requestedDeliveryDate: Date | string | null annualUnitPrice: string | null currency: string | null quantity: string | null quantityUnit: string | null totalWeight: string | null weightUnit: string | null priceUnit: string | null purchaseUnit: string | null materialWeight: string | null prNumber: string | null hasSpecDocument: boolean | null specification: string | null bidUnitPrice?: string | number | null bidAmount?: string | number | null proposedDeliveryDate?: string | Date | null technicalSpecification?: string | null } interface BiddingPrItemQuotation { prItemId: number bidUnitPrice: number bidAmount: number proposedDeliveryDate?: string technicalSpecification?: string } interface BiddingConditions { id?: number biddingId?: number paymentTerms?: string taxConditions?: string incoterms?: string incotermsOption?: string contractDeliveryDate?: string shippingPort?: string destinationPort?: string isPriceAdjustmentApplicable?: boolean sparePartOptions?: string createdAt?: string updatedAt?: string } export function PartnersBiddingDetail({ biddingId, companyId }: PartnersBiddingDetailProps) { const router = useRouter() const { toast } = useToast() const session = useSession() const [isPending, startTransition] = useTransition() const [biddingDetail, setBiddingDetail] = React.useState(null) const [isLoading, setIsLoading] = React.useState(true) const [isUpdatingParticipation, setIsUpdatingParticipation] = React.useState(false) const [isSavingDraft, setIsSavingDraft] = React.useState(false) const [isSubmitting, setIsSubmitting] = React.useState(false) const [isCancelling, setIsCancelling] = React.useState(false) const [isFinalSubmission, setIsFinalSubmission] = React.useState(false) const [isBiddingNoticeLoading, setIsBiddingNoticeLoading] = React.useState(false) const [isExpired, setIsExpired] = React.useState(false) // 입찰공고 관련 상태 const [biddingNotice, setBiddingNotice] = React.useState<{ id?: number biddingId?: number | null title?: string content?: string isTemplate?: boolean createdAt?: string | Date updatedAt?: string | Date } | null>(null) const [biddingConditions, setBiddingConditions] = React.useState(null) const [isNoticeOpen, setIsNoticeOpen] = React.useState(false) // 품목별 입찰 관련 상태 const [prItems, setPrItems] = React.useState([]) const [prItemQuotations, setPrItemQuotations] = React.useState([]) const [totalQuotationAmount, setTotalQuotationAmount] = React.useState(0) // 응찰 폼 상태 const [responseData, setResponseData] = React.useState({ finalQuoteAmount: '', proposedContractDeliveryDate: '', additionalProposals: '', priceAdjustmentResponse: null as boolean | null, // null: 미선택, true: 적용, false: 미적용 }) // 연동제 폼 상태 const [priceAdjustmentForm, setPriceAdjustmentForm] = React.useState({ itemName: '', adjustmentReflectionPoint: '', majorApplicableRawMaterial: '', adjustmentFormula: '', rawMaterialPriceIndex: '', referenceDate: '', comparisonDate: '', adjustmentRatio: '', notes: '', adjustmentConditions: '', majorNonApplicableRawMaterial: '', adjustmentPeriod: '', contractorWriter: '', adjustmentDate: '', nonApplicableReason: '', // 연동제 미희망 사유 }) const userId = session.data?.user?.id || '' // 데이터 로드 // 입찰공고 로드 함수 const loadBiddingNotice = React.useCallback(async () => { setIsBiddingNoticeLoading(true) try { const notice = await getBiddingNotice(biddingId) console.log('입찰공고 로드 성공:', notice) setBiddingNotice(notice) } catch (error) { console.error('Failed to load bidding notice:', error) } finally { setIsBiddingNoticeLoading(false) } }, [biddingId]) React.useEffect(() => { const loadData = async () => { try { setIsLoading(true) // 데이터 로드 시작 로그 console.log('입찰 상세 데이터 로드 시작:', { biddingId, companyId }) console.log('데이터베이스 쿼리 시작...') const [result, prItemsResult, noticeResult, conditionsResult] = await Promise.all([ getBiddingDetailsForPartners(biddingId, companyId).catch(error => { console.error('Failed to get bidding details:', error) return null }), getPrItemsForBidding(biddingId, companyId).catch(error => { console.error('Failed to get PR items:', error) return [] }), loadBiddingNotice().catch(error => { console.error('Failed to load bidding notice:', error) return null }), getBiddingConditions(biddingId).catch(error => { console.error('Failed to load bidding conditions:', error) return null }) ]) console.log('데이터베이스 쿼리 완료:', { resultExists: !!result, prItemsExists: !!prItemsResult, noticeExists: !!noticeResult, conditionsExists: !!conditionsResult }) console.log('데이터 로드 완료:', { result: !!result, prItemsCount: Array.isArray(prItemsResult) ? prItemsResult.length : 0, noticeResult: !!noticeResult, conditionsResult: !!conditionsResult }) if (result) { console.log('입찰 상세 데이터 로드 성공:', { biddingId: result.biddingId, isBiddingParticipated: result.isBiddingParticipated, invitationStatus: result.invitationStatus, finalQuoteAmount: result.finalQuoteAmount }) setBiddingDetail(result) // 만료 여부 확인 if (result.submissionEndDate) { const now = new Date() const deadline = new Date(result.submissionEndDate) setIsExpired(deadline < now) } // 기존 응답 데이터로 폼 초기화 setResponseData({ finalQuoteAmount: result.finalQuoteAmount?.toString() || '', proposedContractDeliveryDate: result.proposedContractDeliveryDate || '', additionalProposals: result.additionalProposals || '', priceAdjustmentResponse: result.priceAdjustmentResponse || null, }) // 입찰 조건 로드 if (conditionsResult) { console.log('입찰 조건 로드:', conditionsResult) setBiddingConditions(conditionsResult) } } // PR 아이템 설정 setPrItems(prItemsResult) // PR 아이템 결과로부터 견적 정보 추출 및 설정 if (Array.isArray(prItemsResult) && prItemsResult.length > 0) { const initialQuotations = prItemsResult.map((item: any) => ({ prItemId: item.id, bidUnitPrice: item.bidUnitPrice ? Number(item.bidUnitPrice) : 0, bidAmount: item.bidAmount ? Number(item.bidAmount) : 0, proposedDeliveryDate: item.proposedDeliveryDate ? (item.proposedDeliveryDate instanceof Date ? item.proposedDeliveryDate.toISOString().split('T')[0] : item.proposedDeliveryDate) : undefined, technicalSpecification: item.technicalSpecification || undefined })); setPrItemQuotations(initialQuotations); // 총 금액 계산 const total = initialQuotations.reduce((sum: number, q: any) => sum + q.bidAmount, 0); setTotalQuotationAmount(total); // 응찰 확정 시 총 금액 설정 if (total > 0 && result?.isBiddingParticipated === true) { setResponseData(prev => ({ ...prev, finalQuoteAmount: total.toString() })); } } // 입찰 데이터를 본입찰용으로 로드 (응찰 확정 시 또는 입찰이 있는 경우) if (result?.biddingCompanyId) { try { // 연동제 데이터 로드 (입찰에서 답변했으면 로드, 아니면 입찰 조건 확인) if (result.priceAdjustmentResponse !== null) { // 입찰에서 이미 답변한 경우 - 연동제 폼 로드 const savedPriceAdjustmentForm = await getPriceAdjustmentFormByBiddingCompanyId(result.biddingCompanyId) if (savedPriceAdjustmentForm) { setPriceAdjustmentForm({ itemName: savedPriceAdjustmentForm.itemName || '', adjustmentReflectionPoint: savedPriceAdjustmentForm.adjustmentReflectionPoint || '', majorApplicableRawMaterial: savedPriceAdjustmentForm.majorApplicableRawMaterial || '', adjustmentFormula: savedPriceAdjustmentForm.adjustmentFormula || '', rawMaterialPriceIndex: savedPriceAdjustmentForm.rawMaterialPriceIndex || '', referenceDate: savedPriceAdjustmentForm.referenceDate || '', comparisonDate: savedPriceAdjustmentForm.comparisonDate || '', adjustmentRatio: savedPriceAdjustmentForm.adjustmentRatio || '', notes: savedPriceAdjustmentForm.notes || '', adjustmentConditions: savedPriceAdjustmentForm.adjustmentConditions || '', majorNonApplicableRawMaterial: savedPriceAdjustmentForm.majorNonApplicableRawMaterial || '', adjustmentPeriod: savedPriceAdjustmentForm.adjustmentPeriod || '', contractorWriter: savedPriceAdjustmentForm.contractorWriter || '', adjustmentDate: savedPriceAdjustmentForm.adjustmentDate || '', nonApplicableReason: savedPriceAdjustmentForm.nonApplicableReason || '', }) } } } catch (error) { console.error('Failed to load pre-quote data:', error) } } } catch (error) { console.error('Failed to load bidding detail:', error) toast({ title: '오류', description: '입찰 정보를 불러오는데 실패했습니다.', variant: 'destructive', }) } finally { setIsLoading(false) } } loadData() }, [biddingId, companyId, toast]) // 입찰 참여여부 결정 핸들러 const handleParticipationDecision = async (participated: boolean) => { if (!biddingDetail) return // 만료 체크 if (isExpired) { toast({ title: "참여 불가", description: "제출 마감일이 지났습니다. 더 이상 입찰에 참여할 수 없습니다.", variant: "destructive", }) return } setIsUpdatingParticipation(true) try { const result = await updatePartnerBiddingParticipation( biddingDetail.biddingCompanyId, participated, userId ) if (result.success) { toast({ title: participated ? '참여 확정' : '미참여 확정', description: participated ? '입찰에 참여하셨습니다. 이제 입찰을 작성할 수 있습니다.' : '입찰 참여를 거절하셨습니다.', }) // 데이터 새로고침 const updatedDetail = await getBiddingDetailsForPartners(biddingId, companyId) if (updatedDetail) { setBiddingDetail(updatedDetail) // 참여 확정 시 입찰 데이터가 있다면 로드 if (participated && updatedDetail.biddingCompanyId) { try { const preQuoteData = await getPartnerBiddingItemQuotations(updatedDetail.biddingCompanyId) if (preQuoteData && Array.isArray(preQuoteData) && preQuoteData.length > 0) { console.log('참여확정 후 입찰 데이터:', preQuoteData) const convertedQuotations = preQuoteData .filter(item => item && typeof item === 'object' && item.prItemId) .map(item => ({ prItemId: item.prItemId, bidUnitPrice: item.bidUnitPrice, bidAmount: item.bidAmount, proposedDeliveryDate: item.proposedDeliveryDate || undefined, technicalSpecification: item.technicalSpecification || undefined })) console.log('참여확정 후 변환된 입찰 데이터:', convertedQuotations) if (Array.isArray(convertedQuotations) && convertedQuotations.length > 0) { setPrItemQuotations(convertedQuotations) const total = convertedQuotations.reduce((sum, q) => { const amount = Number(q.bidAmount) || 0 return sum + amount }, 0) setTotalQuotationAmount(total) console.log('참여확정 후 계산된 총 금액:', total) if (total > 0) { setResponseData(prev => ({ ...prev, finalQuoteAmount: total.toString() })) } } } } catch (error) { console.error('Failed to load pre-quote data after participation:', error) } } } } else { toast({ title: '오류', description: result.error, variant: 'destructive', }) } } catch (error) { console.error('Failed to update participation:', error) toast({ title: '오류', description: '참여여부 업데이트에 실패했습니다.', variant: 'destructive', }) } finally { setIsUpdatingParticipation(false) } } // 품목별 입찰 변경 핸들러 const handleQuotationsChange = (quotations: BiddingPrItemQuotation[]) => { console.log('입찰 변경:', quotations) setPrItemQuotations(quotations) } // 총 금액 변경 핸들러 const handleTotalAmountChange = (total: number) => { setTotalQuotationAmount(total) // 자동으로 총 입찰 금액도 업데이트 setResponseData(prev => ({ ...prev, finalQuoteAmount: total.toString() })) } // 임시 저장 핸들러 const handleSaveDraft = async () => { if (!biddingDetail || !userId) return // 제출 마감일 체크 (상태 사용) if (isExpired) { toast({ title: "접근 제한", description: "제출 마감일이 지났습니다. 더 이상 입찰에 참여할 수 없습니다.", variant: "destructive", }) return } // 입찰 마감 상태 체크 const biddingStatus = biddingDetail.status const isClosed = biddingStatus === 'bidding_closed' || biddingStatus === 'vendor_selected' || biddingStatus === 'bidding_disposal' if (isClosed) { toast({ title: "접근 제한", description: "입찰이 마감되어 더 이상 입찰에 참여할 수 없습니다.", variant: "destructive", }) return } if (prItemQuotations.length === 0) { toast({ title: '저장할 데이터 없음', description: '저장할 품목별 입찰이 없습니다.', variant: 'destructive', }) return } setIsSavingDraft(true) try { const quotationsForSave = prItemQuotations.map(q => ({ prItemId: q.prItemId, bidUnitPrice: q.bidUnitPrice, bidAmount: q.bidAmount, proposedDeliveryDate: q.proposedDeliveryDate, technicalSpecification: q.technicalSpecification })) console.log('임시저장 - prItemQuotations:', prItemQuotations) console.log('임시저장 - quotationsForSave:', quotationsForSave) const result = await saveBiddingDraft( biddingDetail.biddingCompanyId, quotationsForSave, userId ) if (result.success) { toast({ title: '임시 저장 완료', description: '품목별 입찰이 임시 저장되었습니다.', }) } else { toast({ title: '임시 저장 실패', description: result.error, variant: 'destructive', }) } } catch (error) { console.error('Failed to save draft:', error) toast({ title: '오류', description: '임시 저장에 실패했습니다.', variant: 'destructive', }) } finally { setIsSavingDraft(false) } } // 응찰 포기 핸들러 const handleCancelResponse = async () => { if (!biddingDetail || !userId) return // 최종제출한 경우 취소 불가 if (biddingDetail.isFinalSubmission) { toast({ title: '취소 불가', description: '최종 제출된 응찰은 취소할 수 없습니다.', variant: 'destructive', }) return } if (isBrowser && !window.confirm('응찰을 취소하시겠습니까? 작성한 입찰 내용이 모두 삭제됩니다.')) { return } setIsCancelling(true) try { const result = await cancelBiddingResponse(biddingDetail.biddingCompanyId, userId) if (result.success) { toast({ title: '응찰 포기 완료', description: '응찰이 취소되었습니다.', }) // 페이지 새로고침 if (isBrowser) { window.location.reload() } else { // 서버사이드에서는 라우터로 이동 router.push(`/partners/bid/${biddingId}`) } } else { toast({ title: '응찰 포기 실패', description: result.error, variant: 'destructive', }) } } catch (error) { console.error('Failed to cancel bidding response:', error) toast({ title: '오류', description: '응찰 포기에 실패했습니다.', variant: 'destructive', }) } finally { setIsCancelling(false) } } const handleSubmitResponse = () => { if (!biddingDetail) return // 제출 마감일 체크 (상태 사용) if (isExpired) { toast({ title: "접근 제한", description: "제출 마감일이 지났습니다. 더 이상 입찰에 참여할 수 없습니다.", variant: "destructive", }) return } // 입찰 마감 상태 체크 const biddingStatus = biddingDetail.status const isClosed = biddingStatus === 'bidding_closed' || biddingStatus === 'vendor_selected' || biddingStatus === 'bidding_disposal' if (isClosed) { toast({ title: "접근 제한", description: "입찰이 마감되어 더 이상 입찰에 참여할 수 없습니다.", variant: "destructive", }) return } // 필수값 검증 if (!responseData.finalQuoteAmount.trim()) { toast({ title: '유효성 오류', description: '입찰 금액을 입력해주세요.', variant: 'destructive', }) return } // 품목별 입찰이 있는지 확인 if (prItems.length > 0 && prItemQuotations.length === 0) { toast({ title: '유효성 오류', description: '품목별 입찰을 작성해주세요.', variant: 'destructive', }) return } setIsSubmitting(true) startTransition(async () => { try { // 1. 입찰 참여 상태를 응찰로 변경 const participationResult = await updatePartnerBiddingParticipation( biddingDetail.biddingCompanyId, true, // 응찰 userId ) if (!participationResult.success) { throw new Error(participationResult.error) } // 2. 최종 입찰 응답 제출 (PR 아이템별 입찰 포함) const result = await submitPartnerResponse( biddingDetail.biddingCompanyId, { finalQuoteAmount: parseFloat(responseData.finalQuoteAmount), proposedContractDeliveryDate: responseData.proposedContractDeliveryDate, additionalProposals: responseData.additionalProposals, isFinalSubmission, // 최종제출 여부 추가 // 연동제 데이터 추가 (연동제 적용요건 문의가 있는 경우만) priceAdjustmentResponse: biddingDetail.isPriceAdjustmentApplicableQuestion ? (responseData.priceAdjustmentResponse ?? false) as boolean : false, priceAdjustmentForm: biddingDetail.isPriceAdjustmentApplicableQuestion && (responseData.priceAdjustmentResponse ?? false) as boolean ? { ...priceAdjustmentForm, adjustmentRatio: parseFloat(priceAdjustmentForm.adjustmentRatio) || 0 } : undefined, prItemQuotations: prItemQuotations.length > 0 ? prItemQuotations.map(q => ({ prItemId: q.prItemId, bidUnitPrice: q.bidUnitPrice, bidAmount: q.bidAmount, proposedDeliveryDate: q.proposedDeliveryDate, technicalSpecification: q.technicalSpecification })) : undefined, }, userId ) if (result.success) { toast({ title: isFinalSubmission ? '응찰 완료' : '임시 저장 완료', description: isFinalSubmission ? '입찰이 최종 제출되었습니다.' : '입찰이 임시 저장되었습니다.', }) // 데이터 새로고침 const updatedDetail = await getBiddingDetailsForPartners(biddingId, companyId) if (updatedDetail) { setBiddingDetail(updatedDetail) } } else { throw new Error(result.error) } } catch (error) { console.error('Failed to submit response:', error) toast({ title: '오류', description: error instanceof Error ? error.message : '응찰 제출에 실패했습니다.', variant: 'destructive', }) } finally { setIsSubmitting(false) } }) } if (isLoading) { return (

입찰 정보를 불러오는 중...

) } if (!biddingDetail) { return (

입찰 정보를 찾을 수 없습니다.

) } return (
{/* 헤더 */}

{biddingDetail.title}

{biddingDetail.biddingNumber} {biddingStatusLabels[biddingDetail.status]}
{/* 입찰 참여여부 상태 표시 */}
{biddingDetail.isBiddingParticipated === null ? '참여 결정 대기' : biddingDetail.isBiddingParticipated === true ? '응찰' : '응찰포기'}
{/* 입찰 공고 섹션 */} 입찰 공고
{contractTypeLabels[biddingDetail.contractType]}
{biddingTypeLabels[biddingDetail.biddingType]}
{biddingDetail.awardCount === 'single' ? '단수' : biddingDetail.awardCount === 'multiple' ? '복수' : '미설정'}
{biddingDetail.bidPicName || '미설정'}
{biddingDetail.bidPicPhone && (
{biddingDetail.bidPicPhone}
)}
{biddingDetail.supplyPicName || '미설정'}
{/* 계약기간 */} {biddingDetail.contractStartDate && biddingDetail.contractEndDate && (
{format(new Date(biddingDetail.contractStartDate), "yyyy-MM-dd")} ~ {format(new Date(biddingDetail.contractEndDate), "yyyy-MM-dd")}
)} {/* 제출 마감일 D-day */} {biddingDetail.submissionEndDate && (
{(() => { const now = new Date() const deadline = new Date(biddingDetail.submissionEndDate) // isExpired 상태 사용 const timeLeft = deadline.getTime() - now.getTime() const daysLeft = Math.floor(timeLeft / (1000 * 60 * 60 * 24)) const hoursLeft = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) const kstDeadline = new Date(deadline.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') return (
제출 마감일: {kstDeadline}
{isExpired ? ( 마감됨 ) : daysLeft <= 1 ? ( {daysLeft === 0 ? `${hoursLeft}시간 남음` : `${daysLeft}일 남음`} ) : ( {daysLeft}일 남음 )}
{isExpired && (
⚠️ 제출 마감일이 지났습니다. 입찰 제출이 불가능합니다.
)}
) })()}
)} {/* 일정 정보 */}
{biddingDetail.submissionStartDate && biddingDetail.submissionEndDate && (
입찰서 제출기간: {(() => { const start = new Date(biddingDetail.submissionStartDate!) const end = new Date(biddingDetail.submissionEndDate!) const kstStart = new Date(start.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') const kstEnd = new Date(end.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ') return `${kstStart} ~ ${kstEnd}` })()}
)}
{/* 입찰공고 토글 섹션 */} {biddingNotice && (
입찰공고 내용
{biddingNotice.title && (

{biddingNotice.title}

)} {biddingNotice.content ? (
) : (

입찰공고 내용이 없습니다.

)}
)} {/* 현재 설정된 조건 섹션 */} {biddingConditions && ( 현재 설정된 입찰 조건

{biddingConditions.paymentTerms || "미설정"}

{biddingConditions.taxConditions ? getTaxConditionName(biddingConditions.taxConditions) : "미설정" }

{biddingConditions.incoterms || "미설정"}

{biddingConditions.incotermsOption || "미설정"}

{biddingConditions.contractDeliveryDate ? formatDate(biddingConditions.contractDeliveryDate, 'KR') : "미설정" }

{biddingConditions.shippingPort || "미설정"}

{biddingConditions.destinationPort || "미설정"}

{/*

{biddingConditions.isPriceAdjustmentApplicable ? "적용 가능" : "적용 불가"}

*/}

{biddingConditions.sparePartOptions}

)} {/* 참여 상태에 따른 섹션 표시 */} {biddingDetail.isBiddingParticipated === false ? ( /* 응찰포기 상태 표시 */ 응찰포기

입찰에 참여하지 않기로 결정했습니다

해당 입찰에 대한 입찰 제출 및 관련 기능은 이용할 수 없습니다.

) : biddingDetail.isBiddingParticipated === null ? ( <> {/* 품목 정보 확인 (Read Only) */} 입찰 품목 정보 {prItems.length > 0 ? ( {}} onTotalAmountChange={() => {}} readOnly={true} /> ) : (

등록된 품목이 없습니다.

)}
{/* 참여 의사 확인 섹션 */} 입찰 참여 의사 확인

이 입찰에 참여하시겠습니까?

참여를 선택하시면 입찰 작성 및 제출이 가능합니다.

) : biddingDetail.isBiddingParticipated === true ? ( /* 응찰 폼 섹션 */ 응찰하기 {/* 품목별 상세 입찰 테이블 */} {prItems.length > 0 ? ( ) : (

등록된 품목이 없습니다.

)} {/* 입찰 첨부파일 섹션 */} {biddingDetail && userId && ( )} {/* 연동제 적용 여부 - 협력업체 별 연동제 적용요건 문의 여부에 따라 표시 */} {biddingDetail.isPriceAdjustmentApplicableQuestion && ( <>
{ const newValue = value === 'apply' ? true : value === 'not-apply' ? false : null setResponseData({...responseData, priceAdjustmentResponse: newValue}) }} >
{/* 연동제 상세 정보 */} {responseData.priceAdjustmentResponse !== null && ( 하도급대금등 연동표 {/* 공통 필드 - 품목등의 명칭 */}
setPriceAdjustmentForm({...priceAdjustmentForm, itemName: e.target.value})} placeholder="품목명을 입력하세요" required />
{/* 연동제 적용 시 - 모든 필드 표시 */} {responseData.priceAdjustmentResponse === true && ( <>
setPriceAdjustmentForm({...priceAdjustmentForm, adjustmentReflectionPoint: e.target.value})} placeholder="반영시점을 입력하세요" required />
setPriceAdjustmentForm({...priceAdjustmentForm, adjustmentRatio: e.target.value})} placeholder="비율을 입력하세요" required />
setPriceAdjustmentForm({...priceAdjustmentForm, adjustmentPeriod: e.target.value})} placeholder="조정주기를 입력하세요" required />
setPriceAdjustmentForm({...priceAdjustmentForm, referenceDate: e.target.value})} required />
setPriceAdjustmentForm({...priceAdjustmentForm, comparisonDate: e.target.value})} required />
setPriceAdjustmentForm({...priceAdjustmentForm, contractorWriter: e.target.value})} placeholder="작성자명을 입력하세요" required />
setPriceAdjustmentForm({...priceAdjustmentForm, adjustmentDate: e.target.value})} required />