fix: 견적 확정 시 finalize API 호출 및 라벨 수정
- 견적 확정 시 updateQuote + finalizeQuote 순차 호출 - is_final 플래그가 정상적으로 설정되어 목록에서 "최종확정" 뱃지 표시 - 버튼 라벨: 임시저장 → 저장, 최종저장 → 견적 확정 - 뱃지 라벨: 최종저장 → 견적 확정, 임시저장 → 저장됨 - toast 메시지 업데이트
This commit is contained in:
@@ -16,7 +16,7 @@ import { IntegratedDetailTemplate } from "@/components/templates/IntegratedDetai
|
|||||||
import { quoteConfig } from "@/components/quotes/quoteConfig";
|
import { quoteConfig } from "@/components/quotes/quoteConfig";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { toast } from "sonner";
|
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";
|
import { transformApiToV2, transformV2ToApi } from "@/components/quotes/types";
|
||||||
|
|
||||||
export default function QuoteDetailPage() {
|
export default function QuoteDetailPage() {
|
||||||
@@ -113,6 +113,16 @@ export default function QuoteDetailPage() {
|
|||||||
router.push("/sales/quote-management");
|
router.push("/sales/quote-management");
|
||||||
}, [router]);
|
}, [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") => {
|
const handleSave = useCallback(async (data: QuoteFormDataV2, saveType: "temporary" | "final") => {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
@@ -123,26 +133,44 @@ export default function QuoteDetailPage() {
|
|||||||
|
|
||||||
console.log("[QuoteDetailPage] 수정 데이터:", apiData);
|
console.log("[QuoteDetailPage] 수정 데이터:", apiData);
|
||||||
console.log("[QuoteDetailPage] 저장 타입:", saveType);
|
console.log("[QuoteDetailPage] 저장 타입:", saveType);
|
||||||
|
console.log("[QuoteDetailPage] 현재 모드:", isEditMode ? "edit" : "view");
|
||||||
|
|
||||||
// API 호출
|
// 1. 먼저 데이터 저장
|
||||||
const result = await updateQuote(quoteId, apiData);
|
const updateResult = await updateQuote(quoteId, apiData);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!updateResult.success) {
|
||||||
toast.error(result.error || "저장 중 오류가 발생했습니다.");
|
toast.error(updateResult.error || "저장 중 오류가 발생했습니다.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
toast.success(`${saveType === "temporary" ? "임시" : "최종"} 저장 완료`);
|
// 2. 견적 확정인 경우 finalize API 호출
|
||||||
|
if (saveType === "final") {
|
||||||
|
const finalizeResult = await finalizeQuote(quoteId);
|
||||||
|
|
||||||
// 저장 후 view 모드로 전환
|
if (!finalizeResult.success) {
|
||||||
router.push(`/sales/quote-management/${quoteId}`);
|
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) {
|
} catch (error) {
|
||||||
console.error("[QuoteDetailPage] 저장 오류:", error);
|
console.error("[QuoteDetailPage] 저장 오류:", error);
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
toast.error("저장 중 오류가 발생했습니다.");
|
||||||
} finally {
|
} finally {
|
||||||
setIsSaving(false);
|
setIsSaving(false);
|
||||||
}
|
}
|
||||||
}, [router, quoteId]);
|
}, [router, quoteId, isEditMode]);
|
||||||
|
|
||||||
// 동적 config (모드별 타이틀)
|
// 동적 config (모드별 타이틀)
|
||||||
const dynamicConfig = useMemo(() => {
|
const dynamicConfig = useMemo(() => {
|
||||||
@@ -158,7 +186,7 @@ export default function QuoteDetailPage() {
|
|||||||
if (!quote) return null;
|
if (!quote) return null;
|
||||||
return (
|
return (
|
||||||
<Badge variant={quote.status === "final" ? "default" : quote.status === "temporary" ? "secondary" : "outline"}>
|
<Badge variant={quote.status === "final" ? "default" : quote.status === "temporary" ? "secondary" : "outline"}>
|
||||||
{quote.status === "final" ? "최종저장" : quote.status === "temporary" ? "임시저장" : "작성중"}
|
{quote.status === "final" ? "견적 확정" : quote.status === "temporary" ? "저장됨" : "작성중"}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
}, [quote]);
|
}, [quote]);
|
||||||
@@ -169,13 +197,15 @@ export default function QuoteDetailPage() {
|
|||||||
<QuoteRegistrationV2
|
<QuoteRegistrationV2
|
||||||
mode={isEditMode ? "edit" : "view"}
|
mode={isEditMode ? "edit" : "view"}
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onSave={isEditMode ? handleSave : undefined}
|
onSave={handleSave}
|
||||||
|
onEdit={handleEdit}
|
||||||
|
onOrderRegister={handleOrderRegister}
|
||||||
initialData={quote}
|
initialData={quote}
|
||||||
isLoading={isSaving}
|
isLoading={isSaving}
|
||||||
hideHeader={true}
|
hideHeader={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [isEditMode, handleBack, handleSave, quote, isSaving]);
|
}, [isEditMode, handleBack, handleSave, handleEdit, handleOrderRegister, quote, isSaving]);
|
||||||
|
|
||||||
// IntegratedDetailTemplate 사용
|
// IntegratedDetailTemplate 사용
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ export function QuoteRegistrationV2({
|
|||||||
status: saveType === "temporary" ? "temporary" : "final",
|
status: saveType === "temporary" ? "temporary" : "final",
|
||||||
};
|
};
|
||||||
await onSave(dataToSave, saveType);
|
await onSave(dataToSave, saveType);
|
||||||
toast.success(saveType === "temporary" ? "임시 저장되었습니다." : "최종 저장되었습니다.");
|
toast.success(saveType === "temporary" ? "저장되었습니다." : "견적이 확정되었습니다.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isNextRedirectError(error)) throw error;
|
if (isNextRedirectError(error)) throw error;
|
||||||
toast.error("저장 중 오류가 발생했습니다.");
|
toast.error("저장 중 오류가 발생했습니다.");
|
||||||
@@ -647,7 +647,7 @@ export function QuoteRegistrationV2({
|
|||||||
{pageTitle}
|
{pageTitle}
|
||||||
</h1>
|
</h1>
|
||||||
<Badge variant={formData.status === "final" ? "default" : formData.status === "temporary" ? "secondary" : "outline"}>
|
<Badge variant={formData.status === "final" ? "default" : formData.status === "temporary" ? "secondary" : "outline"}>
|
||||||
{formData.status === "final" ? "최종저장" : formData.status === "temporary" ? "임시저장" : "작성중"}
|
{formData.status === "final" ? "견적 확정" : formData.status === "temporary" ? "저장됨" : "작성중"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user