From d54309aa92805f5ea23e8f47e565151e8b6b3735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=98=81=EC=84=B1?= Date: Tue, 27 Jan 2026 15:15:08 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EA=B2=AC=EC=A0=81=20=ED=99=95=EC=A0=95?= =?UTF-8?q?=20=EC=8B=9C=20finalize=20API=20=ED=98=B8=EC=B6=9C=20=EB=B0=8F?= =?UTF-8?q?=20=EB=9D=BC=EB=B2=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 견적 확정 시 updateQuote + finalizeQuote 순차 호출 - is_final 플래그가 정상적으로 설정되어 목록에서 "최종확정" 뱃지 표시 - 버튼 라벨: 임시저장 → 저장, 최종저장 → 견적 확정 - 뱃지 라벨: 최종저장 → 견적 확정, 임시저장 → 저장됨 - toast 메시지 업데이트 --- .../sales/quote-management/[id]/page.tsx | 54 ++++++++++++++----- src/components/quotes/QuoteRegistrationV2.tsx | 4 +- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx b/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx index 7c6e8872..db4eb694 100644 --- a/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx +++ b/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx @@ -16,7 +16,7 @@ import { IntegratedDetailTemplate } from "@/components/templates/IntegratedDetai import { quoteConfig } from "@/components/quotes/quoteConfig"; import { Badge } from "@/components/ui/badge"; import { toast } from "sonner"; -import { getQuoteById, updateQuote, calculateBomBulk, BomCalculateItem } from "@/components/quotes/actions"; +import { getQuoteById, updateQuote, finalizeQuote, calculateBomBulk, BomCalculateItem } from "@/components/quotes/actions"; import { transformApiToV2, transformV2ToApi } from "@/components/quotes/types"; export default function QuoteDetailPage() { @@ -113,6 +113,16 @@ export default function QuoteDetailPage() { router.push("/sales/quote-management"); }, [router]); + // 수정 모드로 전환 핸들러 + const handleEdit = useCallback(() => { + router.push(`/sales/quote-management/${quoteId}?mode=edit`); + }, [router, quoteId]); + + // 수주등록 페이지로 이동 핸들러 + const handleOrderRegister = useCallback(() => { + router.push(`/sales/order-management-sales/new?quoteId=${quoteId}`); + }, [router, quoteId]); + // 수정 저장 핸들러 const handleSave = useCallback(async (data: QuoteFormDataV2, saveType: "temporary" | "final") => { setIsSaving(true); @@ -123,26 +133,44 @@ export default function QuoteDetailPage() { console.log("[QuoteDetailPage] 수정 데이터:", apiData); console.log("[QuoteDetailPage] 저장 타입:", saveType); + console.log("[QuoteDetailPage] 현재 모드:", isEditMode ? "edit" : "view"); - // API 호출 - const result = await updateQuote(quoteId, apiData); + // 1. 먼저 데이터 저장 + const updateResult = await updateQuote(quoteId, apiData); - if (!result.success) { - toast.error(result.error || "저장 중 오류가 발생했습니다."); + if (!updateResult.success) { + toast.error(updateResult.error || "저장 중 오류가 발생했습니다."); return; } - toast.success(`${saveType === "temporary" ? "임시" : "최종"} 저장 완료`); + // 2. 견적 확정인 경우 finalize API 호출 + if (saveType === "final") { + const finalizeResult = await finalizeQuote(quoteId); - // 저장 후 view 모드로 전환 - router.push(`/sales/quote-management/${quoteId}`); + if (!finalizeResult.success) { + toast.error(finalizeResult.error || "견적 확정 중 오류가 발생했습니다."); + return; + } + + toast.success("견적이 확정되었습니다."); + } else { + toast.success("저장되었습니다."); + } + + if (isEditMode) { + // edit 모드에서 저장 후 view 모드로 전환 + router.push(`/sales/quote-management/${quoteId}`); + } else { + // view 모드에서 최종저장 후 페이지 새로고침 (데이터 갱신) + router.refresh(); + } } catch (error) { console.error("[QuoteDetailPage] 저장 오류:", error); toast.error("저장 중 오류가 발생했습니다."); } finally { setIsSaving(false); } - }, [router, quoteId]); + }, [router, quoteId, isEditMode]); // 동적 config (모드별 타이틀) const dynamicConfig = useMemo(() => { @@ -158,7 +186,7 @@ export default function QuoteDetailPage() { if (!quote) return null; return ( - {quote.status === "final" ? "최종저장" : quote.status === "temporary" ? "임시저장" : "작성중"} + {quote.status === "final" ? "견적 확정" : quote.status === "temporary" ? "저장됨" : "작성중"} ); }, [quote]); @@ -169,13 +197,15 @@ export default function QuoteDetailPage() { ); - }, [isEditMode, handleBack, handleSave, quote, isSaving]); + }, [isEditMode, handleBack, handleSave, handleEdit, handleOrderRegister, quote, isSaving]); // IntegratedDetailTemplate 사용 return ( diff --git a/src/components/quotes/QuoteRegistrationV2.tsx b/src/components/quotes/QuoteRegistrationV2.tsx index 50728e23..ddfb51cf 100644 --- a/src/components/quotes/QuoteRegistrationV2.tsx +++ b/src/components/quotes/QuoteRegistrationV2.tsx @@ -619,7 +619,7 @@ export function QuoteRegistrationV2({ status: saveType === "temporary" ? "temporary" : "final", }; await onSave(dataToSave, saveType); - toast.success(saveType === "temporary" ? "임시 저장되었습니다." : "최종 저장되었습니다."); + toast.success(saveType === "temporary" ? "저장되었습니다." : "견적이 확정되었습니다."); } catch (error) { if (isNextRedirectError(error)) throw error; toast.error("저장 중 오류가 발생했습니다."); @@ -647,7 +647,7 @@ export function QuoteRegistrationV2({ {pageTitle} - {formData.status === "final" ? "최종저장" : formData.status === "temporary" ? "임시저장" : "작성중"} + {formData.status === "final" ? "견적 확정" : formData.status === "temporary" ? "저장됨" : "작성중"} )}