[251102] fix: CartPanel mock-2

🕐 커밋 시간: 2025. 11. 02. 11:04:08

📊 변경 통계:
  • 총 파일: 10개
  • 추가: +453줄
  • 삭제: -162줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/mockCartActions.js
  ~ com.twin.app.shoptime/src/reducers/mockCartReducer.js
  ~ com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js
  ~ com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx
  ~ com.twin.app.shoptime/src/views/CartPanel/CartProduct.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/SummaryContainerMock.jsx
  ~ com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx
  ~ com.twin.app.shoptime/src/views/MainView/MainView.jsx

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • 공통 유틸리티 함수 최적화
  • UI 컴포넌트 아키텍처 개선
  • 대규모 기능 개발
  • 모듈 구조 개선
This commit is contained in:
2025-11-02 11:04:13 +09:00
parent efeb45823e
commit 0f755cac53
10 changed files with 450 additions and 159 deletions

View File

@@ -1,5 +1,5 @@
import { BUYNOW_CONFIG } from '../utils/BuyNowConfig';
import { createMockCartListData, addMockCartItem, removeMockCartItem, updateMockCartItemQuantity } from '../utils/BuyNowDataManipulator';
import { createMockCartListData, createMockCartData, addMockCartItem, removeMockCartItem, updateMockCartItemQuantity } from '../utils/BuyNowDataManipulator';
// Mock Cart Action Types
export const MOCK_CART_TYPES = {
@@ -9,11 +9,12 @@ export const MOCK_CART_TYPES = {
UPDATE_MOCK_CART_ITEM: 'mockCart/UPDATE_MOCK_CART_ITEM',
CLEAR_MOCK_CART: 'mockCart/CLEAR_MOCK_CART',
SET_MOCK_CART_QUANTITY: 'mockCart/SET_MOCK_CART_QUANTITY',
UPDATE_SELECTED_ITEMS: 'mockCart/UPDATE_SELECTED_ITEMS',
};
/**
* Mock 장바구니 초기화
* BuyOption에서 ADD TO CART 시 호출
* BuyOption에서 ADD TO CART 시 호출 - 기존 장바구니에 상품 추가
*/
export const initializeMockCart = (productData, optionInfo = {}, quantity = 1) => (dispatch, getState) => {
if (!BUYNOW_CONFIG.isMockMode()) {
@@ -22,20 +23,19 @@ export const initializeMockCart = (productData, optionInfo = {}, quantity = 1) =
console.log('[MockCartActions] initializeMockCart - productData:', productData);
// 초기 Mock 장바구니 데이터 생성
const mockCartData = createMockCartListData(productData, optionInfo, quantity);
// 기존 장바구니 데이터 확인
const currentCart = getState().mockCart.cartInfo || [];
console.log('[MockCartActions] initializeMockCart - current cart items:', currentCart.length);
dispatch({
type: MOCK_CART_TYPES.INIT_MOCK_CART,
payload: {
items: mockCartData,
lastAction: {
type: 'init',
data: productData,
timestamp: Date.now(),
},
},
});
// 새 상품 데이터 생성
const newCartItem = createMockCartData(productData, optionInfo, quantity);
if (newCartItem) {
// addToMockCart를 사용하여 기존 장바구니에 상품 추가 (덮어쓰기 방지)
dispatch(addToMockCart(productData, optionInfo, quantity));
} else {
console.log('[MockCartActions] initializeMockCart - Failed to create cart item');
}
};
/**
@@ -177,22 +177,20 @@ export const clearMockCart = () => (dispatch, getState) => {
};
/**
* Mock 장바구니 데이터 재설정 (초기 상태로 복원)
* Mock 장바구니 데이터 재설정 (빈 장바구니로 복원)
*/
export const resetMockCart = () => (dispatch, getState) => {
if (!BUYNOW_CONFIG.isMockMode()) {
return;
}
console.log('[MockCartActions] resetMockCart');
// 기본 Mock 장바구니 데이터로 재설정
const defaultMockCartData = createMockCartListData();
console.log('[MockCartActions] resetMockCart - Clearing cart to empty');
// 빈 장바구니로 재설정 (기본 Mock 상품 없음)
dispatch({
type: MOCK_CART_TYPES.INIT_MOCK_CART,
payload: {
items: defaultMockCartData,
items: [], // 빈 배열로 재설정
lastAction: {
type: 'reset',
timestamp: Date.now(),
@@ -200,3 +198,26 @@ export const resetMockCart = () => (dispatch, getState) => {
},
});
};
/**
* 선택된 상품들 업데이트
* @param {Array} selectedItems - 선택된 상품들의 prodSno 배열
*/
export const updateSelectedItems = (selectedItems) => (dispatch, getState) => {
if (!BUYNOW_CONFIG.isMockMode()) {
return;
}
console.log('[MockCartActions] updateSelectedItems - selectedItems:', selectedItems);
dispatch({
type: MOCK_CART_TYPES.UPDATE_SELECTED_ITEMS,
payload: {
selectedItems,
lastAction: {
type: 'update_selected',
timestamp: Date.now(),
},
},
});
};

View File

@@ -10,6 +10,8 @@ const isBrowser = typeof window !== 'undefined' && typeof window.localStorage !=
const initialState = {
// Mock 장바구니 목록
cartInfo: [],
// 선택된 상품들
selectedItems: [],
// 마지막 실행된 액션 정보
lastAction: null,
// 에러 정보
@@ -34,6 +36,7 @@ const saveToLocalStorage = (state) => {
try {
const dataToSave = {
cartInfo: state.cartInfo,
selectedItems: state.selectedItems,
totalQuantity: state.totalQuantity,
totalPrice: state.totalPrice,
lastUpdated: state.lastUpdated,
@@ -57,6 +60,7 @@ const loadFromLocalStorage = () => {
return {
...initialState,
cartInfo: parsedData.cartInfo || [],
selectedItems: parsedData.selectedItems || [],
totalQuantity: parsedData.totalQuantity || 0,
totalPrice: parsedData.totalPrice || 0,
lastUpdated: parsedData.lastUpdated || Date.now(),
@@ -254,6 +258,7 @@ export const mockCartReducer = (state = loadFromLocalStorage(), action) => {
const newState = {
...state,
cartInfo: [],
selectedItems: [], // 선택된 상품들도 초기화
lastAction: action.payload.lastAction,
error: null,
lastUpdated: Date.now(),
@@ -266,6 +271,21 @@ export const mockCartReducer = (state = loadFromLocalStorage(), action) => {
return newState;
}
case MOCK_CART_TYPES.UPDATE_SELECTED_ITEMS: {
const { selectedItems } = action.payload;
const newState = {
...state,
selectedItems,
lastAction: action.payload.lastAction,
lastUpdated: Date.now(),
};
// localStorage에 저장
saveToLocalStorage(newState);
return newState;
}
default:
return state;
}

View File

@@ -107,82 +107,18 @@ export const createMockCartListData = (productData, optionInfo = {}, quantity =
return [];
}
// ✅ 기본 Mock 장바구니 데이터 (CartPanel 구조에 맞춰 개선 - 모든 이미지 필드 포함)
const defaultMockItems = [
// QVC 상품
{
prodSno: 'MOCK_CART_1',
prdtId: 'MOCK_QVC_001',
prdtNm: 'Mock Premium Wireless Headphones',
patnrId: 'QVC',
patncNm: 'QVC',
patncLogPath: '/assets/images/ic-partners-qvc@3x.png',
imgUrl: '/assets/images/img-thumb-empty-144@3x.png',
thumbnailUrl: '/assets/images/img-thumb-empty-144@3x.png',
imgList: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }],
imgUrls: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }], // CheckOutPanel 호환성
price2: '299.99', // 원가
price3: '199.99', // 할인가
price5: '29.99', // 옵션 할인가
optPrc: '29.99', // 옵션 가격
shippingCharge: '12.99',
prodQty: 1,
optNm: 'Color: Black, Warranty: 2 Years',
addedAt: new Date().toISOString()
},
// HSN 상품
{
prodSno: 'MOCK_CART_2',
prdtId: 'MOCK_HSN_001',
prdtNm: 'Mock Smart Watch Pro',
patnrId: 'HSN',
patncNm: 'HSN',
patncLogPath: '/assets/images/ic-partners-hsn@3x.png',
imgUrl: '/assets/images/img-thumb-empty-144@3x.png',
thumbnailUrl: '/assets/images/img-thumb-empty-144@3x.png',
imgList: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }],
imgUrls: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }], // CheckOutPanel 호환성
price2: '399.99',
price3: '299.99',
price5: '49.99',
optPrc: '49.99',
shippingCharge: '9.99',
prodQty: 2,
optNm: 'Color: Silver, Size: 42mm',
addedAt: new Date().toISOString()
},
// 다른 파트너사 상품
{
prodSno: 'MOCK_CART_3',
prdtId: 'MOCK_EVINE_001',
prdtNm: 'Mock Luxury Skincare Set',
patnrId: 'EVINE',
patncNm: 'EVINE',
patncLogPath: '/assets/images/ic-partners-evine@3x.png',
imgUrl: '/assets/images/img-thumb-empty-144@3x.png',
thumbnailUrl: '/assets/images/img-thumb-empty-144@3x.png',
imgList: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }],
imgUrls: [{ imgUrl: '/assets/images/img-thumb-empty-144@3x.png' }], // CheckOutPanel 호환성
price2: '149.99',
price3: '99.99',
price5: '19.99',
optPrc: '19.99',
shippingCharge: '7.99',
prodQty: 1,
optNm: 'Size: Full Set, Type: Anti-Aging',
addedAt: new Date().toISOString()
}
];
// 초기 상품 데이터가 있는 경우
// 초기 상품 데이터가 있는 경우에만 해당 상품 추가
if (productData) {
const newCartItem = createMockCartData(productData, optionInfo, quantity);
if (newCartItem) {
return [newCartItem, ...defaultMockItems];
console.log('[BuyNowDataManipulator] createMockCartListData - Adding only user product:', newCartItem.prdtNm);
return [newCartItem];
}
}
return defaultMockItems;
// 기본 Mock 상품은 추가하지 않음
console.log('[BuyNowDataManipulator] createMockCartListData - No product data, returning empty array');
return [];
};
// Mock 장바구니 데이터를 저장할 전역 변수

