[251102] fix: CartPanel mock-4 done
🕐 커밋 시간: 2025. 11. 02. 12:58:36 📊 변경 통계: • 총 파일: 6개 • 추가: +409줄 • 삭제: -153줄 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx ~ com.twin.app.shoptime/src/views/CartPanel/CartSidebar.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/container/InformationContainerMock.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/container/OrderItemsSideBar.jsx ~ com.twin.app.shoptime/src/views/CheckOutPanel/container/SummaryContainerMock.jsx 🔧 주요 변경 내용: • 대규모 기능 개발 • 모듈 구조 개선
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
||||
|
||||
import { getMyInfoCartSearch } from '../../actions/cartActions';
|
||||
import { initializeMockCart, resetMockCart } from '../../actions/mockCartActions';
|
||||
import { popPanel } from '../../actions/panelActions';
|
||||
import { popPanel, updatePanel } from '../../actions/panelActions';
|
||||
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
|
||||
import * as Config from '../../utils/Config';
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
@@ -45,37 +45,48 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
|
||||
return isMockMode ? mockCartData : cartData;
|
||||
}, [isMockMode, mockCartData, cartData]);
|
||||
|
||||
// PlayerPanel/MediaPanel 충돌 방지 로직 (DEBUG_LOG가 true일 때만 로깅)
|
||||
// PlayerPanel/MediaPanel 충돌 방지 로직 (CheckOutPanel 전환 시에만 활성화)
|
||||
useEffect(() => {
|
||||
const DEBUG_LOG = false; // 수동 설정: false로 비활성화
|
||||
const DEBUG_LOG = true; // 수동 설정: true로 활성화 (디버깅용)
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartPanel] Component mounted - checking for panel conflicts');
|
||||
console.log('[CartPanel] Current panels:', panels?.map((p) => ({ name: p.name, hasModal: !!p.panelInfo?.modal })));
|
||||
}
|
||||
|
||||
// PlayerPanel 충돌 방지: PlayerPanel이 있고 modal 상태면 비활성화
|
||||
const playerPanelIndex = panels?.findIndex(p =>
|
||||
p.name === Config.panel_names.PLAYER_PANEL ||
|
||||
p.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
||||
p.name === Config.panel_names.MEDIA_PANEL
|
||||
);
|
||||
// ⚠️ CheckOutPanel 전환 시에만 PlayerPanel 충돌 방지 로직 활성화
|
||||
// 다른 경우에는 MainView의 STANDALONE_PANELS 로직이 처리하므로 간섭함
|
||||
const topPanel = panels[panels.length - 1];
|
||||
const isCheckoutPanel = topPanel?.name === Config.panel_names.CHECKOUT_PANEL;
|
||||
|
||||
if (playerPanelIndex >= 0) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartPanel] 🚨 PlayerPanel/MediaPanel detected at index:', playerPanelIndex);
|
||||
}
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartPanel] Panel conflict check - topPanel:', topPanel?.name, 'isCheckoutPanel:', isCheckoutPanel);
|
||||
}
|
||||
|
||||
// PlayerPanel/MediaPanel 상태를 비활성화하여 CartPanel과의 충돌 방지
|
||||
if (panels[playerPanelIndex].panelInfo?.modal) {
|
||||
if (!isCheckoutPanel) {
|
||||
// PlayerPanel 충돌 방지: PlayerPanel이 있고 modal 상태면 비활성화
|
||||
const playerPanelIndex = panels?.findIndex(p =>
|
||||
p.name === Config.panel_names.PLAYER_PANEL ||
|
||||
p.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
||||
p.name === Config.panel_names.MEDIA_PANEL
|
||||
);
|
||||
|
||||
if (playerPanelIndex >= 0) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartPanel] 🔄 Disabling modal PlayerPanel to prevent conflicts');
|
||||
console.log('[CartPanel] 🚨 PlayerPanel/MediaPanel detected at index:', playerPanelIndex);
|
||||
}
|
||||
|
||||
// PlayerPanel/MediaPanel 상태를 비활성화하여 CartPanel과의 충돌 방지
|
||||
if (panels[playerPanelIndex].panelInfo?.modal) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartPanel] 🔄 Disabling modal PlayerPanel to prevent conflicts');
|
||||
}
|
||||
// ✅ PlayerPanel 비활성화 실행
|
||||
dispatch(updatePanel({
|
||||
name: panels[playerPanelIndex].name,
|
||||
panelInfo: { ...panels[playerPanelIndex].panelInfo, isActive: false }
|
||||
}));
|
||||
}
|
||||
// 필요하다면 여기서 PlayerPanel 상태를 비활성화하는 액션을 디스패치할 수 있음
|
||||
// dispatch(updatePanel({
|
||||
// name: panels[playerPanelIndex].name,
|
||||
// panelInfo: { ...panels[playerPanelIndex].panelInfo, isActive: false }
|
||||
// }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { memo } from 'react';
|
||||
import TButton from '../../components/TButton/TButton';
|
||||
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
|
||||
import { pushPanel } from '../../actions/panelActions';
|
||||
import Config from '../../utils/Config';
|
||||
import * as Config from '../../utils/Config';
|
||||
import { calculateOrderSummaryFromProductInfo } from '../../utils/mockDataSafetyUtils';
|
||||
import css from './CartSidebar.module.less';
|
||||
|
||||
@@ -196,34 +196,165 @@ const CartSidebar = ({ cartInfo }) => {
|
||||
}
|
||||
}, [isMockMode, displayCartInfo, getSelectedItems, itemPriceCache]);
|
||||
|
||||
// 체크아웃 버튼 클릭 핸들러
|
||||
const handleCheckoutClick = useCallback(() => {
|
||||
|
||||
// 체크아웃 버튼 클릭 핸들러 - 완전한 데이터 전송
|
||||
const handleCheckoutClick = useCallback(() => {
|
||||
const DEBUG_LOG = true; // 수동 설정: true로 활성화 (디버깅용)
|
||||
|
||||
console.log('[CartSidebar] 🎯 Checkout button clicked! Starting checkout process...');
|
||||
console.log('[CartSidebar] 📊 Current state:', {
|
||||
isMockMode,
|
||||
totalItems: cartInfo?.length || 0,
|
||||
selectedItemsCount: selectedItems.length
|
||||
});
|
||||
|
||||
if (isMockMode) {
|
||||
// Mock Mode: 바로 체크아웃 패널로 이동
|
||||
console.log('[CartSidebar] Mock Mode - Going to Checkout');
|
||||
console.log('[CartSidebar] Mock Mode - cartInfo:', JSON.stringify(cartInfo));
|
||||
// Mock Mode: 선택된 상품들로 CheckOutPanel로 이동
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Mock Mode - Going to Checkout');
|
||||
console.log('[CartSidebar] Mock Mode - cartInfo:', JSON.stringify(cartInfo));
|
||||
}
|
||||
|
||||
// ✅ panelInfo에 cartInfo를 포함하여 전달
|
||||
// CheckOutPanel에서 productList로 사용할 수 있도록 구성
|
||||
const cartItems = cartInfo && Array.isArray(cartInfo) ? cartInfo : [];
|
||||
// ✅ 선택된 상품들만 필터링 (체크박스 선택된 상품)
|
||||
const allCartItems = cartInfo && Array.isArray(cartInfo) ? cartInfo : [];
|
||||
const selectedCartItems = getSelectedItems(allCartItems);
|
||||
|
||||
console.log('[CartSidebar] Mock Mode - cartItems:', JSON.stringify(cartItems));
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Mock Mode - All cart items:', allCartItems.length);
|
||||
console.log('[CartSidebar] Mock Mode - Selected cart items:', selectedCartItems.length);
|
||||
console.log('[CartSidebar] Mock Mode - Selected items:', JSON.stringify(selectedCartItems));
|
||||
}
|
||||
|
||||
// dispatch(pushPanel({
|
||||
// name: Config.panel_names.CHECKOUT_PANEL,
|
||||
// panelInfo: {
|
||||
// productInfo: cartItems.length > 0 ? cartItems[0] : null, // 첫 번째 상품
|
||||
// cartItems: cartItems, // 전체 카트 아이템
|
||||
// isFromCart: true, // CartPanel에서 왔음을 표시
|
||||
// }
|
||||
// }));
|
||||
// 선택된 상품이 없는 경우
|
||||
if (selectedCartItems.length === 0) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Mock Mode - No items selected, cannot proceed to checkout');
|
||||
}
|
||||
return; // 아무것도 하지 않음
|
||||
}
|
||||
|
||||
// ✅ CheckOutPanel 전송용 데이터 구성 (CheckOutPanel 기대 구조에 맞춤)
|
||||
const checkoutData = {
|
||||
// 하위 호환성: 첫 번째 상품을 productInfo로
|
||||
productInfo: selectedCartItems[0],
|
||||
|
||||
// ✅ CheckOutPanel이 기대하는 cartItems 필드 (isFromCart=true일 때 사용)
|
||||
cartItems: selectedCartItems.map(item => ({
|
||||
prdtId: item.prdtId,
|
||||
prdtNm: item.prdtNm,
|
||||
patnrId: item.patnrId,
|
||||
patncNm: item.patncNm,
|
||||
price2: item.price2, // 원가
|
||||
price3: item.price3, // 할인가
|
||||
price5: item.price5, // 쿠폰
|
||||
prodQty: item.prodQty || item.qty || 1,
|
||||
optNm: item.optNm,
|
||||
shippingCharge: item.shippingCharge || '0',
|
||||
// 추가 필드들
|
||||
finalPrice: item.finalPrice,
|
||||
discountPrice: item.discountPrice,
|
||||
origPrice: item.origPrice,
|
||||
priceInfo: item.priceInfo,
|
||||
// 이미지 정보
|
||||
imgUrl: item.imgUrl,
|
||||
thumbnailUrl: item.thumbnailUrl,
|
||||
imgUrls600: item.imgUrls600
|
||||
})),
|
||||
|
||||
// 메타데이터
|
||||
isFromCart: true,
|
||||
fromCartPanel: true, // 추가 확인용
|
||||
cartTotal: orderTotalBeforeTax,
|
||||
itemCount: itemCount,
|
||||
subtotal: subtotal,
|
||||
optionTotal: optionTotal,
|
||||
shippingHandling: shippingHandling
|
||||
};
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('%c🚨🚨🚨 CART SIDEBAR CHECKOUT DATA ANALYSIS 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cOriginal cartInfo:', 'background: yellow; color: black; padding: 3px;', cartInfo);
|
||||
console.log('%c🔍 ORIGINAL CART ITEM 1 PRICE FIELDS:', 'background: magenta; color: white; padding: 3px;');
|
||||
console.log('price2:', cartInfo?.[0]?.price2);
|
||||
console.log('price3:', cartInfo?.[0]?.price3);
|
||||
console.log('price5:', cartInfo?.[0]?.price5);
|
||||
console.log('optPrc:', cartInfo?.[0]?.optPrc);
|
||||
console.log('finalPrice:', cartInfo?.[0]?.finalPrice);
|
||||
console.log('discountPrice:', cartInfo?.[0]?.discountPrice);
|
||||
console.log('price:', cartInfo?.[0]?.price);
|
||||
console.log('origPrice:', cartInfo?.[0]?.origPrice);
|
||||
console.log('Full first item:', cartInfo?.[0]);
|
||||
console.log('%cSelected items array:', 'background: yellow; color: black; padding: 3px;', selectedItems);
|
||||
console.log('%cSelected cart items (filtered):', 'background: yellow; color: black; padding: 3px;', selectedCartItems);
|
||||
console.log('%cCheckout data prepared:', 'background: yellow; color: black; padding: 3px;', checkoutData);
|
||||
console.log('%cCartItems count:', 'background: yellow; color: black; padding: 3px;', checkoutData.cartItems.length);
|
||||
console.log('%cCartItems details:', 'background: yellow; color: black; padding: 3px;', checkoutData.cartItems);
|
||||
console.log('%cisFromCart value:', 'background: yellow; color: black; padding: 3px;', checkoutData.isFromCart);
|
||||
console.log('%cAbout to pushPanel to CHECKOUT_PANEL', 'background: yellow; color: black; padding: 3px;');
|
||||
}
|
||||
|
||||
// ✅ CheckOutPanel로 이동
|
||||
console.log('[CartSidebar] 🚀 Executing pushPanel to CHECKOUT_PANEL');
|
||||
console.log('[CartSidebar] 📦 Checkout data summary:', {
|
||||
itemCount: checkoutData.cartItems?.length || 0,
|
||||
cartTotal: checkoutData.cartTotal,
|
||||
hasProductInfo: !!checkoutData.productInfo,
|
||||
isFromCart: checkoutData.isFromCart,
|
||||
panelName: Config.panel_names.CHECKOUT_PANEL
|
||||
});
|
||||
|
||||
try {
|
||||
dispatch(pushPanel({
|
||||
name: Config.panel_names.CHECKOUT_PANEL,
|
||||
panelInfo: checkoutData
|
||||
}));
|
||||
console.log('[CartSidebar] ✅ pushPanel executed successfully');
|
||||
} catch (error) {
|
||||
console.error('[CartSidebar] ❌ pushPanel failed:', error);
|
||||
}
|
||||
|
||||
} else {
|
||||
// API Mode: 실제 로직 (향후 구현)
|
||||
console.log('[CartSidebar] API Mode - Checkout (to be implemented)');
|
||||
// API Mode: 실제 API 데이터로 처리
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] API Mode - Checkout (to be implemented)');
|
||||
}
|
||||
|
||||
// API Mode에서도 동일한 로직 적용
|
||||
const allCartItems = cartInfo && Array.isArray(cartInfo) ? cartInfo : [];
|
||||
const selectedCartItems = getSelectedItems(allCartItems);
|
||||
|
||||
if (selectedCartItems.length === 0) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] API Mode - No items selected, cannot proceed to checkout');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const checkoutData = {
|
||||
productInfo: selectedCartItems[0],
|
||||
cartItems: selectedCartItems.map(item => ({
|
||||
prdtId: item.prdtId,
|
||||
prdtNm: item.prdtNm,
|
||||
patnrId: item.patnrId,
|
||||
patncNm: item.patncNm,
|
||||
price2: item.price2,
|
||||
price3: item.price3,
|
||||
price5: item.price5,
|
||||
prodQty: item.prodQty || item.qty || 1,
|
||||
optNm: item.optNm,
|
||||
shippingCharge: item.shippingCharge || '0'
|
||||
})),
|
||||
isFromCart: true,
|
||||
fromCartPanel: true, // 추가 확인용
|
||||
cartTotal: orderTotalBeforeTax,
|
||||
itemCount: itemCount
|
||||
};
|
||||
|
||||
dispatch(pushPanel({
|
||||
name: Config.panel_names.CHECKOUT_PANEL,
|
||||
panelInfo: checkoutData
|
||||
}));
|
||||
}
|
||||
}, [dispatch, isMockMode, cartInfo]);
|
||||
}, [dispatch, isMockMode, cartInfo, getSelectedItems, orderTotalBeforeTax, itemCount, subtotal, optionTotal, shippingHandling]);
|
||||
|
||||
const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
@@ -46,7 +46,7 @@ import InformationContainerMock from './container/InformationContainerMock';
|
||||
|
||||
export default function CheckOutPanel({ panelInfo }) {
|
||||
// DEBUG_LOG 설정 - 이 값이 true일 때만 console.log가 실행됨
|
||||
const DEBUG_LOG = false;
|
||||
const DEBUG_LOG = true; // 계속 활성화하여 디버깅
|
||||
|
||||
// console.log 오버라이드를 위한 ref
|
||||
const originalConsoleLog = useRef();
|
||||
@@ -64,8 +64,15 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
};
|
||||
|
||||
console.log('%c[BuyOption][CheckOutPanel] ▶️ Component mounted START', 'background: blue; color: white; font-weight: bold; padding: 5px;');
|
||||
console.log('[BuyOption][CheckOutPanel] panelInfo:', panelInfo);
|
||||
console.log('[BuyOption][CheckOutPanel] panelInfo.logInfo:', panelInfo?.logInfo);
|
||||
console.log('%c🚨🚨🚨 CHECKOUT PANEL PANELINFO ANALYSIS 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cpanelInfo:', 'background: yellow; color: black; padding: 3px;', panelInfo);
|
||||
console.log('%cpanelInfo.isFromCart:', 'background: yellow; color: black; padding: 3px;', panelInfo?.isFromCart);
|
||||
console.log('%cpanelInfo.fromCartPanel:', 'background: yellow; color: black; padding: 3px;', panelInfo?.fromCartPanel);
|
||||
console.log('%cpanelInfo.cartItems:', 'background: yellow; color: black; padding: 3px;', panelInfo?.cartItems);
|
||||
console.log('%cpanelInfo.cartItems type:', 'background: yellow; color: black; padding: 3px;', typeof panelInfo?.cartItems);
|
||||
console.log('%cpanelInfo.cartItems length:', 'background: yellow; color: black; padding: 3px;', panelInfo?.cartItems?.length);
|
||||
console.log('%cpanelInfo.productInfo:', 'background: yellow; color: black; padding: 3px;', panelInfo?.productInfo);
|
||||
console.log('%cpanelInfo.logInfo:', 'background: yellow; color: black; padding: 3px;', panelInfo?.logInfo);
|
||||
console.log('[BuyOption][CheckOutPanel] Current panels:', panels?.map((p) => ({ name: p.name, hasModal: !!p.panelInfo?.modal })));
|
||||
|
||||
// PlayerPanel 충돌 방지: PlayerPanel이 있고 modal 상태면 비활성화
|
||||
@@ -113,17 +120,24 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
// Mock Mode: panelInfo.productInfo 또는 Redux에서 상품 데이터 사용
|
||||
const productData = BUYNOW_CONFIG.isMockMode()
|
||||
? (() => {
|
||||
console.log('[BuyOption][CheckOutPanel] Mock Mode - panelInfo:', panelInfo);
|
||||
console.log('[BuyOption][CheckOutPanel] Mock Mode - panelInfo.productInfo:', panelInfo?.productInfo);
|
||||
console.log('[BuyOption][CheckOutPanel] Mock Mode - panelInfo.cartItems:', panelInfo?.cartItems);
|
||||
console.log('[BuyOption][CheckOutPanel] Mock Mode - panelInfo.isFromCart:', panelInfo?.isFromCart);
|
||||
console.log('%c🚨🚨🚨 MOCK MODE DATA SELECTION START 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cisFromCart condition check:', 'background: yellow; color: black; padding: 3px;');
|
||||
console.log('%c - panelInfo?.isFromCart:', 'background: yellow; color: black; padding: 3px;', panelInfo?.isFromCart);
|
||||
console.log('%c - panelInfo?.cartItems exists:', 'background: yellow; color: black; padding: 3px;', !!panelInfo?.cartItems);
|
||||
console.log('%c - Array.isArray(panelInfo?.cartItems):', 'background: yellow; color: black; padding: 3px;', Array.isArray(panelInfo?.cartItems));
|
||||
console.log('%c - panelInfo?.cartItems.length > 0:', 'background: yellow; color: black; padding: 3px;', panelInfo?.cartItems?.length > 0);
|
||||
|
||||
// ✅ 1순위: CartPanel에서 전달된 cartItems (전체 카트 데이터)
|
||||
if (panelInfo?.isFromCart && panelInfo?.cartItems && Array.isArray(panelInfo.cartItems) && panelInfo.cartItems.length > 0) {
|
||||
console.log('%c[BuyOption][CheckOutPanel] ✅ Mock Mode - Using cartItems from CartPanel (multi-product):', 'background: blue; color: white; font-weight: bold; padding: 5px;', panelInfo.cartItems);
|
||||
console.log('%c✅ SUCCESS! Using cartItems from CartPanel (multi-product) ✅', 'background: green; color: white; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%ccartItems:', 'background: green; color: white; padding: 3px;', panelInfo.cartItems);
|
||||
console.log('%ccartItems count:', 'background: green; color: white; padding: 3px;', panelInfo.cartItems.length);
|
||||
return panelInfo.cartItems;
|
||||
}
|
||||
|
||||
console.log('%c❌ FAILED! isFromCart condition FAILED ❌', 'background: red; color: white; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cTrying next condition - productInfo check...', 'background: red; color: white; padding: 3px;');
|
||||
|
||||
// 2순위: BuyOption에서 전달된 productInfo (단일 상품)
|
||||
if (panelInfo?.productInfo) {
|
||||
console.log('%c[BuyOption][CheckOutPanel] ✅ Mock Mode - Using panelInfo.productInfo (single product):', 'background: green; color: white; font-weight: bold; padding: 5px;', panelInfo.productInfo);
|
||||
@@ -155,9 +169,12 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
})()
|
||||
: reduxProductData;
|
||||
|
||||
console.log('[BuyOption][CheckOutPanel] isMockMode:', BUYNOW_CONFIG.isMockMode());
|
||||
console.log('[BuyOption][CheckOutPanel] productData loaded:', productData && productData.length, 'items');
|
||||
console.log('[BuyOption][CheckOutPanel] productData[0].prdtId:', productData?.[0]?.prdtId);
|
||||
console.log('%c🚨🚨🚨 FINAL PRODUCT DATA RESULT 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cisMockMode:', 'background: yellow; color: black; padding: 3px;', BUYNOW_CONFIG.isMockMode());
|
||||
console.log('%cproductData loaded:', 'background: yellow; color: black; padding: 3px;', productData && productData.length, 'items');
|
||||
console.log('%cproductData[0].prdtId:', 'background: yellow; color: black; padding: 3px;', productData?.[0]?.prdtId);
|
||||
console.log('%cproductData length:', 'background: yellow; color: black; padding: 3px;', productData?.length || 0);
|
||||
console.log('%cproductData:', 'background: yellow; color: black; padding: 3px;', productData);
|
||||
|
||||
// 표시용으로 모든 상품 데이터 정규화 (없는 필드는 안전한 기본값으로)
|
||||
// Mock 모드에서는 항상 정규화, API 모드에서는 그대로 사용
|
||||
@@ -175,6 +192,33 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
console.log('[BuyOption][CheckOutPanel] firstProduct:', firstProduct);
|
||||
console.log('[BuyOption][CheckOutPanel] currSign:', currSign, 'currSignLoc:', currSignLoc);
|
||||
|
||||
const orderItemsCount = useMemo(() => {
|
||||
const normalizeQty = (value) => {
|
||||
const numeric = Number(value);
|
||||
if (Number.isFinite(numeric) && numeric > 0) {
|
||||
return numeric;
|
||||
}
|
||||
return 1;
|
||||
};
|
||||
|
||||
if (Array.isArray(productData) && productData.length > 0) {
|
||||
return productData.reduce((sum, item) => {
|
||||
if (!item) {
|
||||
return sum + 1;
|
||||
}
|
||||
const qty = item.prodQty ?? item.qty ?? 1;
|
||||
return sum + normalizeQty(qty);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (panelInfo?.productInfo) {
|
||||
const qty = panelInfo.productInfo?.prodQty ?? panelInfo.productInfo?.qty ?? 1;
|
||||
return normalizeQty(qty);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}, [productData, panelInfo]);
|
||||
|
||||
const [orderSideBarOpen, setOrderSideBarOpen] = useState(false);
|
||||
const [offerSideBarOpen, setOfferSideBarOpen] = useState(false);
|
||||
const [placeOrderPopup, setPlaceOrderPopup] = useState(false);
|
||||
@@ -534,8 +578,10 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
currSignLoc={currSignLoc}
|
||||
doSendLogPaymentEntry={doSendLogPaymentEntry}
|
||||
productData={safeProductData}
|
||||
rawProductData={productData}
|
||||
productInfo={panelInfo?.productInfo}
|
||||
defaultPrice={panelInfo?.defaultPrice}
|
||||
fromCartPanel={panelInfo?.fromCartPanel}
|
||||
/>
|
||||
) : (
|
||||
<SummaryContainer
|
||||
@@ -553,6 +599,7 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
toggleOfferSideBar={toggleOfferSideBar}
|
||||
scrollTopBody={scrollTopBody}
|
||||
doSendLogMyInfoEdit={doSendLogMyInfoEdit}
|
||||
orderItemsCount={orderItemsCount}
|
||||
/>
|
||||
) : (
|
||||
<InformationContainer
|
||||
@@ -566,7 +613,15 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
</TBody>
|
||||
</TPanel>
|
||||
|
||||
{orderSideBarOpen && <OrderItemsSideBar closeSideBar={toggleOrderSideBar} productData={safeProductData} productInfo={panelInfo?.productInfo} />}
|
||||
{orderSideBarOpen && (
|
||||
<OrderItemsSideBar
|
||||
closeSideBar={toggleOrderSideBar}
|
||||
productData={safeProductData}
|
||||
rawProductData={productData}
|
||||
productInfo={panelInfo?.productInfo}
|
||||
fromCartPanel={panelInfo?.fromCartPanel}
|
||||
/>
|
||||
)}
|
||||
{offerSideBarOpen && (
|
||||
<FixedSideBar closeSideBar={toggleOfferSideBar} offerSideBarOpen={offerSideBarOpen} />
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import Spottable from '@enact/spotlight/Spottable';
|
||||
|
||||
@@ -88,6 +88,7 @@ export default function InformationContainerMock({
|
||||
toggleOrderSideBar,
|
||||
scrollTopBody,
|
||||
doSendLogMyInfoEdit,
|
||||
orderItemsCount = 0,
|
||||
}) {
|
||||
console.log('[CheckOutPanel] InformationContainerMock - Mounted');
|
||||
console.log('[CheckOutPanel] InformationContainerMock - mockCheckoutData:', mockCheckoutData);
|
||||
@@ -125,6 +126,12 @@ export default function InformationContainerMock({
|
||||
[doSendLogMyInfoEdit]
|
||||
);
|
||||
|
||||
const orderItemsLabel = useMemo(() => {
|
||||
const count = Number(orderItemsCount) || 0;
|
||||
const suffix = count === 1 ? 'ITEM' : 'ITEMS';
|
||||
return `${count} ${suffix}`;
|
||||
}, [orderItemsCount]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={css.container}>
|
||||
@@ -137,7 +144,7 @@ export default function InformationContainerMock({
|
||||
spotlightId="checkout-btn-first"
|
||||
onFocus={handleFocus}
|
||||
>
|
||||
1 ITEMS
|
||||
{orderItemsLabel}
|
||||
</BtnSpot>
|
||||
</div>
|
||||
<div className={css.listBox}>
|
||||
|
||||
@@ -19,89 +19,117 @@ import css from './OrderItemsSideBar.module.less';
|
||||
|
||||
const SideBarContainer = SpotlightContainerDecorator('div');
|
||||
|
||||
export default function OrderItemsSideBar({ closeSideBar, productData, productInfo }) {
|
||||
export default function OrderItemsSideBar({
|
||||
closeSideBar,
|
||||
productData,
|
||||
rawProductData,
|
||||
productInfo,
|
||||
fromCartPanel,
|
||||
}) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar mounted');
|
||||
|
||||
const reduxOrderItemList = useSelector((state) => state.checkout?.checkoutData?.productList);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar reduxOrderItemList:', reduxOrderItemList);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar productData:', productData);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar rawProductData:', rawProductData);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar productInfo:', productInfo);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar fromCartPanel:', fromCartPanel);
|
||||
|
||||
// Check if reduxOrderItemList has actual data
|
||||
const hasValidOrderItemList = Array.isArray(reduxOrderItemList) && reduxOrderItemList.length > 0;
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar hasValidOrderItemList:', hasValidOrderItemList);
|
||||
|
||||
const mapToOrderItem = (product, index) => {
|
||||
const normalized =
|
||||
product && product.isValid ? product : normalizeProductDataForDisplay(product);
|
||||
|
||||
if (!normalized) {
|
||||
console.warn('[CheckOutPanel] OrderItemsSideBar mapToOrderItem - product is invalid at index:', index);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar mapToOrderItem - normalized product:', normalized);
|
||||
|
||||
return {
|
||||
prdtId: normalized.prdtId,
|
||||
prdtNm: normalized.prdtNm,
|
||||
prodQty: normalized.prodQty,
|
||||
prdtOpt:
|
||||
(normalized.options && normalized.options.length > 0
|
||||
? normalized.options
|
||||
: [{ prodOptCdCval: 'DEFAULT_OPT', optNm: 'Default Option' }]),
|
||||
patncLogPath: normalized.imgUrl,
|
||||
expsPrdtNo: normalized.prdtId,
|
||||
currSign: normalized.currSign,
|
||||
currSignLoc: normalized.currSignLoc,
|
||||
shippingCharge: normalized.shippingCharge || 0,
|
||||
auctProdYn: normalized.auctProdYn || 'N',
|
||||
auctFinalPriceChgDt: null,
|
||||
imgUrls: [{ imgUrl: normalized.imgUrl }],
|
||||
price: normalized.price,
|
||||
discount: normalized.discount,
|
||||
};
|
||||
};
|
||||
|
||||
const orderItemList = hasValidOrderItemList
|
||||
? reduxOrderItemList
|
||||
: BUYNOW_CONFIG.isMockMode()
|
||||
? (() => {
|
||||
// Mock Mode: productInfo가 있으면 우선 사용
|
||||
if (productInfo) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using productInfo for image');
|
||||
const normalized = normalizeProductDataForDisplay(productInfo);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar productInfo normalized imgUrl:', normalized.imgUrl);
|
||||
// Mock Mode: CartPanel에서 온 전체 아이템 우선 사용
|
||||
if (fromCartPanel) {
|
||||
const sourceItems =
|
||||
Array.isArray(rawProductData) && rawProductData.length > 0
|
||||
? rawProductData
|
||||
: productData;
|
||||
|
||||
return [{
|
||||
prdtId: normalized.prdtId,
|
||||
prdtNm: normalized.prdtNm,
|
||||
prodQty: normalized.prodQty,
|
||||
prdtOpt: normalized.options || [{ prodOptCdCval: 'DEFAULT_OPT', optNm: 'Default Option' }],
|
||||
patncLogPath: normalized.imgUrl,
|
||||
expsPrdtNo: normalized.prdtId,
|
||||
currSign: normalized.currSign,
|
||||
currSignLoc: normalized.currSignLoc,
|
||||
shippingCharge: normalized.shippingCharge || 0,
|
||||
auctProdYn: normalized.auctProdYn || 'N',
|
||||
auctFinalPriceChgDt: null,
|
||||
imgUrls: [{ imgUrl: normalized.imgUrl }], // productInfo에서 추출한 실제 이미지
|
||||
// 표시용 추가 필드
|
||||
price: normalized.price,
|
||||
discount: normalized.discount,
|
||||
}];
|
||||
if (Array.isArray(sourceItems) && sourceItems.length > 0) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using full cart items from CartPanel');
|
||||
|
||||
const mappedItems = sourceItems
|
||||
.map((item, index) => mapToOrderItem(item, index))
|
||||
.filter(Boolean);
|
||||
|
||||
if (mappedItems.length > 0) {
|
||||
return mappedItems;
|
||||
}
|
||||
}
|
||||
}
|
||||
// productInfo가 없으면 productData 사용
|
||||
else if (productData && productData.length > 0) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using productData (fallback)');
|
||||
return productData.map((prod) => {
|
||||
const normalized = normalizeProductDataForDisplay(prod);
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar productData normalized imgUrl:', normalized.imgUrl);
|
||||
return {
|
||||
prdtId: normalized.prdtId,
|
||||
prdtNm: normalized.prdtNm,
|
||||
prodQty: normalized.prodQty,
|
||||
prdtOpt: normalized.options || [{ prodOptCdCval: 'MOCK_OPT_1', optNm: 'Selected Option' }],
|
||||
patncLogPath: normalized.imgUrl,
|
||||
expsPrdtNo: normalized.prdtId,
|
||||
currSign: normalized.currSign,
|
||||
currSignLoc: normalized.currSignLoc,
|
||||
shippingCharge: normalized.shippingCharge || 0,
|
||||
auctProdYn: normalized.auctProdYn || 'N',
|
||||
auctFinalPriceChgDt: null,
|
||||
imgUrls: [{ imgUrl: normalized.imgUrl }], // 이미지 URL 추가
|
||||
// 표시용 추가 필드
|
||||
price: normalized.price,
|
||||
discount: normalized.discount,
|
||||
};
|
||||
});
|
||||
|
||||
// productData가 있으면 사용 (BuyOption 등에서 전달된 다중 상품)
|
||||
if (Array.isArray(productData) && productData.length > 0) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using productData fallback');
|
||||
const mappedItems = productData
|
||||
.map((prod, index) => mapToOrderItem(prod, index))
|
||||
.filter(Boolean);
|
||||
|
||||
if (mappedItems.length > 0) {
|
||||
return mappedItems;
|
||||
}
|
||||
}
|
||||
|
||||
// productInfo가 있으면 단일 상품
|
||||
if (productInfo) {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using productInfo for fallback');
|
||||
const mappedItem = mapToOrderItem(productInfo, 0);
|
||||
return mappedItem ? [mappedItem] : [];
|
||||
}
|
||||
|
||||
// 둘 다 없으면 기본 Mock 데이터
|
||||
else {
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using default mock data');
|
||||
return [{
|
||||
prdtId: 'MOCK_PRODUCT_1',
|
||||
prdtNm: 'Mock Product',
|
||||
prodQty: 1,
|
||||
prdtOpt: [{ prodOptCdCval: 'MOCK_OPT_1', optNm: 'Mock Option' }],
|
||||
patncLogPath: '/mock/image.jpg',
|
||||
expsPrdtNo: 'MOCK_EXP_1',
|
||||
currSign: '$',
|
||||
currSignLoc: 'left',
|
||||
shippingCharge: 0,
|
||||
auctProdYn: 'N',
|
||||
auctFinalPriceChgDt: null,
|
||||
imgUrls: [{ imgUrl: '/mock/image.jpg' }],
|
||||
}];
|
||||
}
|
||||
console.log('[CheckOutPanel] OrderItemsSideBar Using default mock data');
|
||||
return [{
|
||||
prdtId: 'MOCK_PRODUCT_1',
|
||||
prdtNm: 'Mock Product',
|
||||
prodQty: 1,
|
||||
prdtOpt: [{ prodOptCdCval: 'MOCK_OPT_1', optNm: 'Mock Option' }],
|
||||
patncLogPath: '/mock/image.jpg',
|
||||
expsPrdtNo: 'MOCK_EXP_1',
|
||||
currSign: '$',
|
||||
currSignLoc: 'left',
|
||||
shippingCharge: 0,
|
||||
auctProdYn: 'N',
|
||||
auctFinalPriceChgDt: null,
|
||||
imgUrls: [{ imgUrl: '/mock/image.jpg' }],
|
||||
}];
|
||||
})()
|
||||
: null;
|
||||
|
||||
|
||||
@@ -14,49 +14,72 @@ export default function SummaryContainerMock({
|
||||
currSignLoc,
|
||||
doSendLogPaymentEntry,
|
||||
productData,
|
||||
rawProductData,
|
||||
productInfo,
|
||||
defaultPrice,
|
||||
fromCartPanel,
|
||||
}) {
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - START render');
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - empTermsData:', empTermsData);
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - currSign:', currSign);
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productData:', productData);
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productInfo:', productInfo);
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productInfo.price2:', productInfo?.price2);
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productInfo.price5:', productInfo?.price5);
|
||||
console.log('%c🚨🚨🚨 SUMMARY CONTAINER MOCK RENDER START 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cfromCartPanel:', 'background: yellow; color: black; padding: 3px;', fromCartPanel);
|
||||
console.log('%cempTermsData:', 'background: yellow; color: black; padding: 3px;', empTermsData);
|
||||
console.log('%ccurrSign:', 'background: yellow; color: black; padding: 3px;', currSign);
|
||||
console.log('%cproductData:', 'background: yellow; color: black; padding: 3px;', productData);
|
||||
console.log('%cproductData length:', 'background: yellow; color: black; padding: 3px;', productData?.length || 0);
|
||||
console.log('%crawProductData:', 'background: yellow; color: black; padding: 3px;', rawProductData);
|
||||
console.log('%crawProductData length:', 'background: yellow; color: black; padding: 3px;', rawProductData?.length || 0);
|
||||
console.log('%cproductInfo:', 'background: yellow; color: black; padding: 3px;', productInfo);
|
||||
console.log('%cproductInfo.price2:', 'background: yellow; color: black; padding: 3px;', productInfo?.price2);
|
||||
console.log('%cproductInfo.price5:', 'background: yellow; color: black; padding: 3px;', productInfo?.price5);
|
||||
|
||||
// ✅ Mock Mode: productData 배열 전체를 합산하거나 productInfo 사용
|
||||
const cartSummarySource = useMemo(() => {
|
||||
if (!fromCartPanel) {
|
||||
return productData;
|
||||
}
|
||||
|
||||
if (Array.isArray(rawProductData) && rawProductData.length > 0) {
|
||||
console.log('%c✅ Using raw cart data for summary calculations', 'background: navy; color: white; font-weight: bold; padding: 4px;');
|
||||
return rawProductData;
|
||||
}
|
||||
|
||||
console.warn('%c⚠️ rawProductData unavailable, falling back to normalized productData for summary', 'background: orange; color: black; font-weight: bold; padding: 4px;');
|
||||
return productData;
|
||||
}, [fromCartPanel, productData, rawProductData]);
|
||||
|
||||
// ✅ Mock Mode: fromCartPanel에 따라 다른 로직 사용
|
||||
const orderSummaryData = useMemo(() => {
|
||||
// ✅ 1순위: CartPanel의 여러 상품 데이터 (productData 배열)
|
||||
if (Array.isArray(productData) && productData.length > 0) {
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using calculateOrderSummaryFromProductData (multiple items)');
|
||||
// ✅ 1순위: CartPanel에서 온 데이터 (fromCartPanel=true)
|
||||
if (fromCartPanel && Array.isArray(cartSummarySource) && cartSummarySource.length > 0) {
|
||||
console.log('%c✅ USING CART PANEL DATA (fromCartPanel=true) ✅', 'background: blue; color: white; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%c✅ CALCULATING WITH MULTIPLE PRODUCTS ✅', 'background: green; color: white; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%cproductData items:', 'background: green; color: white; padding: 3px;', cartSummarySource.length);
|
||||
|
||||
// 전체 상품의 가격 합산
|
||||
// CartPanel 데이터: calculateOrderSummaryFromProductInfo 사용
|
||||
let totalItems = 0;
|
||||
let totalCoupon = 0;
|
||||
let totalShipping = 0;
|
||||
|
||||
productData.forEach((product) => {
|
||||
const price = parseFloat(product.price3 || product.price2 || product.finalPrice || product.discountPrice || 0);
|
||||
const qty = product.prodQty || 1;
|
||||
const coupon = parseFloat(product.discount || product.price5 || 0);
|
||||
const shipping = parseFloat(product.shippingCharge || 0);
|
||||
cartSummarySource.forEach((product, index) => {
|
||||
console.log(`%c🔍 PRODUCT ${index + 1} CART PANEL ANALYSIS:`, 'background: cyan; color: black; padding: 3px;');
|
||||
console.log('product object:', product);
|
||||
|
||||
totalItems += price * qty;
|
||||
totalCoupon += coupon * qty;
|
||||
totalShipping += shipping;
|
||||
// calculateOrderSummaryFromProductInfo 함수로 개별 상품 가격 계산
|
||||
const productSummary = calculateOrderSummaryFromProductInfo(product);
|
||||
console.log('Product summary result:', productSummary);
|
||||
|
||||
totalItems += productSummary.items;
|
||||
totalCoupon += productSummary.couponSavings;
|
||||
totalShipping += productSummary.shipping;
|
||||
});
|
||||
|
||||
const subtotal = Math.max(0, totalItems - totalCoupon + totalShipping);
|
||||
const tax = Math.round((subtotal * 0.1) * 100) / 100;
|
||||
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Multi-product calculation:', {
|
||||
totalItems,
|
||||
totalCoupon,
|
||||
totalShipping,
|
||||
subtotal,
|
||||
tax
|
||||
});
|
||||
console.log('%c🚨🚨🚨 MULTI-PRODUCT CALCULATION RESULT 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%ctotalItems:', 'background: yellow; color: black; padding: 3px;', totalItems);
|
||||
console.log('%ctotalCoupon:', 'background: yellow; color: black; padding: 3px;', totalCoupon);
|
||||
console.log('%ctotalShipping:', 'background: yellow; color: black; padding: 3px;', totalShipping);
|
||||
console.log('%csubtotal:', 'background: yellow; color: black; padding: 3px;', subtotal);
|
||||
console.log('%ctax:', 'background: yellow; color: black; padding: 3px;', tax);
|
||||
|
||||
return {
|
||||
items: totalItems,
|
||||
@@ -107,9 +130,10 @@ export default function SummaryContainerMock({
|
||||
total: Math.max(0, productPrice - productDiscount) + tax,
|
||||
currency: { currSign, currSignLoc }
|
||||
};
|
||||
}, [productInfo, productData, defaultPrice, currSign, currSignLoc]);
|
||||
}, [productInfo, productData, cartSummarySource, defaultPrice, currSign, currSignLoc, fromCartPanel]);
|
||||
|
||||
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - orderSummaryData:', orderSummaryData);
|
||||
console.log('%c🚨🚨🚨 FINAL ORDER SUMMARY DATA 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;');
|
||||
console.log('%corderSummaryData:', 'background: yellow; color: black; padding: 3px;', orderSummaryData);
|
||||
|
||||
// 기존 호환성을 위해 effectivePriceTotalData 유지
|
||||
const effectivePriceTotalData = {
|
||||
|
||||
Reference in New Issue
Block a user