[251101] fix: CheckOutPanel Mock-2
🕐 커밋 시간: 2025. 11. 01. 21:01:06 📊 변경 통계: • 총 파일: 10개 • 추가: +617줄 • 삭제: -142줄 📁 추가된 파일: + com.twin.app.shoptime/src/utils/mockDataSafetyUtils.js 📝 수정된 파일: ~ com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js ~ com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/container/OrderItemsSideBar.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/container/SummaryContainerMock.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less ~ com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/components/FavoriteBtn.module.less ~ com.twin.app.shoptime/src/views/ErrorBoundary.js 🔧 주요 변경 내용: • 공통 유틸리티 함수 최적화 • UI 컴포넌트 아키텍처 개선 • 오류 처리 로직 개선 • 대규모 기능 개발 • 모듈 구조 개선
This commit is contained in:
@@ -110,23 +110,30 @@ const BuyOption = ({
|
||||
|
||||
// Mock Mode에서 옵션 데이터 처리
|
||||
const productOptionInfos = useMemo(() => {
|
||||
console.log('[BuyOption] productOptionInfos useMemo - isMockMode:', BUYNOW_CONFIG.isMockMode());
|
||||
console.log('[BuyOption] productOptionInfos useMemo - reduxProductOptionInfos:', reduxProductOptionInfos);
|
||||
|
||||
// API Mode: 기존 로직 100% 유지
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
console.log('[BuyOption] API Mode - using reduxProductOptionInfos');
|
||||
return reduxProductOptionInfos;
|
||||
}
|
||||
|
||||
// Mock Mode: 옵션 데이터가 없으면 Mock 데이터 생성
|
||||
if (!reduxProductOptionInfos || reduxProductOptionInfos.length === 0) {
|
||||
// Mock Mode: 옵션 데이터가 없거나 배열이 아니면 Mock 데이터 생성
|
||||
const isValidReduxData = Array.isArray(reduxProductOptionInfos) && reduxProductOptionInfos.length > 0;
|
||||
|
||||
if (!isValidReduxData) {
|
||||
console.log('[BuyOption] Mock Mode - generating mock option data (reduxData invalid)');
|
||||
const mockOptionData = createMockProductOptionData(productData);
|
||||
// Mock 옵션 데이터 배열 반환 (기존 구조와 호환)
|
||||
return mockOptionData?.optionList || [];
|
||||
console.log('[BuyOption] Mock Mode - createMockProductOptionData result:', mockOptionData);
|
||||
// Mock 옵션 데이터는 이미 배열 구조로 반환됨
|
||||
return mockOptionData || [];
|
||||
}
|
||||
|
||||
// Mock Mode이고 옵션 데이터가 있으면 그대로 사용
|
||||
// Mock Mode이고 유효한 옵션 데이터가 있으면 그대로 사용
|
||||
console.log('[BuyOption] Mock Mode - using existing valid reduxProductOptionInfos');
|
||||
return reduxProductOptionInfos;
|
||||
}, [reduxProductOptionInfos, productData]);
|
||||
|
||||
// logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
|
||||
}, [reduxProductOptionInfos, productData]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
|
||||
const logInfo = useMemo(() => {
|
||||
if (productData) {
|
||||
// productData가 있으면 SingleOption과 동일하게 처리
|
||||
@@ -222,17 +229,50 @@ const BuyOption = ({
|
||||
}, [selectedIndex, productOptionInfos, type]);
|
||||
|
||||
// 옵션 자동 선택 로직 (SingleOption과 동일)
|
||||
// Mock Mode: 항상 첫 번째 옵션을 자동으로 선택
|
||||
useEffect(() => {
|
||||
if (
|
||||
productOptionInfos &&
|
||||
selectedBtnOptIdx >= 0 &&
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length === 1 &&
|
||||
!isOptionValue
|
||||
) {
|
||||
setSelectedOptions(productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl[0]);
|
||||
console.log('[BuyOption] autoSelect useEffect - productOptionInfos:', productOptionInfos);
|
||||
console.log('[BuyOption] autoSelect useEffect - selectedBtnOptIdx:', selectedBtnOptIdx);
|
||||
console.log('[BuyOption] autoSelect useEffect - isOptionValue:', isOptionValue);
|
||||
|
||||
if (!productOptionInfos || productOptionInfos.length === 0) {
|
||||
console.log('[BuyOption] autoSelect - productOptionInfos is empty, returning');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentOptionGroup = productOptionInfos[selectedBtnOptIdx];
|
||||
if (!currentOptionGroup) {
|
||||
console.log('[BuyOption] autoSelect - currentOptionGroup is not found at index:', selectedBtnOptIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
const optionDetails = currentOptionGroup.prdtOptDtl;
|
||||
console.log('[BuyOption] autoSelect - optionDetails:', optionDetails);
|
||||
|
||||
if (!optionDetails || optionDetails.length === 0) {
|
||||
console.log('[BuyOption] autoSelect - optionDetails is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 선택되었으면 스킵
|
||||
if (isOptionValue) {
|
||||
console.log('[BuyOption] autoSelect - already selected, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Mock Mode: 첫 번째 옵션 자동 선택
|
||||
if (BUYNOW_CONFIG.isMockMode()) {
|
||||
console.log('[BuyOption] Mock Mode - Auto selecting first option:', optionDetails[0]);
|
||||
setSelectedOptions(optionDetails[0]);
|
||||
setIsOptionValue(true);
|
||||
}
|
||||
}, [productOptionInfos, selectedBtnOptIdx, isOptionValue]);
|
||||
// API Mode: 옵션이 1개일 때만 자동 선택
|
||||
else if (optionDetails.length === 1) {
|
||||
console.log('[BuyOption] API Mode - Auto selecting only option:', optionDetails[0]);
|
||||
setSelectedOptions(optionDetails[0]);
|
||||
setIsOptionValue(true);
|
||||
}
|
||||
}, [productOptionInfos, selectedBtnOptIdx]);
|
||||
|
||||
// 필수 데이터 로드 (SingleOption과 동일)
|
||||
// Mock Mode: API 호출 스킵 (Mock 데이터만 사용)
|
||||
@@ -320,17 +360,17 @@ const BuyOption = ({
|
||||
}
|
||||
} else if (response.retCode === 1001) {
|
||||
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
|
||||
dispatch(changeAppStatus({ isLoading: false }));
|
||||
} else {
|
||||
dispatch(changeAppStatus({ isLoading: false }));
|
||||
} else {
|
||||
dispatch(
|
||||
showError(
|
||||
response.retCode,
|
||||
response.retMsg,
|
||||
false,
|
||||
response.retMsg,
|
||||
false,
|
||||
response.retDetailCode,
|
||||
response.returnBindStrings
|
||||
)
|
||||
);
|
||||
);
|
||||
dispatch(changeAppStatus({ isLoading: false }));
|
||||
return;
|
||||
}
|
||||
@@ -369,18 +409,18 @@ const BuyOption = ({
|
||||
productOptionInfos.length > 1 &&
|
||||
productInfo?.optProdYn === 'Y'
|
||||
) {
|
||||
|
||||
|
||||
if (selectFirstOptionIndex === 0) {
|
||||
dispatch(
|
||||
showError(
|
||||
showError(
|
||||
null,
|
||||
"PLEASE SELECT OPTION",
|
||||
false,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -389,18 +429,18 @@ const BuyOption = ({
|
||||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1 &&
|
||||
productInfo?.optProdYn === 'Y'
|
||||
) {
|
||||
|
||||
|
||||
if (selectSecondOptionIndex === 0) {
|
||||
dispatch(
|
||||
showError(
|
||||
showError(
|
||||
null,
|
||||
"PLEASE SELECT OPTION",
|
||||
false,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -408,10 +448,27 @@ const BuyOption = ({
|
||||
if (userNumber && selectedPatnrId && selectedPrdtId && quantity) {
|
||||
const { prodOptCval, priceInfo } = selectedOptions || {};
|
||||
const { patncNm, brndNm, catNm, prdtNm, prdtId } = productInfo;
|
||||
const regularPrice = priceInfo?.split('|')[0];
|
||||
const discountPrice = priceInfo?.split('|')[1];
|
||||
console.log('[BuyOption] handleClickOrder - selectedOptions:', selectedOptions);
|
||||
console.log('[BuyOption] handleClickOrder - priceInfo:', priceInfo);
|
||||
console.log('[BuyOption] handleClickOrder - logInfo:', logInfo);
|
||||
|
||||
// priceInfo 파싱 및 숫자 변환
|
||||
let regularPrice = parseInt(priceInfo?.split('|')[0], 10) || 0;
|
||||
let discountPrice = parseInt(priceInfo?.split('|')[1], 10) || 0;
|
||||
const discountRate = priceInfo?.split('|')[4];
|
||||
|
||||
// selectedOptions가 없으면 logInfo에서 가격 추출
|
||||
if (!selectedOptions && logInfo) {
|
||||
console.log('[BuyOption] handleClickOrder - selectedOptions is undefined, using logInfo prices');
|
||||
// logInfo의 dcBefPrc와 dcAftrPrc는 "$ 521.66" 형식이므로 숫자만 추출 (소수점 포함)
|
||||
const dcBefPrcMatch = logInfo.dcBefPrc?.match(/[\d.]+/);
|
||||
const dcAftrPrcMatch = logInfo.dcAftrPrc?.match(/[\d.]+/);
|
||||
regularPrice = dcBefPrcMatch ? parseFloat(dcBefPrcMatch[0]) : 0;
|
||||
discountPrice = dcAftrPrcMatch ? parseFloat(dcAftrPrcMatch[0]) : regularPrice;
|
||||
console.log('[BuyOption] handleClickOrder - extracted from logInfo - dcBefPrc:', logInfo.dcBefPrc, 'dcAftrPrc:', logInfo.dcAftrPrc);
|
||||
}
|
||||
console.log('[BuyOption] handleClickOrder - regularPrice:', regularPrice, 'discountPrice:', discountPrice, 'discountRate:', discountRate);
|
||||
|
||||
dispatch(
|
||||
sendLogTotalRecommend({
|
||||
nowMenu: nowMenu,
|
||||
@@ -452,13 +509,44 @@ const BuyOption = ({
|
||||
);
|
||||
} else {
|
||||
// Mock Mode: 체크아웃 페이지로 이동 (시뮬레이션)
|
||||
// panelInfo의 logInfo에 선택한 상품 정보를 포함시켜 CheckOutPanel에서 사용
|
||||
console.log('[BuyOption] Mock Mode - Simulating checkout');
|
||||
console.log('[BuyOption] logInfo:', logInfo);
|
||||
console.log('[BuyOption] Dispatching pushPanel to CHECKOUT_PANEL');
|
||||
dispatch(finishVideoPreview());
|
||||
dispatch(finishMediaPreview());
|
||||
const checkoutPanelInfo = { logInfo: { ...logInfo, cartTpSno: `MOCK_${Date.now()}` } };
|
||||
console.log('[BuyOption] checkoutPanelInfo:', checkoutPanelInfo);
|
||||
|
||||
// Mock 모드: 선택 상품의 정보를 panelInfo에 담아서 전달
|
||||
// CheckOutPanel에서 이 정보로 Mock 상품 데이터 생성
|
||||
// 이미지 URL 추출 (productInfo의 imgList 또는 thumbnailUrl 사용)
|
||||
const imgUrl = productInfo?.imgList?.[0]?.imgUrl ||
|
||||
productInfo?.thumbnailUrl ||
|
||||
productInfo?.patncLogPath ||
|
||||
'/mock/image.jpg';
|
||||
|
||||
const mockProductInfo = {
|
||||
prdtId: selectedPrdtId,
|
||||
prdtNm: productInfo?.prdtNm,
|
||||
patnrId: selectedPatnrId,
|
||||
patncNm: patncNm,
|
||||
prodQty: quantity,
|
||||
origPrice: regularPrice || 99999, // 원래 가격 (숫자)
|
||||
discountPrice: discountPrice || regularPrice || 99999, // 할인된 가격 (실제 판매 가격, 숫자)
|
||||
finalPrice: discountPrice || regularPrice || 99999, // 최종 가격 (숫자)
|
||||
currSign: '$',
|
||||
currSignLoc: 'left',
|
||||
imgUrl: imgUrl, // 상품 이미지 URL 추가
|
||||
};
|
||||
|
||||
const checkoutPanelInfo = {
|
||||
logInfo: { ...logInfo, cartTpSno: `MOCK_${Date.now()}` },
|
||||
productInfo: productInfo,
|
||||
defaultPrice: discountPrice,
|
||||
};
|
||||
|
||||
console.log('[BuyOption] Mock Mode - mockProductInfo:', mockProductInfo);
|
||||
console.log('[BuyOption] Mock Mode - regularPrice(숫자):', regularPrice, 'discountPrice(숫자):', discountPrice);
|
||||
console.log('[BuyOption] Mock Mode - checkoutPanelInfo:', checkoutPanelInfo);
|
||||
dispatch(
|
||||
pushPanel({
|
||||
name: Config.panel_names.CHECKOUT_PANEL,
|
||||
@@ -673,6 +761,11 @@ const BuyOption = ({
|
||||
setFavoriteFlag(ev);
|
||||
}, []);
|
||||
|
||||
// 구매창이 뜰 때 콘솔 로그 출력 (상품 정보 포함 태그 [BuyOption] 붙임)
|
||||
useEffect(() => {
|
||||
console.log('[BuyOption]', '상품 정보:', JSON.stringify(productInfo));
|
||||
}, []); // 컴포넌트 마운트 시 한 번만 출력
|
||||
|
||||
// hasOnClose 로직 (SingleOption과 동일)
|
||||
const hasOnClose = useMemo(() => {
|
||||
if (productOptionInfos && productOptionInfos.length > 0) {
|
||||
|
||||
Reference in New Issue
Block a user