From 088a161f8852dd7566619baca93257c0ccd901b7 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 20 Nov 2025 02:38:04 +0000 Subject: (최겸) 구매 입찰 제출기간 수정 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bidding/manage/bidding-schedule-editor.tsx | 95 ++++++++++++++++------ 1 file changed, 72 insertions(+), 23 deletions(-) (limited to 'components/bidding') diff --git a/components/bidding/manage/bidding-schedule-editor.tsx b/components/bidding/manage/bidding-schedule-editor.tsx index f2978f95..f3260f04 100644 --- a/components/bidding/manage/bidding-schedule-editor.tsx +++ b/components/bidding/manage/bidding-schedule-editor.tsx @@ -19,7 +19,7 @@ import { BiddingInvitationDialog } from '@/lib/bidding/detail/table/bidding-invi import { sendBiddingBasicContracts, getSelectedVendorsForBidding } from '@/lib/bidding/pre-quote/service' import { registerBidding } from '@/lib/bidding/detail/service' import { useToast } from '@/hooks/use-toast' - +import { format } from 'date-fns' interface BiddingSchedule { submissionStartDate?: string submissionEndDate?: string @@ -112,7 +112,7 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc }) const [isLoading, setIsLoading] = React.useState(false) const [isSubmitting, setIsSubmitting] = React.useState(false) - const [biddingInfo, setBiddingInfo] = React.useState<{ title: string; projectName?: string; status: string } | null>(null) + const [biddingInfo, setBiddingInfo] = React.useState<{ title: string; projectName?: string; status: string; biddingNumber?: string } | null>(null) const [isBiddingInvitationDialogOpen, setIsBiddingInvitationDialogOpen] = React.useState(false) const [isApprovalDialogOpen, setIsApprovalDialogOpen] = React.useState(false) const [selectedVendors, setSelectedVendors] = React.useState([]) @@ -120,6 +120,19 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc const [approvalTitle, setApprovalTitle] = React.useState('') const [invitationData, setInvitationData] = React.useState(null) + // 차수 추출 헬퍼 함수 + const getRoundNumber = (biddingNumber: string): number => { + const match = biddingNumber.match(/-(\d+)$/) + return match ? parseInt(match[1]) : 1 + } + + // 차수 증가된 입찰인지 확인 (01이 아닌 경우) + const isRoundIncreased = (biddingNumber?: string): boolean => { + if (!biddingNumber) return false + const round = getRoundNumber(biddingNumber) + return round > 1 + } + // 데이터 로딩 React.useEffect(() => { const loadSchedule = async () => { @@ -132,25 +145,12 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc title: bidding.title || '', projectName: bidding.projectName || undefined, status: bidding.status || '', + biddingNumber: bidding.biddingNumber || undefined, }) - // 날짜를 문자열로 변환하는 헬퍼 - const formatDateTime = (date: unknown): string => { - if (!date) return '' - if (typeof date === 'string') { - // 이미 datetime-local 형식인 경우 - if (date.includes('T')) { - return date.slice(0, 16) - } - return date - } - if (date instanceof Date) return date.toISOString().slice(0, 16) - return '' - } - setSchedule({ - submissionStartDate: formatDateTime(bidding.submissionStartDate), - submissionEndDate: formatDateTime(bidding.submissionEndDate), + submissionStartDate: bidding.submissionStartDate ? new Date(bidding.submissionStartDate).toISOString().slice(0, 16) : '', + submissionEndDate: bidding.submissionEndDate ? new Date(bidding.submissionEndDate).toISOString().slice(0, 16) : '', remarks: bidding.remarks || '', isUrgent: bidding.isUrgent || false, hasSpecificationMeeting: bidding.hasSpecificationMeeting || false, @@ -286,6 +286,48 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc } } + // 차수 증가된 입찰의 직접 입찰공고 함수 (결재 생략) + const handleDirectBiddingInvitation = async (data: BiddingInvitationData, vendors: VendorContractRequirement[]) => { + try { + if (!session?.user?.id) { + toast({ + title: '오류', + description: '사용자 정보가 없습니다.', + variant: 'destructive', + }) + return + } + + // 입찰 등록 (결재 생략) + const result = await registerBidding(biddingId, session.user.id.toString()) + + if (result.success) { + toast({ + title: '입찰공고 완료', + description: '차수 증가된 입찰이 성공적으로 공고되었습니다.', + }) + + // 다이얼로그 닫기 및 페이지 새로고침 + setIsBiddingInvitationDialogOpen(false) + setInvitationData(null) + router.refresh() + } else { + toast({ + title: '오류', + description: result.error || '입찰공고 중 오류가 발생했습니다.', + variant: 'destructive', + }) + } + } catch (error) { + console.error('직접 입찰공고 중 오류 발생:', error) + toast({ + title: '오류', + description: '입찰공고 중 오류가 발생했습니다.', + variant: 'destructive', + }) + } + } + // 입찰 초대 발송 핸들러 - 결재 준비 및 결재 다이얼로그 열기 const handleBiddingInvitationSend = async (data: BiddingInvitationData) => { try { @@ -309,7 +351,14 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc return } - // 결재 데이터 준비 (템플릿 변수, 제목 등) + // 차수 증가된 입찰(02, 03 등)인지 확인 + if (isRoundIncreased(biddingInfo?.biddingNumber)) { + // 차수 증가된 입찰은 결재 생략하고 바로 입찰 등록 + await handleDirectBiddingInvitation(data, vendors) + return + } + + // 일반 입찰의 경우 결재 데이터 준비 (템플릿 변수, 제목 등) const approvalData = await prepareBiddingApprovalData({ biddingId, vendors, @@ -435,7 +484,7 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc handleScheduleChange('submissionStartDate', e.target.value)} /> @@ -444,7 +493,7 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc handleScheduleChange('submissionEndDate', e.target.value)} /> @@ -613,7 +662,7 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc 입찰서 제출 기간: {schedule.submissionStartDate && schedule.submissionEndDate - ? `${new Date(schedule.submissionStartDate).toLocaleString('ko-KR')} ~ ${new Date(schedule.submissionEndDate).toLocaleString('ko-KR')}` + ? `${format(new Date(schedule.submissionStartDate), "yyyy-MM-dd HH:mm")} ~ ${format(new Date(schedule.submissionEndDate), "yyyy-MM-dd HH:mm")}` : '미설정' } @@ -634,7 +683,7 @@ export function BiddingScheduleEditor({ biddingId, readonly = false }: BiddingSc
사양설명회 일시: - {new Date(specMeetingInfo.meetingDate).toLocaleString('ko-KR')} + {format(new Date(specMeetingInfo.meetingDate), "yyyy-MM-dd HH:mm")}
)} -- cgit v1.2.3