import React, { memo, useCallback, useMemo, } from 'react'; import { useDispatch, useSelector, } from 'react-redux'; import { getMyInfoCheckoutInfo } from '../../actions/checkoutActions'; import { changeAppStatus, setShowPopup, showError, } from '../../actions/commonActions'; import { popPanel, pushPanel, } from '../../actions/panelActions'; import TButton from '../../components/TButton/TButton'; import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig'; import * as Config from '../../utils/Config'; import { calculateOrderSummaryFromProductInfo, } from '../../utils/mockDataSafetyUtils'; import css from './CartSidebar.module.less'; const CartSidebar = ({ cartInfo }) => { const dispatch = useDispatch(); // Mock Mode 확인 const isMockMode = BUYNOW_CONFIG.isMockMode(); // 실제 장바구니 데이터 (API 모드일 때만 사용) const fallbackCartInfo = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo); // ✅ Redux에서 체크된 상품 정보 가져오기 const checkedItems = useSelector((state) => state.cart.selectCart.checkedItems || []); const { userNumber } = useSelector( (state) => state.common.appStatus.loginUserData ); // 사용할 장바구니 데이터 결정 const displayCartInfo = cartInfo || (isMockMode ? null : fallbackCartInfo); // ✅ 계산할 상품 결정 (체크된 상품이 있으면 체크된 상품, 없으면 전체 상품) const itemsToCalculate = useMemo(() => { if (checkedItems && checkedItems.length > 0) { // 체크된 상품이 있으면 체크된 상품만 사용 console.log('[CartSidebar] Using checked items for calculation:', checkedItems.length); return checkedItems; } else if (displayCartInfo && Array.isArray(displayCartInfo)) { // 체크된 상품이 없으면 전체 장바구니 상품 사용 console.log('[CartSidebar] No checked items, using all cart items:', displayCartInfo.length); return displayCartInfo; } return []; }, [checkedItems, displayCartInfo]); // checkOutValidate 콜백 함수 (SingleOption과 동일한 로직) function checkOutValidate(response) { console.log('%c[BuyOption] 🔴 checkOutValidate CALLED', 'background: red; color: white; font-weight: bold; padding: 5px;', { hasResponse: !!response, retCode: response?.retCode, retMsg: response?.retMsg, hasCardInfo: !!response?.data?.cardInfo, hasBillingAddress: response?.data?.billingAddressList?.length > 0, hasShippingAddress: response?.data?.shippingAddressList?.length > 0, hasProductList: response?.data?.productList?.length > 0, }); if (response) { if (response.retCode === 0) { // 🔍 조건 체크 const isCardInfoNull = response.data.cardInfo === null; const isBillingAddressEmpty = response.data.billingAddressList.length === 0; const isShippingAddressEmpty = response.data.shippingAddressList.length === 0; console.log('%c[BuyOption] 🔍 Address & Card Validation:', 'background: blue; color: white; font-weight: bold; padding: 5px;', { isCardInfoNull, isBillingAddressEmpty, isShippingAddressEmpty, needsQRPopup: isCardInfoNull || isBillingAddressEmpty || isShippingAddressEmpty, }); if ( isCardInfoNull || isBillingAddressEmpty || isShippingAddressEmpty ) { console.log('%c[BuyOption] 🟡 Missing card/address - Showing QR Popup', 'background: orange; color: white; font-weight: bold; padding: 5px;'); dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup)); dispatch(changeAppStatus({ isLoading: false })); return; } else { console.log('%c[BuyOption] ✅ All address & card data present - Proceeding to CheckOutPanel', 'background: green; color: white; font-weight: bold; padding: 5px;'); const { mbrId, prdtId, prodSno } = response.data.productList[0]; const cartTpSno = `${mbrId}_${prdtId}_${prodSno}`; // 🔴 CRITICAL: 기존 CheckOutPanel 있으면 제거 후 새로 push (API Mode) dispatch((dispatchFn, getState) => { const panels = getState().panels?.panels || []; const checkoutPanelExists = panels.some(p => p.name === Config.panel_names.CHECKOUT_PANEL); console.log('[BuyOption] 📊 API Mode - Current panels:', panels.map(p => p.name)); // 1️⃣ DetailPanel 제거 (STANDALONE_PANEL이므로 다른 패널 제거 필수) console.log('[BuyOption] 🗑️ API Mode - Removing DetailPanel'); dispatchFn(popPanel(Config.panel_names.DETAIL_PANEL)); // 2️⃣ 기존 CheckOutPanel 제거 (있으면) if (checkoutPanelExists) { console.log('[BuyOption] 🗑️ API Mode - Removing existing CheckOutPanel'); dispatchFn(popPanel(Config.panel_names.CHECKOUT_PANEL)); } // 3️⃣ 새로운 CheckOutPanel push console.log('[BuyOption] ➕ API Mode - Pushing new CheckOutPanel'); dispatchFn(pushPanel({ name: Config.panel_names.CHECKOUT_PANEL, // panelInfo: { logInfo: { ...logInfo, cartTpSno } }, })); }); } } else if (response.retCode === 1001) { dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup)); dispatch(changeAppStatus({ isLoading: false })); } else { dispatch( showError( response.retCode, response.retMsg, false, response.retDetailCode, response.returnBindStrings ) ); dispatch(changeAppStatus({ isLoading: false })); return; } } } // ✅ Mock 데이터 또는 실제 데이터 계산 - 최적화 버전 const calculatedData = useMemo(() => { const DEBUG_LOG = false; if (itemsToCalculate && itemsToCalculate.length > 0) { let totalItems = 0; let totalOption = 0; let totalShipping = 0; let totalQuantity = 0; itemsToCalculate.forEach((item) => { // API 모드: 실제 가격 필드 사용 const productPrice = parseFloat(Number(item.price3) !== 0 ? Number(item.price3) : Number(item.price2) !== 0 ? Number(item.price2) : 0); const optionPrice = parseFloat(Number(item.price3) !== Number(item.optPrc) && (Number(item.price3) !== Number(item.optPrc)) ? Number(item.optPrc) : 0 || 0); const shippingPrice = parseFloat(Number(item.shippingCharge) || 0); const qty = item.prodQty || item.qty || 1; totalItems += productPrice * qty; totalOption += optionPrice * qty; totalShipping += shippingPrice; totalQuantity += qty; if (DEBUG_LOG) { console.log('[CartSidebar] Item calculation:', { name: item.prdtNm, qty, itemPrice: productPrice, itemOption: optionPrice, itemShipping: shippingPrice, runningTotal: totalItems }); } }); const subtotal = Math.max(0, totalItems + totalOption + totalShipping); if (DEBUG_LOG) { console.log('[CartSidebar] Final calculation:', { isCheckedMode: checkedItems && checkedItems.length > 0, itemCount: itemsToCalculate.length, totalQuantity, totalItems, totalOption, totalShipping, subtotal, }); } return { itemCount: totalQuantity, subtotal: totalItems, optionTotal: totalOption, shippingHandling: totalShipping, orderTotalBeforeTax: subtotal, }; } else { // 상품이 없는 경우 return { itemCount: 0, subtotal: 0, optionTotal: 0, shippingHandling: 0, orderTotalBeforeTax: 0, }; } }, [itemsToCalculate, checkedItems]); // 체크아웃 버튼 클릭 핸들러 const handleCheckoutClick = useCallback(() => { const DEBUG_LOG = true; // 수동 설정: true로 활성화 (디버깅용) console.log('[CartSidebar] 🎯 Checkout button clicked! Starting checkout process...'); console.log('[CartSidebar] 📊 Current state:', { isMockMode, checkedItemsCount: checkedItems.length, itemsToCalculateCount: itemsToCalculate.length }); // ✅ 계산할 상품이 없는 경우 if (itemsToCalculate.length === 0) { console.log('[CartSidebar] ❌ No items to checkout'); return; } if (DEBUG_LOG) { console.log('[CartSidebar] Items to checkout:', JSON.stringify(itemsToCalculate)); } // ✅ CheckOutPanel 전송용 데이터 구성 const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData; const checkoutData = { // 하위 호환성: 첫 번째 상품을 productInfo로 productInfo: itemsToCalculate[0], // ✅ CheckOutPanel이 기대하는 cartItems 필드 cartItems: itemsToCalculate.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, prodSno: item.prodSno, })), // 메타데이터 isFromCart: true, fromCartPanel: true, isCheckedMode: checkedItems && checkedItems.length > 0, // ✅ 체크 모드 여부 cartTotal: orderTotalBeforeTax, itemCount: itemCount, subtotal: subtotal, optionTotal: optionTotal, shippingHandling: shippingHandling }; if (DEBUG_LOG) { console.log('%c🚨🚨🚨 CART SIDEBAR CHECKOUT DATA 🚨🚨🚨', 'background: yellow; color: black; font-weight: bold; font-size: 14px; padding: 5px;'); console.log('%cMode:', 'background: yellow; color: black; padding: 3px;', checkedItems.length > 0 ? 'CHECKED ITEMS MODE' : 'ALL ITEMS MODE'); console.log('%cItems to checkout:', 'background: yellow; color: black; padding: 3px;', itemsToCalculate); 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('%cCalculated totals:', 'background: yellow; color: black; padding: 3px;', { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax }); } // ✅ CheckOutPanel로 이동 console.log('[CartSidebar] 🚀 Executing pushPanel to CHECKOUT_PANEL'); try { dispatch( getMyInfoCheckoutInfo( { mbrNo: userNumber, dirPurcSelYn: 'Y', cartList: checkedItems && checkedItems.map((item) => ({ patnrId: item.patnrId, prdtId: item.prdtId, prodOptCdCval: item.prodOptCdCval ? item.prodOptCdCval : null, prodQty: String(item.prodQty), prodOptTpCdCval: item.prodOptTpCdCval, })) }, checkOutValidate ) ); // dispatch(pushPanel({ // name: Config.panel_names.CHECKOUT_PANEL, // panelInfo: checkoutData // })); console.log('[CartSidebar] ✅ pushPanel executed successfully'); } catch (error) { console.error('[CartSidebar] ❌ pushPanel failed:', error); } }, [dispatch, checkedItems, itemsToCalculate, calculatedData]); const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData; const formatPrice = (price) => { return `$${price.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; }; return (
Subtotal
{userNumber ? itemCount : 0} Items
Subtotal {userNumber ? formatPrice(subtotal) : 0}
Option {userNumber ? formatPrice(optionTotal) : 0}
S&H {userNumber ? formatPrice(shippingHandling) : 0}
Order Total
(Before Tax)
{userNumber ? formatPrice(orderTotalBeforeTax) : 0}

• Final costs will be available on your Order Review before you place order

• By proceeding, you agree to ShopTime's General Terms of Use and acknowledge the Privacy Statement

Checkout
); }; export default CartSidebar;