'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 { formatDate } from '@/lib/utils' import { getBiddingDetailsForPartners, submitPartnerResponse, updatePartnerBiddingParticipation, saveBiddingDraft, getPriceAdjustmentFormByBiddingCompanyId } from '../detail/service' import { cancelBiddingResponse } from '../detail/bidding-actions' import { getPrItemsForBidding, getSavedPrItemQuotations } 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 | null contractEndDate: Date | null preQuoteDate: Date | null biddingRegistrationDate: Date | null submissionStartDate: Date | null submissionEndDate: Date | null evaluationDate: Date | null currency: string budget: number | null targetPrice: number | null status: string bidPicName: 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 // 사전견적 참여 여부 } 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 | 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 } 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 [biddingNotice, setBiddingNotice] = React.useState<{ id?: number biddingId?: number title?: string content?: string isTemplate?: boolean createdAt?: string updatedAt?: string } | 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).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) // 기존 응답 데이터로 폼 초기화 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) // 사전견적 데이터를 본입찰용으로 로드 (응찰 확정 시 또는 사전견적이 있는 경우) if (result?.biddingCompanyId) { try { // 사전견적 데이터를 가져와서 본입찰용으로 변환 const preQuoteData = await getSavedPrItemQuotations(result.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) } } // 응찰 확정 시에만 사전견적 금액을 finalQuoteAmount로 설정 if (totalQuotationAmount > 0 && result?.isBiddingParticipated === true) { console.log('응찰 확정됨, 사전견적 금액 설정:', totalQuotationAmount) console.log('사전견적 금액을 finalQuoteAmount로 설정:', totalQuotationAmount) setResponseData(prev => ({ ...prev, finalQuoteAmount: totalQuotationAmount.toString() })) } // 연동제 데이터 로드 (사전견적에서 답변했으면 로드, 아니면 입찰 조건 확인) 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 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 getSavedPrItemQuotations(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 // 입찰 마감 상태 체크 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 // 입찰 마감 상태 체크 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, // 최종제출 여부 추가 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.supplyPicName || '미설정'}
{/* 계약기간 */} {biddingDetail.contractStartDate && biddingDetail.contractEndDate && (
{formatDate(biddingDetail.contractStartDate, 'KR')} ~ {formatDate(biddingDetail.contractEndDate, 'KR')}
)} {/* 제출 마감일 D-day */} {/* {biddingDetail.submissionEndDate && (
{(() => { const now = new Date() const deadline = new Date(biddingDetail.submissionEndDate) const isExpired = deadline < now 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)) return (
제출 마감일: {formatDate(biddingDetail.submissionEndDate, 'KR')}
{isExpired ? ( 마감됨 ) : daysLeft <= 1 ? ( {daysLeft === 0 ? `${hoursLeft}시간 남음` : `${daysLeft}일 남음`} ) : ( {daysLeft}일 남음 )}
{isExpired && (
⚠️ 제출 마감일이 지났습니다. 입찰 제출이 불가능합니다.
)}
) })()}
)} */} {/* 일정 정보 */}
{biddingDetail.submissionStartDate && biddingDetail.submissionEndDate && (
응찰기간: {formatDate(biddingDetail.submissionStartDate, 'KR')} ~ {formatDate(biddingDetail.submissionEndDate, 'KR')}
)} {biddingDetail.evaluationDate && (
평가일: {formatDate(biddingDetail.evaluationDate, 'KR')}
)}
{/* 입찰공고 토글 섹션 */} {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 ? ( /* 참여 의사 확인 섹션 */ 입찰 참여 의사 확인

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

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

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

등록된 품목이 없습니다.

)} {/* 견적 첨부파일 섹션 */} {biddingDetail && userId && ( )} {/* 연동제 적용 여부 - SHI가 연동제를 요구하고, 사전견적에서 답변하지 않은 경우만 표시 */} {biddingConditions?.isPriceAdjustmentApplicable && biddingDetail.priceAdjustmentResponse === null && ( <>
{ 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 />