From 9fb5c171ebeb4e3d349e40db823af369d6675572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Fri, 23 Jan 2026 21:09:18 +0900 Subject: [PATCH] =?UTF-8?q?fix(WEB):=20=EA=B2=AC=EC=A0=81=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EA=B2=AC=EC=A0=81=EC=99=84?= =?UTF-8?q?=EB=A3=8C=EC=9D=BC=20=ED=91=9C=EC=8B=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 견적 상세 페이지에서 상태 변경 가능하도록 Select 컴포넌트 추가 - 견적완료일(completedDate) 필드를 FormData 타입에 추가 - 견적완료 상태일 때 완료일자 표시 - 저장 시 상태를 강제로 'completed'로 변경하던 로직 제거 - 목록 페이지에서 견적완료일이 표시되지 않던 문제 수정 - updated_at을 완료 상태의 completedDate로 사용 - QuantityInput 컴포넌트 controlled component 에러 수정 - defaultValue props 분리하여 spread 방지 --- .../estimates/EstimateDetailForm.tsx | 4 +- .../construction/estimates/actions.ts | 12 ++++- .../sections/EstimateInfoSection.tsx | 49 +++++++++++++++---- .../business/construction/estimates/types.ts | 3 ++ src/components/ui/quantity-input.tsx | 2 + 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/components/business/construction/estimates/EstimateDetailForm.tsx b/src/components/business/construction/estimates/EstimateDetailForm.tsx index 2284a569..d70a718b 100644 --- a/src/components/business/construction/estimates/EstimateDetailForm.tsx +++ b/src/components/business/construction/estimates/EstimateDetailForm.tsx @@ -113,11 +113,11 @@ export default function EstimateDetailForm({ console.log('🔍 [handleConfirmSave] formData.priceAdjustmentData:', formData.priceAdjustmentData); console.log('🔍 [handleConfirmSave] formData 전체:', formData); - // 현재 사용자 이름을 견적자로 설정하고, 상태를 견적완료로 변경하여 저장 + // 현재 사용자 이름을 견적자로 설정하고 저장 (상태는 사용자 선택값 유지) const result = await updateEstimate(estimateId, { ...formData, estimatorName: currentUser!.name, - status: 'completed', // 저장 시 견적완료 상태로 변경 (입찰에서 조회 가능) + // status는 formData에 포함되어 있으므로 사용자가 선택한 값 그대로 전송 }); if (result.success) { diff --git a/src/components/business/construction/estimates/actions.ts b/src/components/business/construction/estimates/actions.ts index 718e1725..9c93703d 100644 --- a/src/components/business/construction/estimates/actions.ts +++ b/src/components/business/construction/estimates/actions.ts @@ -247,6 +247,14 @@ interface ApiBidDocument { * 기존 프론트엔드 타입과 호환성 유지 */ function transformQuoteToEstimate(apiData: ApiQuote): Estimate { + const status = mapQuoteStatusToEstimateStatus(apiData.status); + + // 완료 상태인 경우 updated_at을 완료일로 사용 + // (상태가 완료로 변경될 때 updated_at이 갱신되므로) + const completedDate = status === 'completed' && apiData.updated_at + ? apiData.updated_at.split('T')[0] // ISO 형식에서 날짜만 추출 + : null; + return { id: String(apiData.id), estimateCode: apiData.quote_number || '', @@ -259,9 +267,9 @@ function transformQuoteToEstimate(apiData: ApiQuote): Estimate { estimateCompanyManagerContact: '', // API에서 제공 시 매핑 필요 itemCount: apiData.items?.length || 0, estimateAmount: Number(apiData.total_amount) || 0, - completedDate: null, + completedDate, bidDate: apiData.registration_date || null, - status: mapQuoteStatusToEstimateStatus(apiData.status), + status, createdAt: apiData.created_at || '', updatedAt: apiData.updated_at || '', createdBy: apiData.created_by ? String(apiData.created_by) : '', diff --git a/src/components/business/construction/estimates/sections/EstimateInfoSection.tsx b/src/components/business/construction/estimates/sections/EstimateInfoSection.tsx index 0b5a556b..edb16fb5 100644 --- a/src/components/business/construction/estimates/sections/EstimateInfoSection.tsx +++ b/src/components/business/construction/estimates/sections/EstimateInfoSection.tsx @@ -15,8 +15,8 @@ import { } from '@/components/ui/select'; import { FileDropzone } from '@/components/ui/file-dropzone'; import { FileList, type ExistingFile } from '@/components/ui/file-list'; -import type { EstimateDetailFormData } from '../types'; -import { STATUS_STYLES, STATUS_LABELS, VAT_TYPE_OPTIONS } from '../types'; +import type { EstimateDetailFormData, EstimateStatus } from '../types'; +import { STATUS_STYLES, STATUS_LABELS, VAT_TYPE_OPTIONS, ESTIMATE_STATUS_OPTIONS } from '../types'; import { formatAmount } from '../utils'; interface EstimateInfoSectionProps { @@ -66,8 +66,8 @@ export function EstimateInfoSection({ - {/* 3행: 견적금액, 상태 */} -
+ {/* 3행: 견적금액, 상태, 완료일자 */} +
-
- - {STATUS_LABELS[formData.status]} - -
+ {isViewMode ? ( +
+ + {STATUS_LABELS[formData.status]} + +
+ ) : ( + + )}
+ {/* 완료일자: 견적완료 상태일 때만 표시 */} + {formData.status === 'completed' && formData.completedDate && ( +
+ + +
+ )}
diff --git a/src/components/business/construction/estimates/types.ts b/src/components/business/construction/estimates/types.ts index f008ab8a..d95bc6e8 100644 --- a/src/components/business/construction/estimates/types.ts +++ b/src/components/business/construction/estimates/types.ts @@ -192,6 +192,7 @@ export interface EstimateDetailFormData { estimateCompanyManagerContact: string; // 견적 회사 담당자 연락처 estimateAmount: number; status: EstimateStatus; + completedDate: string | null; // 견적완료일 // 현장설명회 정보 siteBriefing: SiteBriefingInfo; @@ -273,6 +274,7 @@ export function getEmptyEstimateDetailFormData(): EstimateDetailFormData { estimateCompanyManagerContact: '', estimateAmount: 0, status: 'pending', + completedDate: null, siteBriefing: { briefingCode: '', partnerName: '', @@ -314,6 +316,7 @@ export function estimateDetailToFormData(detail: EstimateDetail): EstimateDetail estimateCompanyManagerContact: detail.estimateCompanyManagerContact || '', estimateAmount: detail.estimateAmount, status: detail.status, + completedDate: detail.completedDate || null, siteBriefing: { ...detail.siteBriefing, briefingDate: normalizeDateValue(detail.siteBriefing?.briefingDate), diff --git a/src/components/ui/quantity-input.tsx b/src/components/ui/quantity-input.tsx index af7884b1..f1039613 100644 --- a/src/components/ui/quantity-input.tsx +++ b/src/components/ui/quantity-input.tsx @@ -63,6 +63,8 @@ const QuantityInput = React.forwardRef( disabled, onFocus, onBlur, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + defaultValue: _defaultValue, // controlled component이므로 defaultValue 무시 ...props }, ref