fix: 견적 확정 시 finalize API 호출 및 라벨 수정

- 견적 확정 시 updateQuote + finalizeQuote 순차 호출
- is_final 플래그가 정상적으로 설정되어 목록에서 "최종확정" 뱃지 표시
- 버튼 라벨: 임시저장 → 저장, 최종저장 → 견적 확정
- 뱃지 라벨: 최종저장 → 견적 확정, 임시저장 → 저장됨
- toast 메시지 업데이트
This commit is contained in:
2026-01-27 15:15:08 +09:00
parent 2a92f48a2c
commit d54309aa92
2 changed files with 44 additions and 14 deletions

View File

@@ -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 (
<Badge variant={quote.status === "final" ? "default" : quote.status === "temporary" ? "secondary" : "outline"}>
{quote.status === "final" ? "최종저장" : quote.status === "temporary" ? "임시저장" : "작성중"}
{quote.status === "final" ? "견적 확정" : quote.status === "temporary" ? "저장" : "작성중"}
</Badge>
);
}, [quote]);
@@ -169,13 +197,15 @@ export default function QuoteDetailPage() {
<QuoteRegistrationV2
mode={isEditMode ? "edit" : "view"}
onBack={handleBack}
onSave={isEditMode ? handleSave : undefined}
onSave={handleSave}
onEdit={handleEdit}
onOrderRegister={handleOrderRegister}
initialData={quote}
isLoading={isSaving}
hideHeader={true}
/>
);
}, [isEditMode, handleBack, handleSave, quote, isSaving]);
}, [isEditMode, handleBack, handleSave, handleEdit, handleOrderRegister, quote, isSaving]);
// IntegratedDetailTemplate 사용
return (

View File

@@ -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}
</h1>
<Badge variant={formData.status === "final" ? "default" : formData.status === "temporary" ? "secondary" : "outline"}>
{formData.status === "final" ? "최종저장" : formData.status === "temporary" ? "임시저장" : "작성중"}
{formData.status === "final" ? "견적 확정" : formData.status === "temporary" ? "저장" : "작성중"}
</Badge>
</div>
)}