feat(WEB): FCM 푸시 알림, 입금 등록, 견적 저장 개선
- 수주 상세 페이지에서 수주확정 시 FCM 푸시 알림 발송 추가 - FCM 프리셋 함수 추가: 계약완료, 발주완료 알림 - 입금 등록 시 입금일, 입금계좌, 입금자명, 입금금액 입력 가능 - 견적 저장 시 토스트 메시지 정상 표시 수정 - ShipmentCreate SelectItem key prop 경고 수정 - DevToolbar 문법 오류 수정
This commit is contained in:
@@ -67,6 +67,7 @@ import {
|
||||
type Order,
|
||||
type OrderStatus,
|
||||
} from "@/components/orders";
|
||||
import { sendSalesOrderNotification } from "@/lib/actions/fcm";
|
||||
|
||||
|
||||
// 상태 뱃지 헬퍼
|
||||
@@ -214,6 +215,20 @@ export default function OrderDetailPage() {
|
||||
setOrder(result.data);
|
||||
toast.success("수주가 확정되었습니다.");
|
||||
setIsConfirmDialogOpen(false);
|
||||
|
||||
// FCM 푸시 알림 발송
|
||||
try {
|
||||
const fcmResult = await sendSalesOrderNotification({
|
||||
body: `${order.client} - ${order.siteName} 수주가 확정되었습니다.`,
|
||||
});
|
||||
if (fcmResult.success) {
|
||||
console.log(`[FCM] 수주확정 알림 발송 성공 (${fcmResult.sentCount || 0}건)`);
|
||||
} else {
|
||||
console.warn('[FCM] 수주확정 알림 발송 실패:', fcmResult.error);
|
||||
}
|
||||
} catch (fcmError) {
|
||||
console.error('[FCM] 수주확정 알림 발송 오류:', fcmError);
|
||||
}
|
||||
} else {
|
||||
toast.error(result.error || "수주 확정에 실패했습니다.");
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ export default function QuoteDetailPage() {
|
||||
};
|
||||
|
||||
// V2 패턴: 수정 저장 핸들러
|
||||
const handleSave = async (formData: QuoteFormData) => {
|
||||
if (isSaving) return;
|
||||
const handleSave = async (formData: QuoteFormData): Promise<{ success: boolean; error?: string }> => {
|
||||
if (isSaving) return { success: false, error: '저장 중입니다.' };
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
@@ -160,13 +160,14 @@ export default function QuoteDetailPage() {
|
||||
const result = await updateQuote(quoteId, apiData as any);
|
||||
|
||||
if (result.success) {
|
||||
toast.success("견적이 수정되었습니다.");
|
||||
// toast는 IntegratedDetailTemplate에서 처리
|
||||
router.push(`/sales/quote-management/${quoteId}`);
|
||||
return { success: true };
|
||||
} else {
|
||||
toast.error(result.error || "견적 수정에 실패했습니다.");
|
||||
return { success: false, error: result.error || "견적 수정에 실패했습니다." };
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("견적 수정에 실패했습니다.");
|
||||
return { success: false, error: "견적 수정에 실패했습니다." };
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ export default function QuoteNewPage() {
|
||||
router.push("/sales/quote-management");
|
||||
};
|
||||
|
||||
const handleSave = async (formData: QuoteFormData) => {
|
||||
if (isSaving) return;
|
||||
const handleSave = async (formData: QuoteFormData): Promise<{ success: boolean; error?: string }> => {
|
||||
if (isSaving) return { success: false, error: '저장 중입니다.' };
|
||||
setIsSaving(true);
|
||||
|
||||
try {
|
||||
@@ -45,13 +45,14 @@ export default function QuoteNewPage() {
|
||||
const result = await createQuote(apiData as any);
|
||||
|
||||
if (result.success && result.data) {
|
||||
toast.success("견적이 등록되었습니다.");
|
||||
// toast는 IntegratedDetailTemplate에서 처리
|
||||
router.push(`/sales/quote-management/${result.data.id}`);
|
||||
return { success: true };
|
||||
} else {
|
||||
toast.error(result.error || "견적 등록에 실패했습니다.");
|
||||
return { success: false, error: result.error || "견적 등록에 실패했습니다." };
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("견적 등록에 실패했습니다.");
|
||||
return { success: false, error: "견적 등록에 실패했습니다." };
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user