[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:
2025-11-02 12:58:39 +09:00
parent e3c7ff18d3
commit dec0bb9746
6 changed files with 407 additions and 151 deletions

View File

@@ -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 }
// }));
}
}

View File

@@ -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;

View File

@@ -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} />
)}

View File

@@ -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}>

View File

@@ -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;

View File

@@ -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 = {