From ba8cd44a0ed2c613a5f2cee06bfc9bd0f61f21c7 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 7 Nov 2025 08:39:04 +0000 Subject: (최겸) 입찰/견적 수정사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/bidding/vendor/partners-bidding-pre-quote.tsx | 1413 --------------------- 1 file changed, 1413 deletions(-) delete mode 100644 lib/bidding/vendor/partners-bidding-pre-quote.tsx (limited to 'lib/bidding/vendor/partners-bidding-pre-quote.tsx') diff --git a/lib/bidding/vendor/partners-bidding-pre-quote.tsx b/lib/bidding/vendor/partners-bidding-pre-quote.tsx deleted file mode 100644 index 8a157c5f..00000000 --- a/lib/bidding/vendor/partners-bidding-pre-quote.tsx +++ /dev/null @@ -1,1413 +0,0 @@ -'use client' - -import * as React from 'react' -import { useRouter } from 'next/navigation' -import { Button } from '@/components/ui/button' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { Badge } from '@/components/ui/badge' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Textarea } from '@/components/ui/textarea' -import { Checkbox } from '@/components/ui/checkbox' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { - ArrowLeft, - Calendar, - Building2, - Package, - User, - FileText, - Users, - Send, - CheckCircle, - XCircle, - Save -} from 'lucide-react' - -import { formatDate } from '@/lib/utils' -import { - getBiddingCompaniesForPartners, - submitPreQuoteResponse, - getPrItemsForBidding, - getSavedPrItemQuotations, - savePreQuoteDraft, - setPreQuoteParticipation -} from '../pre-quote/service' -import { getBiddingConditions } from '../service' -import { getPriceAdjustmentFormByBiddingCompanyId } from '../detail/service' -import { getIncotermsForSelection, getPaymentTermsForSelection, getPlaceOfShippingForSelection, getPlaceOfDestinationForSelection } from '@/lib/procurement-select/service' -import { TAX_CONDITIONS, getTaxConditionName } from '@/lib/tax-conditions/types' -import { PrItemsPricingTable } from './components/pr-items-pricing-table' -import { SimpleFileUpload } from './components/simple-file-upload' -import { - biddingStatusLabels, -} from '@/db/schema' -import { useToast } from '@/hooks/use-toast' -import { useTransition } from 'react' -import { useSession } from 'next-auth/react' - -interface PartnersBiddingPreQuoteProps { - biddingId: number - companyId: number -} - -interface BiddingDetail { - id: number - biddingNumber: string - revision: number | null - projectName: string | null - itemName: string | null - title: string - description: string | null - content: string | null - contractType: string - biddingType: string - awardCount: string - contractStartDate: Date | null - contractEndDate: Date | null - preQuoteDate: string | null - biddingRegistrationDate: string | null - submissionStartDate: string | null - submissionEndDate: string | null - evaluationDate: string | null - currency: string - budget: number | null - targetPrice: number | null - status: string - managerName: string | null - managerEmail: string | null - managerPhone: string | null - biddingCompanyId: number | null - biddingId: number // bidding의 ID 추가 - invitationStatus: string | null - preQuoteAmount: string | null - preQuoteSubmittedAt: string | null - preQuoteDeadline: string | null - isPreQuoteSelected: boolean | null - isAttendingMeeting: boolean | null - // companyConditionResponses에서 가져온 조건들 (제시된 조건과 응답 모두) - paymentTermsResponse: string | null - taxConditionsResponse: string | null - incotermsResponse: string | null - proposedContractDeliveryDate: string | null - proposedShippingPort: string | null - proposedDestinationPort: string | null - priceAdjustmentResponse: boolean | null - sparePartResponse: string | null - isInitialResponse: boolean | null - additionalProposals: string | null -} - -export function PartnersBiddingPreQuote({ biddingId, companyId }: PartnersBiddingPreQuoteProps) { - const router = useRouter() - const { toast } = useToast() - const [isPending, startTransition] = useTransition() - const session = useSession() - const [biddingDetail, setBiddingDetail] = React.useState(null) - const [isLoading, setIsLoading] = React.useState(true) - const [biddingConditions, setBiddingConditions] = React.useState(null) - - // Procurement 데이터 상태들 - const [paymentTermsOptions, setPaymentTermsOptions] = React.useState>([]) - const [incotermsOptions, setIncotermsOptions] = React.useState>([]) - const [shippingPlaces, setShippingPlaces] = React.useState>([]) - const [destinationPlaces, setDestinationPlaces] = React.useState>([]) - - // 품목별 견적 관련 상태 - const [prItems, setPrItems] = React.useState([]) - const [prItemQuotations, setPrItemQuotations] = React.useState([]) - const [totalAmount, setTotalAmount] = React.useState(0) - const [isSaving, setIsSaving] = React.useState(false) - - // 사전견적 폼 상태 - const [responseData, setResponseData] = React.useState({ - preQuoteAmount: '', - paymentTermsResponse: '', - taxConditionsResponse: '', - incotermsResponse: '', - proposedContractDeliveryDate: '', - proposedShippingPort: '', - proposedDestinationPort: '', - priceAdjustmentResponse: false, - isInitialResponse: false, - sparePartResponse: '', - additionalProposals: '', - isAttendingMeeting: false, - }) - - // 사전견적 참여의사 상태 - const [participationDecision, setParticipationDecision] = React.useState(null) - - // 연동제 폼 상태 - 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 || '' - - // Procurement 데이터 로드 함수들 - const loadPaymentTerms = React.useCallback(async () => { - try { - const data = await getPaymentTermsForSelection(); - setPaymentTermsOptions(data); - } catch (error) { - console.error("Failed to load payment terms:", error); - } - }, []); - - const loadIncoterms = React.useCallback(async () => { - try { - const data = await getIncotermsForSelection(); - setIncotermsOptions(data); - } catch (error) { - console.error("Failed to load incoterms:", error); - } - }, []); - - const loadShippingPlaces = React.useCallback(async () => { - try { - const data = await getPlaceOfShippingForSelection(); - setShippingPlaces(data); - } catch (error) { - console.error("Failed to load shipping places:", error); - } - }, []); - - const loadDestinationPlaces = React.useCallback(async () => { - try { - const data = await getPlaceOfDestinationForSelection(); - setDestinationPlaces(data); - } catch (error) { - console.error("Failed to load destination places:", error); - } - }, []); - - // 데이터 로드 - React.useEffect(() => { - const loadData = async () => { - try { - setIsLoading(true) - - // 모든 필요한 데이터를 병렬로 로드 - const [result, conditions, prItemsData] = await Promise.all([ - getBiddingCompaniesForPartners(biddingId, companyId), - getBiddingConditions(biddingId), - getPrItemsForBidding(biddingId) - ]) - - if (result) { - setBiddingDetail(result as BiddingDetail) - - // 저장된 품목별 견적 정보가 있으면 로드 - if (result.biddingCompanyId) { - const savedQuotations = await getSavedPrItemQuotations(result.biddingCompanyId) - setPrItemQuotations(savedQuotations) - - // 총 금액 계산 - const calculatedTotal = savedQuotations.reduce((sum: number, item: any) => sum + item.bidAmount, 0) - setTotalAmount(calculatedTotal) - - // 저장된 연동제 정보가 있으면 로드 - if (result.priceAdjustmentResponse) { - 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 ? new Date(savedPriceAdjustmentForm.referenceDate).toISOString().split('T')[0] : '', - comparisonDate: savedPriceAdjustmentForm.comparisonDate ? new Date(savedPriceAdjustmentForm.comparisonDate).toISOString().split('T')[0] : '', - adjustmentRatio: savedPriceAdjustmentForm.adjustmentRatio?.toString() || '', - notes: savedPriceAdjustmentForm.notes || '', - adjustmentConditions: savedPriceAdjustmentForm.adjustmentConditions || '', - majorNonApplicableRawMaterial: savedPriceAdjustmentForm.majorNonApplicableRawMaterial || '', - adjustmentPeriod: savedPriceAdjustmentForm.adjustmentPeriod || '', - contractorWriter: savedPriceAdjustmentForm.contractorWriter || '', - adjustmentDate: savedPriceAdjustmentForm.adjustmentDate ? new Date(savedPriceAdjustmentForm.adjustmentDate).toISOString().split('T')[0] : '', - nonApplicableReason: savedPriceAdjustmentForm.nonApplicableReason || '', - }) - } - } - } - - // 기존 응답 데이터로 폼 초기화 - setResponseData({ - preQuoteAmount: result.preQuoteAmount?.toString() || '', - paymentTermsResponse: result.paymentTermsResponse || '', - taxConditionsResponse: result.taxConditionsResponse || '', - incotermsResponse: result.incotermsResponse || '', - proposedContractDeliveryDate: result.proposedContractDeliveryDate || '', - proposedShippingPort: result.proposedShippingPort || '', - proposedDestinationPort: result.proposedDestinationPort || '', - priceAdjustmentResponse: result.priceAdjustmentResponse || false, - isInitialResponse: result.isInitialResponse || false, - sparePartResponse: result.sparePartResponse || '', - additionalProposals: result.additionalProposals || '', - isAttendingMeeting: result.isAttendingMeeting || false, - }) - - // 사전견적 참여의사 초기화 - setParticipationDecision(result.isPreQuoteParticipated) - } - - if (conditions) { - // BiddingConditionsEdit와 같은 방식으로 raw 데이터 사용 - setBiddingConditions(conditions) - } - - if (prItemsData) { - setPrItems(prItemsData) - } - - // Procurement 데이터 로드 - await Promise.all([ - loadPaymentTerms(), - loadIncoterms(), - loadShippingPlaces(), - loadDestinationPlaces() - ]) - } catch (error) { - console.error('Failed to load bidding company:', error) - toast({ - title: '오류', - description: '입찰 정보를 불러오는데 실패했습니다.', - variant: 'destructive', - }) - } finally { - setIsLoading(false) - } - } - - loadData() - }, [biddingId, companyId, toast, loadPaymentTerms, loadIncoterms, loadShippingPlaces, loadDestinationPlaces]) - - // 임시저장 기능 - const handleTempSave = () => { - if (!biddingDetail || !biddingDetail.biddingCompanyId) { - 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", - }) - router.back() - return - } - - // 사전견적 상태 체크 - const isPreQuoteStatus = biddingStatus === 'request_for_quotation' || biddingStatus === 'received_quotation' - if (!isPreQuoteStatus) { - toast({ - title: "접근 제한", - description: "사전견적 단계가 아니므로 임시저장이 불가능합니다.", - variant: "destructive", - }) - return - } - - if (!userId) { - toast({ - title: '임시저장 실패', - description: '사용자 정보를 확인할 수 없습니다. 다시 로그인해주세요.', - variant: 'destructive', - }) - return - } - - setIsSaving(true) - startTransition(async () => { - try { - const result = await savePreQuoteDraft( - biddingDetail.biddingCompanyId!, - { - prItemQuotations, - paymentTermsResponse: responseData.paymentTermsResponse, - taxConditionsResponse: responseData.taxConditionsResponse, - incotermsResponse: responseData.incotermsResponse, - proposedContractDeliveryDate: responseData.proposedContractDeliveryDate, - proposedShippingPort: responseData.proposedShippingPort, - proposedDestinationPort: responseData.proposedDestinationPort, - priceAdjustmentResponse: responseData.priceAdjustmentResponse || false, // 체크 안하면 false로 설정 - isInitialResponse: responseData.isInitialResponse || false, // 체크 안하면 false로 설정 - sparePartResponse: responseData.sparePartResponse, - additionalProposals: responseData.additionalProposals, - priceAdjustmentForm: (responseData.priceAdjustmentResponse || false) ? { - itemName: priceAdjustmentForm.itemName, - adjustmentReflectionPoint: priceAdjustmentForm.adjustmentReflectionPoint, - majorApplicableRawMaterial: priceAdjustmentForm.majorApplicableRawMaterial, - adjustmentFormula: priceAdjustmentForm.adjustmentFormula, - rawMaterialPriceIndex: priceAdjustmentForm.rawMaterialPriceIndex, - referenceDate: priceAdjustmentForm.referenceDate, - comparisonDate: priceAdjustmentForm.comparisonDate, - adjustmentRatio: priceAdjustmentForm.adjustmentRatio ? parseFloat(priceAdjustmentForm.adjustmentRatio) : undefined, - notes: priceAdjustmentForm.notes, - adjustmentConditions: priceAdjustmentForm.adjustmentConditions, - majorNonApplicableRawMaterial: priceAdjustmentForm.majorNonApplicableRawMaterial, - adjustmentPeriod: priceAdjustmentForm.adjustmentPeriod, - contractorWriter: priceAdjustmentForm.contractorWriter, - adjustmentDate: priceAdjustmentForm.adjustmentDate, - nonApplicableReason: priceAdjustmentForm.nonApplicableReason, - } : undefined - }, - userId - ) - - if (result.success) { - toast({ - title: '임시저장 완료', - description: result.message, - }) - } else { - toast({ - title: '임시저장 실패', - description: result.error, - variant: 'destructive', - }) - } - } catch (error) { - console.error('Temp save error:', error) - toast({ - title: '임시저장 실패', - description: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.', - variant: 'destructive', - }) - } finally { - setIsSaving(false) - } - }) - } - - // 사전견적 참여의사 설정 함수 - const handleParticipationDecision = async (participate: boolean) => { - if (!biddingDetail?.biddingCompanyId) return - - startTransition(async () => { - const result = await setPreQuoteParticipation( - biddingDetail.biddingCompanyId!, - participate - ) - - if (result.success) { - setParticipationDecision(participate) - toast({ - title: '설정 완료', - description: `사전견적 ${participate ? '참여' : '미참여'}로 설정되었습니다.`, - }) - } else { - toast({ - title: '설정 실패', - description: result.error, - variant: 'destructive', - }) - } - }) - } - - 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", - }) - router.back() - return - } - - // 사전견적 상태 체크 - const isPreQuoteStatus = biddingStatus === 'request_for_quotation' || biddingStatus === 'received_quotation' - if (!isPreQuoteStatus) { - toast({ - title: "접근 제한", - description: "사전견적 단계가 아니므로 견적 제출이 불가능합니다.", - variant: "destructive", - }) - return - } - - // 견적마감일 체크 - if (biddingDetail.preQuoteDeadline) { - const now = new Date() - const deadline = new Date(biddingDetail.preQuoteDeadline) - if (deadline < now) { - toast({ - title: '견적 마감', - description: '견적 마감일이 지나 제출할 수 없습니다.', - variant: 'destructive', - }) - return - } - } - - // 필수값 검증 - if (prItemQuotations.length === 0 || totalAmount === 0) { - toast({ - title: '유효성 오류', - description: '품목별 견적을 입력해주세요.', - variant: 'destructive', - }) - return - } - - // 품목별 납품일 검증 - if (prItemQuotations.length > 0) { - for (const quotation of prItemQuotations) { - if (!quotation.proposedDeliveryDate?.trim()) { - const prItem = prItems.find(item => item.id === quotation.prItemId) - toast({ - title: '유효성 오류', - description: `품목 ${prItem?.itemNumber || quotation.prItemId}의 납품예정일을 입력해주세요.`, - variant: 'destructive', - }) - return - } - } - } - - const requiredFields = [ - { value: responseData.proposedContractDeliveryDate, name: '제안 납품일' }, - { value: responseData.paymentTermsResponse, name: '응답 지급조건' }, - { value: responseData.taxConditionsResponse, name: '응답 세금조건' }, - { value: responseData.incotermsResponse, name: '응답 운송조건' }, - { value: responseData.proposedShippingPort, name: '제안 선적지' }, - { value: responseData.proposedDestinationPort, name: '제안 하역지' }, - { value: responseData.sparePartResponse, name: '스페어파트 응답' }, - ] - - const missingField = requiredFields.find(field => !field.value?.trim()) - if (missingField) { - toast({ - title: '유효성 오류', - description: `${missingField.name}을(를) 입력해주세요.`, - variant: 'destructive', - }) - return - } - - startTransition(async () => { - const submissionData = { - preQuoteAmount: totalAmount, // 품목별 계산된 총 금액 사용 - prItemQuotations, // 품목별 견적 데이터 추가 - paymentTermsResponse: responseData.paymentTermsResponse, - taxConditionsResponse: responseData.taxConditionsResponse, - incotermsResponse: responseData.incotermsResponse, - proposedContractDeliveryDate: responseData.proposedContractDeliveryDate, - proposedShippingPort: responseData.proposedShippingPort, - proposedDestinationPort: responseData.proposedDestinationPort, - priceAdjustmentResponse: responseData.priceAdjustmentResponse || false, // 체크 안하면 false로 설정 - isInitialResponse: responseData.isInitialResponse || false, // 체크 안하면 false로 설정 - sparePartResponse: responseData.sparePartResponse, - additionalProposals: responseData.additionalProposals, - priceAdjustmentForm: (responseData.priceAdjustmentResponse || false) ? { - itemName: priceAdjustmentForm.itemName, - adjustmentReflectionPoint: priceAdjustmentForm.adjustmentReflectionPoint, - majorApplicableRawMaterial: priceAdjustmentForm.majorApplicableRawMaterial, - adjustmentFormula: priceAdjustmentForm.adjustmentFormula, - rawMaterialPriceIndex: priceAdjustmentForm.rawMaterialPriceIndex, - referenceDate: priceAdjustmentForm.referenceDate, - comparisonDate: priceAdjustmentForm.comparisonDate, - adjustmentRatio: priceAdjustmentForm.adjustmentRatio ? parseFloat(priceAdjustmentForm.adjustmentRatio) : undefined, - notes: priceAdjustmentForm.notes, - adjustmentConditions: priceAdjustmentForm.adjustmentConditions, - majorNonApplicableRawMaterial: priceAdjustmentForm.majorNonApplicableRawMaterial, - adjustmentPeriod: priceAdjustmentForm.adjustmentPeriod, - contractorWriter: priceAdjustmentForm.contractorWriter, - adjustmentDate: priceAdjustmentForm.adjustmentDate, - nonApplicableReason: priceAdjustmentForm.nonApplicableReason, - } : undefined - } - - const result = await submitPreQuoteResponse( - biddingDetail.biddingCompanyId!, - submissionData, - userId - ) - - console.log('제출 결과:', result) - - if (result.success) { - toast({ - title: '성공', - description: result.message, - }) - - // 데이터 새로고침 및 폼 상태 업데이트 - const updatedDetail = await getBiddingCompaniesForPartners(biddingId, companyId) - console.log('업데이트된 데이터:', updatedDetail) - - if (updatedDetail) { - setBiddingDetail(updatedDetail as BiddingDetail) - - // 폼 상태도 업데이트된 데이터로 다시 설정 - setResponseData({ - preQuoteAmount: updatedDetail.preQuoteAmount?.toString() || '', - paymentTermsResponse: updatedDetail.paymentTermsResponse || '', - taxConditionsResponse: updatedDetail.taxConditionsResponse || '', - incotermsResponse: updatedDetail.incotermsResponse || '', - proposedContractDeliveryDate: updatedDetail.proposedContractDeliveryDate || '', - proposedShippingPort: updatedDetail.proposedShippingPort || '', - proposedDestinationPort: updatedDetail.proposedDestinationPort || '', - priceAdjustmentResponse: updatedDetail.priceAdjustmentResponse || false, - isInitialResponse: updatedDetail.isInitialResponse || false, - sparePartResponse: updatedDetail.sparePartResponse || '', - additionalProposals: updatedDetail.additionalProposals || '', - isAttendingMeeting: updatedDetail.isAttendingMeeting || false, - }) - - // 연동제 데이터도 다시 로드 - if (updatedDetail.biddingCompanyId && updatedDetail.priceAdjustmentResponse) { - const savedPriceAdjustmentForm = await getPriceAdjustmentFormByBiddingCompanyId(updatedDetail.biddingCompanyId) - if (savedPriceAdjustmentForm) { - setPriceAdjustmentForm({ - itemName: savedPriceAdjustmentForm.itemName || '', - adjustmentReflectionPoint: savedPriceAdjustmentForm.adjustmentReflectionPoint || '', - majorApplicableRawMaterial: savedPriceAdjustmentForm.majorApplicableRawMaterial || '', - adjustmentFormula: savedPriceAdjustmentForm.adjustmentFormula || '', - rawMaterialPriceIndex: savedPriceAdjustmentForm.rawMaterialPriceIndex || '', - referenceDate: savedPriceAdjustmentForm.referenceDate ? new Date(savedPriceAdjustmentForm.referenceDate).toISOString().split('T')[0] : '', - comparisonDate: savedPriceAdjustmentForm.comparisonDate ? new Date(savedPriceAdjustmentForm.comparisonDate).toISOString().split('T')[0] : '', - adjustmentRatio: savedPriceAdjustmentForm.adjustmentRatio?.toString() || '', - notes: savedPriceAdjustmentForm.notes || '', - adjustmentConditions: savedPriceAdjustmentForm.adjustmentConditions || '', - majorNonApplicableRawMaterial: savedPriceAdjustmentForm.majorNonApplicableRawMaterial || '', - adjustmentPeriod: savedPriceAdjustmentForm.adjustmentPeriod || '', - contractorWriter: savedPriceAdjustmentForm.contractorWriter || '', - adjustmentDate: savedPriceAdjustmentForm.adjustmentDate ? new Date(savedPriceAdjustmentForm.adjustmentDate).toISOString().split('T')[0] : '', - nonApplicableReason: savedPriceAdjustmentForm.nonApplicableReason || '', - }) - } - } - } - } else { - toast({ - title: '오류', - description: result.error, - variant: 'destructive', - }) - } - }) - } - - - if (isLoading) { - return ( -
-
-
-

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

-
-
- ) - } - - if (!biddingDetail) { - return ( -
-

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

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

{biddingDetail.title}

-
- - {biddingDetail.biddingNumber} - {biddingDetail.revision && biddingDetail.revision > 0 && ` Rev.${biddingDetail.revision}`} - - - {biddingStatusLabels[biddingDetail.status]} - -
-
-
- -
- - {/* 입찰 공고 섹션 */} - - - - - 입찰 공고 - - - -
-
- -
- - {biddingDetail.projectName} -
-
-
- -
- - {biddingDetail.itemName} -
-
- {/*
- -
{contractTypeLabels[biddingDetail.contractType]}
-
-
- -
{biddingTypeLabels[biddingDetail.biddingType]}
-
-
- -
{biddingDetail.awardCount === 'single' ? '단수' : '복수'}
-
*/} -
- -
- - {biddingDetail.managerName} -
-
-
- - {/* {biddingDetail.budget && ( -
- -
- - {formatCurrency(biddingDetail.budget)} -
-
- )} */} - - {/* 일정 정보 */} - {/*
- -
- {biddingDetail.submissionStartDate && biddingDetail.submissionEndDate && ( -
- 제출기간: {formatDate(biddingDetail.submissionStartDate, 'KR')} ~ {formatDate(biddingDetail.submissionEndDate, 'KR')} -
- )} - {biddingDetail.evaluationDate && ( -
- 평가일: {formatDate(biddingDetail.evaluationDate, 'KR')} -
- )} -
-
*/} - - {/* 견적마감일 정보 */} - {biddingDetail.preQuoteDeadline && ( -
- - {(() => { - const now = new Date() - const deadline = new Date(biddingDetail.preQuoteDeadline) - 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.preQuoteDeadline, 'KR')} - -
- {isExpired ? ( - - 마감됨 - - ) : daysLeft <= 1 ? ( - - {daysLeft === 0 ? `${hoursLeft}시간 남음` : `${daysLeft}일 남음`} - - ) : ( - - {daysLeft}일 남음 - - )} -
- {isExpired && ( -
- ⚠️ 견적 마감일이 지났습니다. 견적 제출이 불가능합니다. -
- )} -
- ) - })()} -
- )} -
-
- - {/* 현재 설정된 조건 섹션 */} - {biddingConditions && ( - - - 현재 설정된 입찰 조건 - - -
-
- -
-

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

-
-
- -
- -
-

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

-
-
- -
- -
-

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

-
-
- -
- -
-

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

-
-
- -
- -
-

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

-
-
- -
- -
-

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

-
-
- -
- -
-

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

-
-
- - -
- -
-

{biddingConditions.sparePartOptions}

-
-
-
-
-
- )} - - {/* 사전견적 참여의사 결정 섹션 */} - - - - - 사전견적 참여의사 결정 - - - - {participationDecision === null ? ( -
-

- 해당 입찰의 사전견적에 참여하시겠습니까? -

-
- - -
-
- ) : ( -
-
- {participationDecision ? ( - - ) : ( - - )} - - 사전견적 {participationDecision ? '참여' : '미참여'}로 설정되었습니다. - -
- {participationDecision === false && ( - <> -
-

- 미참여로 설정되어 견적 작성 섹션이 숨겨집니다. 참여하시려면 아래 버튼을 클릭해주세요. -

-
- - - - )} -
- )} -
-
- - {/* 참여 결정 시에만 견적 작성 섹션들 표시 (단, 견적마감일이 지나지 않은 경우에만) */} - {participationDecision === true && (() => { - // 견적마감일 체크 - if (biddingDetail?.preQuoteDeadline) { - const now = new Date() - const deadline = new Date(biddingDetail.preQuoteDeadline) - const isExpired = deadline < now - - if (isExpired) { - return ( - - -
- -

견적 마감

-

- 견적 마감일({formatDate(biddingDetail.preQuoteDeadline, 'KR')})이 지나 견적 제출이 불가능합니다. -

-
-
-
- ) - } - } - - return true // 견적 작성 가능 - })() && ( - <> - {/* 품목별 견적 작성 섹션 */} - {prItems.length > 0 && ( - - )} - - {/* 견적 문서 업로드 섹션 */} - - - {/* 사전견적 폼 섹션 */} - - - - - 사전견적 제출하기 - - - - {/* 총 금액 표시 (읽기 전용) */} -
-
- - -
- -
- - setResponseData({...responseData, proposedContractDeliveryDate: e.target.value})} - title={biddingConditions?.contractDeliveryDate ? `참고 납기일: ${formatDate(biddingConditions.contractDeliveryDate, 'KR')}` : "납품일을 선택하세요"} - /> - {biddingConditions?.contractDeliveryDate && ( -

- 참고 납기일: {formatDate(biddingConditions.contractDeliveryDate, 'KR')} -

- )} -
-
- -
-
- - -
- -
- - -
-
- -
-
- - -
- -
- - -
-
- -
-
- - -
- -
- - setResponseData({...responseData, sparePartResponse: e.target.value})} - placeholder={biddingConditions?.sparePartOptions ? `참고: ${biddingConditions.sparePartOptions}` : "스페어파트 관련 응답을 입력하세요"} - /> -
-
- -
- -