feat(WEB): FCM 푸시 알림, 입금 등록, 견적 저장 개선
- 수주 상세 페이지에서 수주확정 시 FCM 푸시 알림 발송 추가 - FCM 프리셋 함수 추가: 계약완료, 발주완료 알림 - 입금 등록 시 입금일, 입금계좌, 입금자명, 입금금액 입력 가능 - 견적 저장 시 토스트 메시지 정상 표시 수정 - ShipmentCreate SelectItem key prop 경고 수정 - DevToolbar 문법 오류 수정
This commit is contained in:
@@ -160,7 +160,7 @@ const CONTROLLERS = [
|
||||
|
||||
interface QuoteRegistrationProps {
|
||||
onBack: () => void;
|
||||
onSave: (quote: QuoteFormData) => Promise<void>;
|
||||
onSave: (quote: QuoteFormData) => Promise<{ success: boolean; error?: string }>;
|
||||
editingQuote?: QuoteFormData | null;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
@@ -208,15 +208,39 @@ export function QuoteRegistration({
|
||||
// DevToolbar용 폼 자동 채우기 등록
|
||||
useDevFill(
|
||||
'quote',
|
||||
useCallback(() => {
|
||||
// 실제 로드된 데이터를 기반으로 샘플 데이터 생성
|
||||
useCallback(async () => {
|
||||
// 1. 카테고리 랜덤 선택
|
||||
const categories = ['SCREEN', 'STEEL'];
|
||||
const selectedCategory = categories[Math.floor(Math.random() * categories.length)];
|
||||
|
||||
// 2. 해당 카테고리 제품 로드 (캐시에 없으면 API 호출)
|
||||
let categoryProducts = categoryProductsCache[selectedCategory];
|
||||
if (!categoryProducts) {
|
||||
try {
|
||||
const result = await getFinishedGoods(selectedCategory);
|
||||
if (result.success) {
|
||||
categoryProducts = result.data;
|
||||
setCategoryProductsCache(prev => ({
|
||||
...prev,
|
||||
[selectedCategory]: result.data
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[DevFill] 카테고리별 제품 로드 실패:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 로드된 제품 목록으로 샘플 데이터 생성
|
||||
const productsToUse = categoryProducts || finishedGoods;
|
||||
const sampleData = generateQuoteData({
|
||||
clients: clients.map(c => ({ id: c.id, name: c.vendorName })),
|
||||
products: finishedGoods.map(p => ({ code: p.item_code, name: p.item_name, category: p.category })),
|
||||
products: productsToUse.map(p => ({ code: p.item_code, name: p.item_name, category: p.category })),
|
||||
category: selectedCategory,
|
||||
});
|
||||
|
||||
setFormData(sampleData);
|
||||
toast.success('[Dev] 견적 폼이 자동으로 채워졌습니다.');
|
||||
}, [clients, finishedGoods])
|
||||
}, [clients, finishedGoods, categoryProductsCache])
|
||||
);
|
||||
|
||||
// 수량 반영 총합계 계산 (useMemo로 최적화)
|
||||
@@ -388,11 +412,12 @@ export function QuoteRegistration({
|
||||
return Object.keys(newErrors).length === 0;
|
||||
}, [formData]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleSubmit = useCallback(async (_data?: Record<string, unknown>): Promise<{ success: boolean; error?: string }> => {
|
||||
if (!validateForm()) {
|
||||
// 페이지 상단으로 스크롤
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
return;
|
||||
return { success: false, error: '입력 정보를 확인해주세요.' };
|
||||
}
|
||||
|
||||
// 에러 초기화
|
||||
@@ -405,18 +430,16 @@ export function QuoteRegistration({
|
||||
...formData,
|
||||
calculationResults: calculationResults || undefined,
|
||||
};
|
||||
await onSave(dataToSave);
|
||||
toast.success(
|
||||
editingQuote ? "견적이 수정되었습니다." : "견적이 등록되었습니다."
|
||||
);
|
||||
onBack();
|
||||
const result = await onSave(dataToSave);
|
||||
// IntegratedDetailTemplate에서 toast 처리 및 navigation 처리
|
||||
return result;
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
toast.error("저장 중 오류가 발생했습니다.");
|
||||
return { success: false, error: '저장 중 오류가 발생했습니다.' };
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
}, [formData, calculationResults, validateForm, onSave, editingQuote, onBack]);
|
||||
}, [formData, calculationResults, validateForm, onSave]);
|
||||
|
||||
const handleFieldChange = (
|
||||
field: keyof QuoteFormData,
|
||||
|
||||
Reference in New Issue
Block a user