View File

@@ -86,22 +86,26 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
console.log('[CartPanel] Component mounted - isMockMode:', isMockMode, 'panelInfo:', panelInfo);
if (isMockMode) {
// Mock Mode: panelInfo가 있으면 해당 상품 추가, 없으면 기본 Mock 데이터 설정
// Mock Mode: panelInfo가 있으면 해당 상품 추가
if (panelInfo?.productInfo) {
console.log('[CartPanel] Mock Mode - Adding product from panelInfo:', panelInfo.productInfo);
console.log('[CartPanel] Mock Mode - Option info:', panelInfo.optionInfo);
console.log('[CartPanel] Mock Mode - Quantity:', panelInfo.quantity);
// panelInfo의 상품 정보로 Mock 장바구니 초기화 (기존 데이터 유지)
// panelInfo의 상품 정보로 Mock 장바구니에 상품 추가 (기존 데이터 유지)
console.log('[CartPanel] Mock Mode - Adding product to existing cart:', panelInfo.productInfo.prdtNm);
dispatch(initializeMockCart(
panelInfo.productInfo,
panelInfo.optionInfo || {},
panelInfo.quantity || 1
));
} else {
// panelInfo가 없으면 기본 Mock 장바구니 데이터로 설정
console.log('[CartPanel] Mock Mode - Initializing with default mock data');
} else if (!mockCartData || mockCartData.length === 0) {
// localStorage에 데이터가 없을 때만 기본 Mock 데이터로 설정
console.log('[CartPanel] Mock Mode - No existing cart data, initializing with default mock data');
dispatch(resetMockCart());
} else {
// localStorage에 데이터가 있으면 그대로 사용
console.log('[CartPanel] Mock Mode - Using existing cart data from localStorage:', mockCartData.length, 'items');
}
} else if (userNumber) {
// API Mode: 실제 API 호출
@@ -110,6 +114,17 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
}
}, [dispatch, userNumber, isMockMode, panelInfo]);
// Mock 장바구니 데이터 변경 감지 (디버깅용)
useEffect(() => {
console.log('[CartPanel] mockCartData changed:', mockCartData?.length, 'items');
console.log('[CartPanel] mockCartData:', JSON.stringify(mockCartData));
}, [mockCartData]);
// displayCartData 변경 감지 (디버깅용)
useEffect(() => {
console.log('[CartPanel] displayCartData changed:', displayCartData?.length, 'items');
}, [displayCartData]);
const {
getScrollTo,
getScrollTo: getScrollToBody,

View File

@@ -8,6 +8,8 @@ import React, {
import classNames from 'classnames';
import { useSelector, useDispatch } from 'react-redux';
import { updateSelectedItems } from '../../actions/mockCartActions';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
@@ -30,6 +32,7 @@ const CartProduct = ({ cartInfo }) => {
// 항상 호출되어야 하는 Hook들
const fallbackCartData = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
const selectedItems = useSelector((state) => state.mockCart.selectedItems || []);
// 실제 장바구니 데이터와 Mock 데이터 중 선택
const cartData = cartInfo || fallbackCartData;
@@ -129,6 +132,27 @@ const CartProduct = ({ cartInfo }) => {
}
}, [dispatch, isMockMode]);
// 체크박스 선택 핸들러
const handleCheckboxChange = useCallback((prodSno, isChecked) => {
if (isMockMode) {
let newSelectedItems;
if (isChecked) {
// 상품 선택
newSelectedItems = [...selectedItems, prodSno];
} else {
// 상품 선택 해제
newSelectedItems = selectedItems.filter(id => id !== prodSno);
}
dispatch(updateSelectedItems(newSelectedItems));
console.log('[CartProduct] Checkbox changed - prodSno:', prodSno, 'isChecked:', isChecked, 'selectedItems:', newSelectedItems);
}
}, [dispatch, isMockMode, selectedItems]);
// 상품이 선택되었는지 확인
const isItemSelected = useCallback((prodSno) => {
return selectedItems.includes(prodSno);
}, [selectedItems]);
return (
<>
{Object.entries(groupedCartData).map(([partnerKey, group]) => {
@@ -196,7 +220,9 @@ const CartProduct = ({ cartInfo }) => {
<div className={css.checkBox}>
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId="productCheckbox"
spotlightId={`productCheckbox-${item.prodSno}`}
checked={isItemSelected(item.prodSno)}
onChange={(isChecked) => handleCheckboxChange(item.prodSno, isChecked)}
/>
<span className={css.productId}>
ID : {item.prdtId}

View File

@@ -5,6 +5,7 @@ import TButton from '../../components/TButton/TButton';
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
import { pushPanel } from '../../actions/panelActions';
import Config from '../../utils/Config';
import { calculateOrderSummaryFromProductInfo } from '../../utils/mockDataSafetyUtils';
import css from './CartSidebar.module.less';
const CartSidebar = ({ cartInfo }) => {
@@ -15,21 +16,118 @@ const CartSidebar = ({ cartInfo }) => {
// 실제 장바구니 데이터 (API 모드일 때만 사용)
const fallbackCartInfo = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
const selectedItems = useSelector((state) => state.mockCart.selectedItems || []);
// 사용할 장바구니 데이터 결정
const displayCartInfo = cartInfo || (isMockMode ? null : fallbackCartInfo);
// Mock 데이터 또는 실제 데이터 계산
// 선택된 상품들만 필터링 - 항상 선택된 상품들만 반환
const getSelectedItems = useCallback((items) => {
console.log('[CartSidebar] getSelectedItems called - isMockMode:', isMockMode, 'selectedItems:', selectedItems);
if (!items || !Array.isArray(items)) {
console.log('[CartSidebar] No items provided, returning empty array');
return [];
}
if (!isMockMode) {
console.log('[CartSidebar] API Mode - returning all items');
return items;
}
if (selectedItems.length === 0) {
console.log('[CartSidebar] No items selected, returning empty array');
return []; // 선택된 상품이 없으면 빈 배열 반환
}
const filtered = items.filter(item => {
const itemId = item.prodSno || item.cartId;
const isSelected = selectedItems.includes(itemId);
console.log('[CartSidebar] Item filter:', {
itemName: item.prdtNm,
itemId,
isSelected
});
return isSelected;
});
console.log('[CartSidebar] Filtered selected items:', filtered.length, 'out of', items.length);
return filtered;
}, [isMockMode, selectedItems]);
// Mock 데이터 또는 실제 데이터 계산 (선택된 상품만) - CheckOutPanel 방식 적용
const calculatedData = useMemo(() => {
if (isMockMode) {
// Mock Mode: 기본 mockData 사용
return {
itemCount: 3,
subtotal: 199.97,
optionTotal: 29.99,
shippingHandling: 19.99,
orderTotalBeforeTax: 249.95,
};
// Mock Mode: 선택된 상품들로 개별 가격 계산
if (displayCartInfo && Array.isArray(displayCartInfo) && displayCartInfo.length > 0) {
const selectedCartItems = getSelectedItems(displayCartInfo);
console.log('[CartSidebar] Selected items for calculation:', selectedCartItems);
// CheckOutPanel 방식: 각 상품의 정확한 가격 추출
let totalItems = 0;
let totalCoupon = 0;
let totalShipping = 0;
let totalQuantity = 0;
selectedCartItems.forEach((item) => {
console.log('[CartSidebar] Processing item:', item.prdtNm, 'price fields:', {
price2: item.price2,
price3: item.price3,
price5: item.price5,
finalPrice: item.finalPrice,
discountPrice: item.discountPrice,
});
// CheckOutPanel의 calculateOrderSummaryFromProductInfo 로직 사용
const orderSummary = calculateOrderSummaryFromProductInfo(item);
const qty = item.prodQty || item.qty || 1;
const itemPrice = orderSummary.items; // 개별 상품 가격
const itemCoupon = orderSummary.couponSavings; // 개별 상품 쿠폰
const itemShipping = orderSummary.shipping; // 개별 상품 배송비
totalItems += itemPrice * qty;
totalCoupon += itemCoupon * qty;
totalShipping += itemShipping * qty; // 배송비도 수량만큼 계산
totalQuantity += qty;
console.log('[CartSidebar] Item calculation:', {
name: item.prdtNm,
qty,
itemPrice,
itemCoupon,
itemShipping,
runningTotal: totalItems
});
});
const subtotal = Math.max(0, totalItems - totalCoupon + totalShipping);
console.log('[CartSidebar] Final calculation for selected items:', {
totalQuantity,
totalItems,
totalCoupon,
totalShipping,
subtotal,
});
return {
itemCount: totalQuantity,
subtotal: totalItems,
optionTotal: totalCoupon,
shippingHandling: totalShipping,
orderTotalBeforeTax: subtotal,
};
} else {
// Mock Mode에 데이터가 없는 경우
return {
itemCount: 0,
subtotal: 0,
optionTotal: 0,
shippingHandling: 0,
orderTotalBeforeTax: 0,
};
}
} else if (displayCartInfo && Array.isArray(displayCartInfo) && displayCartInfo.length > 0) {
// API Mode: 실제 장바구니 데이터로 계산
const itemCount = displayCartInfo.reduce((sum, item) => sum + (item.prodQty || 1), 0);
@@ -62,21 +160,36 @@ const CartSidebar = ({ cartInfo }) => {
orderTotalBeforeTax: 0,
};
}
}, [isMockMode, displayCartInfo]);
}, [isMockMode, displayCartInfo, getSelectedItems]);
// 체크아웃 버튼 클릭 핸들러
const handleCheckoutClick = useCallback(() => {
if (isMockMode) {
// Mock Mode: 바로 체크아웃 패널로 이동
console.log('[CartSidebar] Mock Mode - Going to Checkout');
dispatch(pushPanel({
name: Config.panel_names.CHECKOUT_PANEL,
}));
console.log('[CartSidebar] Mock Mode - cartInfo:', JSON.stringify(cartInfo));
// ✅ panelInfo에 cartInfo를 포함하여 전달
// CheckOutPanel에서 productList로 사용할 수 있도록 구성
const cartItems = cartInfo && Array.isArray(cartInfo) ? cartInfo : [];
console.log('[CartSidebar] Mock Mode - cartItems:', JSON.stringify(cartItems));
// dispatch(pushPanel({
// name: Config.panel_names.CHECKOUT_PANEL,
// panelInfo: {
// productInfo: cartItems.length > 0 ? cartItems[0] : null, // 첫 번째 상품
// cartItems: cartItems, // 전체 카트 아이템
// isFromCart: true, // CartPanel에서 왔음을 표시
// }
// }));
} else {
// API Mode: 실제 로직 (향후 구현)
console.log('[CartSidebar] API Mode - Checkout (to be implemented)');
}
}, [dispatch, isMockMode]);
}, [dispatch, isMockMode, cartInfo]);
const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData;

View File

@@ -115,20 +115,28 @@ export default function CheckOutPanel({ panelInfo }) {
? (() => {
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);
// 1순위: BuyOption에서 전달된 productInfo (Mock Mode에서는 검증 최소화)
// 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);
return panelInfo.cartItems;
}
// 2순위: BuyOption에서 전달된 productInfo (단일 상품)
if (panelInfo?.productInfo) {
console.log('%c[BuyOption][CheckOutPanel] ✅ Mock Mode - Using panelInfo.productInfo (no validation):', 'background: green; color: white; font-weight: bold; padding: 5px;', 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);
return [panelInfo.productInfo];
}
// 2순위: Redux에서 가져온 상품 데이터
// 3순위: Redux에서 가져온 상품 데이터
if (reduxProductData && Array.isArray(reduxProductData) && reduxProductData.length > 0) {
console.log('%c[BuyOption][CheckOutPanel] ✅ Mock Mode - Using reduxProductData:', 'background: green; color: white; font-weight: bold; padding: 5px;', 'count=' + reduxProductData.length);
return reduxProductData;
}
// 3순위: 기본 Hardcoded Mock 데이터 (최후의 fallback)
// 4순위: 기본 Hardcoded Mock 데이터 (최후의 fallback)
console.error('%c[BuyOption][CheckOutPanel] ⚠️ Mock Mode - Using fallback mock data:', 'background: orange; color: white; font-weight: bold; padding: 5px;', 'panelInfo=', panelInfo, 'reduxProductData=', reduxProductData);
return [
{

View File

@@ -25,47 +25,88 @@ export default function SummaryContainerMock({
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productInfo.price2:', productInfo?.price2);
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - productInfo.price5:', productInfo?.price5);
// Mock Mode: productInfo로부터 ORDER SUMMARY용 가격 데이터 계산
// Mock Mode: productData 배열 전체를 합산하거나 productInfo 사용
const orderSummaryData = useMemo(() => {
if (productInfo) {
// productInfo가 있으면 직접 계산
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using calculateOrderSummaryFromProductInfo');
return calculateOrderSummaryFromProductInfo(productInfo);
} else {
// productInfo가 없으면 기존 방식으로 fallback
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using fallback calculation (no productInfo)');
const selectedProduct = getSafeFirstProduct(productData);
// ✅ 1순위: CartPanel의 여러 상품 데이터 (productData 배열)
if (Array.isArray(productData) && productData.length > 0) {
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using calculateOrderSummaryFromProductData (multiple items)');
// 최소한 하나의 가격이라도 찾기
const anyPrice = parseFloat(
defaultPrice ||
selectedProduct?.finalPrice ||
selectedProduct?.discountPrice ||
selectedProduct?.price ||
0
);
// 전체 상품의 가격 합산
let totalItems = 0;
let totalCoupon = 0;
let totalShipping = 0;
const productPrice = anyPrice > 0 ? anyPrice : 0;
const productDiscount = parseFloat(selectedProduct?.discount || 0);
const tax = Math.round((productPrice * 0.1) * 100) / 100;
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);
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Fallback calculation:', {
productPrice,
productDiscount,
tax,
selectedProduct
totalItems += price * qty;
totalCoupon += coupon * qty;
totalShipping += 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
});
return {
items: productPrice,
couponSavings: productDiscount,
shipping: 0,
subtotal: Math.max(0, productPrice - productDiscount),
items: totalItems,
couponSavings: totalCoupon,
shipping: totalShipping,
subtotal: subtotal,
tax: tax,
total: Math.max(0, productPrice - productDiscount) + tax,
total: subtotal + tax,
currency: { currSign, currSignLoc }
};
}
// 2순위: productInfo (단일 상품)
if (productInfo) {
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using calculateOrderSummaryFromProductInfo (single product)');
return calculateOrderSummaryFromProductInfo(productInfo);
}
// 3순위: 기존 방식으로 fallback
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Using fallback calculation (no data)');
const selectedProduct = getSafeFirstProduct(productData);
const anyPrice = parseFloat(
defaultPrice ||
selectedProduct?.finalPrice ||
selectedProduct?.discountPrice ||
selectedProduct?.price ||
0
);
const productPrice = anyPrice > 0 ? anyPrice : 0;
const productDiscount = parseFloat(selectedProduct?.discount || 0);
const tax = Math.round((productPrice * 0.1) * 100) / 100;
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - Fallback calculation:', {
productPrice,
productDiscount,
tax,
selectedProduct
});
return {
items: productPrice,
couponSavings: productDiscount,
shipping: 0,
subtotal: Math.max(0, productPrice - productDiscount),
tax: tax,
total: Math.max(0, productPrice - productDiscount) + tax,
currency: { currSign, currSignLoc }
};
}, [productInfo, productData, defaultPrice, currSign, currSignLoc]);
console.log('[BuyOption][CheckOutPanel] SummaryContainerMock - orderSummaryData:', orderSummaryData);

View File

@@ -823,9 +823,51 @@ const BuyOption = ({
productInfo?.patncLogPath ||
'/assets/images/img-thumb-empty-144@3x.png';
// 가격 정보 구성
const regularPrice = productInfo?.regularPrice || 299.99;
const discountPrice = productInfo?.discountPrice || regularPrice;
// ✅ 정확한 가격 추출 (Mock Mode와 동일한 로직)
console.log('[BuyOption] API Mode ADD TO CART - Extracting accurate prices from productInfo');
console.log('[BuyOption] API Mode ADD TO CART - productInfo.price2:', productInfo?.price2);
console.log('[BuyOption] API Mode ADD TO CART - productInfo.price5:', productInfo?.price5);
console.log('[BuyOption] API Mode ADD TO CART - productInfo.priceInfo:', productInfo?.priceInfo);
// 정확한 가격 추출 로직
const extractNumericPrice = (value) => {
if (typeof value === 'number') return value;
if (typeof value === 'string') {
const numMatch = value.match(/[\d.]+/);
return numMatch ? parseFloat(numMatch[0]) : 0;
}
return 0;
};
const price2Value = extractNumericPrice(productInfo?.price2);
const price5Value = extractNumericPrice(productInfo?.price5);
console.log('[BuyOption] API Mode ADD TO CART - extracted price2Value:', price2Value);
console.log('[BuyOption] API Mode ADD TO CART - extracted price5Value:', price5Value);
// 정확한 가격 계산 (ProductAllSection의 가격 정보 사용)
let discountPrice = price2Value > 0 ? price2Value : 521.66; // ProductAllSection 가격 fallback
let regularPrice = price2Value + price5Value;
// priceInfo에서 직접 가격 추출 시도
if (productInfo?.priceInfo) {
const priceParts = productInfo.priceInfo.split('|');
if (priceParts.length >= 2) {
const infoDiscountPrice = extractNumericPrice(priceParts[1]);
const infoRegularPrice = extractNumericPrice(priceParts[0]);
if (infoDiscountPrice > 0) {
discountPrice = infoDiscountPrice;
regularPrice = infoRegularPrice > 0 ? infoRegularPrice : discountPrice;
}
}
}
console.log('[BuyOption] API Mode ADD TO CART - Final calculated prices:', {
discountPrice,
regularPrice,
price2Value,
price5Value
});
const productInfoForCart = {
// 기본 정보
@@ -843,9 +885,15 @@ const BuyOption = ({
imgUrls: productInfo?.imgUrls || [{ imgUrl: imgUrl }], // imgUrls 배열 구조 추가 (CheckOutPanel 호환성)
imgUrls600: productInfo?.imgUrls600, // ✅ 고품질 이미지 배열 포함
// 가격 정보 (문자열 형식으로)
price2: regularPrice.toFixed(2), // 원가
price3: discountPrice.toFixed(2), // 할인가/판매가
// 가격 정보 (정확한 가격 정보 - 문자열 형식으로)
price2: regularPrice.toFixed(2), // 원가 (ProductAllSection 가격)
price3: discountPrice.toFixed(2), // 할인가/판매가 (ProductAllSection 가격)
// 추가 가격 필드들 (다양한 fallback 지원)
finalPrice: discountPrice,
discountPrice: discountPrice,
origPrice: regularPrice,
discount: Math.max(0, regularPrice - discountPrice),
priceInfo: productInfo?.priceInfo, // 원본 priceInfo 보존
// 수량 정보
prodQty: quantity,
@@ -865,6 +913,11 @@ const BuyOption = ({
price: productOptionInfos[0]?.prdtOptDtl[0]?.optPrc || '0.00'
};
console.log('[BuyOption] 🛒 API Mode - Pushing CART_PANEL with name:', Config.panel_names.CART_PANEL);
console.log('[BuyOption] 🛒 API Mode - productInfoForCart:', productInfoForCart);
console.log('[BuyOption] 🛒 API Mode - optionInfoForCart:', optionInfoForCart);
console.log('[BuyOption] 🛒 API Mode - quantity:', quantity);
dispatch(
pushPanel({
name: Config.panel_names.CART_PANEL,
@@ -887,9 +940,51 @@ const BuyOption = ({
productInfo?.patncLogPath ||
'/assets/images/img-thumb-empty-144@3x.png';
// 가격 정보 구성 (CheckOutPanel과 동일한 방식)
const regularPrice = productInfo?.regularPrice || 299.99;
const discountPrice = productInfo?.discountPrice || regularPrice;
// ✅ 정확한 가격 추출 (handleBuyNowClick와 동일한 로직 사용)
console.log('[BuyOption] Mock Mode ADD TO CART - Extracting accurate prices from productInfo');
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.price2:', productInfo?.price2);
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.price5:', productInfo?.price5);
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.priceInfo:', productInfo?.priceInfo);
// handleBuyNowClick의 가격 추출 로직과 동일하게 적용
const extractNumericPrice = (value) => {
if (typeof value === 'number') return value;
if (typeof value === 'string') {
const numMatch = value.match(/[\d.]+/);
return numMatch ? parseFloat(numMatch[0]) : 0;
}
return 0;
};
const price2Value = extractNumericPrice(productInfo?.price2);
const price5Value = extractNumericPrice(productInfo?.price5);
console.log('[BuyOption] Mock Mode ADD TO CART - extracted price2Value:', price2Value);
console.log('[BuyOption] Mock Mode ADD TO CART - extracted price5Value:', price5Value);
// 정확한 가격 계산 (ProductAllSection의 가격 정보 사용)
let discountPrice = price2Value > 0 ? price2Value : 521.66; // ProductAllSection 가격 fallback
let regularPrice = price2Value + price5Value;
// priceInfo에서 직접 가격 추출 시도
if (productInfo?.priceInfo) {
const priceParts = productInfo.priceInfo.split('|');
if (priceParts.length >= 2) {
const infoDiscountPrice = extractNumericPrice(priceParts[1]);
const infoRegularPrice = extractNumericPrice(priceParts[0]);
if (infoDiscountPrice > 0) {
discountPrice = infoDiscountPrice;
regularPrice = infoRegularPrice > 0 ? infoRegularPrice : discountPrice;
}
}
}
console.log('[BuyOption] Mock Mode ADD TO CART - Final calculated prices:', {
discountPrice,
regularPrice,
price2Value,
price5Value
});
// Mock 상품 정보 구성 (CheckOutPanel 구조 참고)
const mockProductInfo = {
@@ -908,9 +1003,15 @@ const BuyOption = ({
imgUrls: productInfo?.imgUrls || [{ imgUrl: imgUrl }], // imgUrls 배열 구조 추가 (CheckOutPanel 호환성)
imgUrls600: productInfo?.imgUrls600, // ✅ 고품질 이미지 배열 포함
// 가격 정보 (문자열 형식으로)
price2: regularPrice.toFixed(2), // 원가
price3: discountPrice.toFixed(2), // 할인가/판매가
// 가격 정보 (정확한 가격 정보 - 문자열 형식으로)
price2: regularPrice.toFixed(2), // 원가 (ProductAllSection 가격)
price3: discountPrice.toFixed(2), // 할인가/판매가 (ProductAllSection 가격)
// 추가 가격 필드들 (다양한 fallback 지원)
finalPrice: discountPrice,
discountPrice: discountPrice,
origPrice: regularPrice,
discount: Math.max(0, regularPrice - discountPrice),
priceInfo: productInfo?.priceInfo, // 원본 priceInfo 보존
// 수량 정보
prodQty: quantity,
@@ -933,6 +1034,11 @@ const BuyOption = ({
};
// CartPanel로 이동 (productInfo 포함) - CartPanel에서 직접 상품 추가
console.log('[BuyOption] 🛒 Pushing CART_PANEL with name:', Config.panel_names.CART_PANEL);
console.log('[BuyOption] 🛒 mockProductInfo:', mockProductInfo);
console.log('[BuyOption] 🛒 optionInfo:', optionInfo);
console.log('[BuyOption] 🛒 quantity:', quantity);
dispatch(
pushPanel({
name: Config.panel_names.CART_PANEL,

View File

@@ -201,8 +201,13 @@ export default function MainView({ className, initService }) {
const topPanel = panels[panels.length - 1];
// 단독 패널 체크 - CheckOutPanel, CartPanel 등 단독으로 렌더링되어야 하는 패널들
console.log(`[MainView] 🔍 Top panel name: ${topPanel?.name}`);
console.log(`[MainView] 🔍 isStandalonePanel check:`, isStandalonePanel(topPanel?.name));
console.log(`[MainView] 🔍 STANDALONE_PANELS:`, STANDALONE_PANELS);
console.log(`[MainView] 🔍 All panels:`, panels.map(p => ({ name: p.name, hasModal: !!p.panelInfo?.modal })));
if (isStandalonePanel(topPanel?.name)) {
console.log(`[MainView] Standalone panel detected: ${topPanel?.name} - rendering independently`);
console.log(`[MainView] Standalone panel detected: ${topPanel?.name} - rendering independently`);
renderingPanels = [topPanel]; // 단독 패널만 단독으로 렌더링
}
// 기존 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)