diff --git a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx index f61dba6e..79c1e6b1 100644 --- a/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx +++ b/com.twin.app.shoptime/src/components/VideoPlayer/TReactPlayer.jsx @@ -22,6 +22,7 @@ export default function TReactPlayer({ mediaEventsMap = handledMediaEventsMap, videoRef, url, + dispatch, ...rest }) { const playerRef = useRef(null); diff --git a/com.twin.app.shoptime/src/reducers/mockCartReducer.js b/com.twin.app.shoptime/src/reducers/mockCartReducer.js new file mode 100644 index 00000000..32ab3ac2 --- /dev/null +++ b/com.twin.app.shoptime/src/reducers/mockCartReducer.js @@ -0,0 +1,272 @@ +import { MOCK_CART_TYPES } from '../actions/mockCartActions'; + +// 브라우저 환경 확인 +const isBrowser = typeof window !== 'undefined' && typeof window.localStorage !== 'undefined'; + +/** + * Mock Cart Reducer 초기 상태 + * 실제 cartReducer와 유사한 구조 유지 + */ +const initialState = { + // Mock 장바구니 목록 + cartInfo: [], + // 마지막 실행된 액션 정보 + lastAction: null, + // 에러 정보 + error: null, + // 총 상품 수량 + totalQuantity: 0, + // 총 가격 + totalPrice: 0, + // 마지막 업데이트 시간 + lastUpdated: null, +}; + +// localStorage 관련 유틸리티 +const MOCK_CART_STORAGE_KEY = 'mockCartData'; + +/** + * Mock 장바구니 데이터를 localStorage에 저장 + */ +const saveToLocalStorage = (state) => { + if (!isBrowser) return; + + try { + const dataToSave = { + cartInfo: state.cartInfo, + totalQuantity: state.totalQuantity, + totalPrice: state.totalPrice, + lastUpdated: state.lastUpdated, + }; + window.localStorage.setItem(MOCK_CART_STORAGE_KEY, JSON.stringify(dataToSave)); + } catch (error) { + console.error('[MockCartReducer] localStorage 저장 실패:', error); + } +}; + +/** + * localStorage에서 Mock 장바구니 데이터 불러오기 + */ +const loadFromLocalStorage = () => { + if (!isBrowser) return initialState; + + try { + const savedData = window.localStorage.getItem(MOCK_CART_STORAGE_KEY); + if (savedData) { + const parsedData = JSON.parse(savedData); + return { + ...initialState, + cartInfo: parsedData.cartInfo || [], + totalQuantity: parsedData.totalQuantity || 0, + totalPrice: parsedData.totalPrice || 0, + lastUpdated: parsedData.lastUpdated || Date.now(), + }; + } + } catch (error) { + console.error('[MockCartReducer] localStorage 로드 실패:', error); + } + return initialState; +}; + +/** + * 장바구니 아이템 중복 확인 + * @param {Array} cartItems - 현재 장바구니 아이템 목록 + * @param {Object} newItem - 추가할 상품 + * @returns {Object} 중복 여부와 기존 아이템 인덱스 + */ +const findDuplicateItem = (cartItems, newItem) => { + const index = cartItems.findIndex(item => + item.prdtId === newItem.prdtId && + item.optNm === newItem.optNm + ); + return { + isDuplicate: index !== -1, + index + }; +}; + +/** + * 총 수량 및 총 가격 계산 + * @param {Array} cartItems - 장바구니 아이템 목록 + * @returns {Object} 총 수량과 총 가격 + */ +const calculateTotals = (cartItems) => { + const totalQuantity = cartItems.reduce((sum, item) => sum + (item.prodQty || 1), 0); + const totalPrice = cartItems.reduce((sum, item) => { + const itemPrice = parseFloat(item.price3 || item.price2 || 0); + const optionPrice = parseFloat(item.price5 || 0); + const shippingPrice = parseFloat(item.shippingCharge || 0); + return sum + ((itemPrice + optionPrice) * (item.prodQty || 1) + shippingPrice); + }, 0); + + return { + totalQuantity, + totalPrice: parseFloat(totalPrice.toFixed(2)) + }; +}; + +/** + * Mock Cart Reducer + * Mock Mode에서 장바구니 데이터를 관리합니다. + */ +export const mockCartReducer = (state = loadFromLocalStorage(), action) => { + switch (action.type) { + case MOCK_CART_TYPES.INIT_MOCK_CART: { + const { items = [] } = action.payload; + const newState = { + ...state, + cartInfo: items, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(items), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + case MOCK_CART_TYPES.ADD_TO_MOCK_CART: { + const { item } = action.payload; + const currentItems = [...state.cartInfo]; + + // 중복 상품 확인 + const { isDuplicate, index } = findDuplicateItem(currentItems, item); + + let updatedItems; + if (isDuplicate) { + // 중복 상품이면 수량 증가 + updatedItems = [...currentItems]; + updatedItems[index] = { + ...updatedItems[index], + prodQty: (updatedItems[index].prodQty || 1) + (item.prodQty || 1) + }; + } else { + // 새 상품 추가 + updatedItems = [...currentItems, item]; + } + + const newState = { + ...state, + cartInfo: updatedItems, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(updatedItems), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + case MOCK_CART_TYPES.REMOVE_FROM_MOCK_CART: { + const { prodSno } = action.payload; + const updatedItems = state.cartInfo.filter(item => item.prodSno !== prodSno); + + const newState = { + ...state, + cartInfo: updatedItems, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(updatedItems), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + case MOCK_CART_TYPES.UPDATE_MOCK_CART_ITEM: { + const { prodSno, quantity } = action.payload; + const updatedItems = state.cartInfo.map(item => { + if (item.prodSno === prodSno) { + return { + ...item, + prodQty: Math.max(1, quantity) // 최소 1개 보장 + }; + } + return item; + }); + + const newState = { + ...state, + cartInfo: updatedItems, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(updatedItems), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + case MOCK_CART_TYPES.SET_MOCK_CART_QUANTITY: { + const { prodSno, quantity } = action.payload; + + if (quantity <= 0) { + // 수량이 0이면 상품 제거 + const updatedItems = state.cartInfo.filter(item => item.prodSno !== prodSno); + + const newState = { + ...state, + cartInfo: updatedItems, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(updatedItems), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + const updatedItems = state.cartInfo.map(item => { + if (item.prodSno === prodSno) { + return { + ...item, + prodQty: quantity + }; + } + return item; + }); + + const newState = { + ...state, + cartInfo: updatedItems, + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + ...calculateTotals(updatedItems), + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + case MOCK_CART_TYPES.CLEAR_MOCK_CART: { + const newState = { + ...state, + cartInfo: [], + lastAction: action.payload.lastAction, + error: null, + lastUpdated: Date.now(), + totalQuantity: 0, + totalPrice: 0, + }; + + // localStorage에 저장 + saveToLocalStorage(newState); + return newState; + } + + default: + return state; + } +}; \ No newline at end of file diff --git a/com.twin.app.shoptime/src/store/store.js b/com.twin.app.shoptime/src/store/store.js index 612591ed..7c5c7728 100644 --- a/com.twin.app.shoptime/src/store/store.js +++ b/com.twin.app.shoptime/src/store/store.js @@ -22,6 +22,7 @@ import { foryouReducer } from '../reducers/forYouReducer'; import { homeReducer } from '../reducers/homeReducer'; import { localSettingsReducer } from '../reducers/localSettingsReducer'; import { mainReducer } from '../reducers/mainReducer'; +import { mockCartReducer } from '../reducers/mockCartReducer'; import { myPageReducer } from '../reducers/myPageReducer'; import { onSaleReducer } from '../reducers/onSaleReducer'; import { orderReducer } from '../reducers/orderReducer'; @@ -47,6 +48,7 @@ const rootReducer = combineReducers({ home: homeReducer, brand: brandReducer, main: mainReducer, + mockCart: mockCartReducer, myPage: myPageReducer, onSale: onSaleReducer, product: productReducer, diff --git a/com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js b/com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js index 5a82ebbf..0bbb7aa7 100644 --- a/com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js +++ b/com.twin.app.shoptime/src/utils/BuyNowDataManipulator.js @@ -63,6 +63,15 @@ export const createMockCartData = (productData, optionInfo = {}, quantity = 1) = return null; } + // ✅ 이미지 URL 처리 (ProductAllSection의 고품질 이미지 우선) + const imgUrl = productData.imgUrls600?.[0] || // ✅ ProductAllSection의 고품질 이미지 + productData.thumbnailUrl960 || // ✅ 960px 썸네일 + productData.imgUrl || + productData.thumbnailUrl || + productData.imgList?.[0]?.imgUrl || + productData.imgUrls?.[0]?.imgUrl || + '/assets/images/img-thumb-empty-144@3x.png'; + return { cartId: `MOCK_CART_${productData.prdtId}_${Date.now()}`, prdtId: productData.prdtId, @@ -71,12 +80,258 @@ export const createMockCartData = (productData, optionInfo = {}, quantity = 1) = patncNm: productData.patncNm, qty: quantity, price: productData.prdtPrice || 0, - thumbnailUrl: productData.thumbnailUrl || null, + + // ✅ 모든 이미지 필드 보존 (CartProduct 호환성 유지 + ProductAllSection 고품질 이미지) + imgUrl: imgUrl, + thumbnailUrl: productData.thumbnailUrl960 || productData.thumbnailUrl || imgUrl, + thumbnailUrl960: productData.thumbnailUrl960, + imgList: productData.imgList || [{ imgUrl: imgUrl }], + imgUrls: productData.imgUrls || [{ imgUrl: imgUrl }], + imgUrls600: productData.imgUrls600, // ✅ ProductAllSection의 고품질 이미지 배열 + optionInfo: optionInfo, addedAt: new Date().toISOString() }; }; +/** + * Mock Mode에서 초기 Mock 장바구니 목록 생성 + * + * @param {Object} productData - 초기 상품 데이터 (可选) + * @param {Object} optionInfo - 옵션 정보 (可选) + * @param {number} quantity - 수량 (可选) + * @returns {Array} Mock 장바구니 데이터 목록 + */ +export const createMockCartListData = (productData, optionInfo = {}, quantity = 1) => { + if (!BUYNOW_CONFIG.isMockMode()) { + 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]; + } + } + + return defaultMockItems; +}; + +// Mock 장바구니 데이터를 저장할 전역 변수 +let mockCartItems = []; + +/** + * Mock Mode에 따라 장바구니 데이터 설정 + * + * @param {Array} cartData - 장바구니 데이터 + */ +export const setMockCartData = (cartData) => { + if (BUYNOW_CONFIG.isMockMode()) { + mockCartItems = cartData || []; + } +}; + +/** + * 현재 Mock 장바구니 데이터 가져오기 + * + * @returns {Array} 현재 Mock 장바구니 데이터 + */ +export const getMockCartData = () => { + return BUYNOW_CONFIG.isMockMode() ? mockCartItems : []; +}; + +/** + * Mock 장바구니에 상품 추가 + * + * @param {Object} productData - 상품 데이터 + * @param {Object} optionInfo - 옵션 정보 + * @param {number} quantity - 수량 + * @returns {Object} 추가된 장바구니 아이템 + */ +export const addMockCartItem = (productData, optionInfo = {}, quantity = 1) => { + if (!BUYNOW_CONFIG.isMockMode() || !productData) { + return null; + } + + // ✅ 이미지 URL 처리 (ProductAllSection의 고품질 이미지 우선) + const imgUrl = productData.imgUrls600?.[0] || // ✅ ProductAllSection의 고품질 이미지 + productData.thumbnailUrl960 || // ✅ 960px 썸네일 + productData.imgUrl || + productData.thumbnailUrl || + productData.imgList?.[0]?.imgUrl || + productData.imgUrls?.[0]?.imgUrl || + '/assets/images/img-thumb-empty-144@3x.png'; + + const cartItem = { + prodSno: `MOCK_CART_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, + prdtId: productData.prdtId || 'MOCK_PRODUCT', + prdtNm: productData.prdtNm || 'Mock Product', + patnrId: productData.patnrId || 'MOCK_PARTNER', + patncNm: productData.patncNm || 'Mock Partner', + patncLogPath: productData.patncLogPath || '/assets/images/ic-partners-qvc@3x.png', + + // ✅ 이미지 정보 (모든 필드명 지원 - CartProduct/CheckOutPanel 호환성 + ProductAllSection 고품질 이미지) + imgUrl: imgUrl, + thumbnailUrl: productData.thumbnailUrl960 || productData.thumbnailUrl || imgUrl, + thumbnailUrl960: productData.thumbnailUrl960, + imgList: productData.imgList || [{ imgUrl: imgUrl }], + imgUrls: productData.imgUrls || [{ imgUrl: imgUrl }], // CheckOutPanel 호환성 + imgUrls600: productData.imgUrls600, // ✅ ProductAllSection의 고품질 이미지 배열 + + // ✅ 가격 정보 (CartProduct의 calculatePartnerTotal에서 사용하는 필드들 모두 포함) + price2: productData.price2 || productData.originalPrice || '299.99', // 원가 + price3: productData.price3 || productData.salePrice || productData.discountPrice || '199.99', // 할인가 + price5: optionInfo.price || productData.price5 || '29.99', // 옵션 할인가 + optPrc: optionInfo.price || productData.optPrc || productData.price5 || '29.99', // 옵션 가격 + + // 배송비 + shippingCharge: productData.shippingCharge || productData.shippingFee || '12.99', + + // 수량 + prodQty: quantity || productData.prodQty || 1, + + // 옵션명 + optNm: optionInfo.name || productData.optNm || '', + + // 기타 정보 + addedAt: new Date().toISOString(), + soldoutFlag: productData.soldoutFlag || 'N', + }; + + // 중복 확인 + const existingIndex = mockCartItems.findIndex(item => + item.prdtId === cartItem.prdtId && item.optNm === cartItem.optNm + ); + + if (existingIndex !== -1) { + // 중복이면 수량 증가 + mockCartItems[existingIndex].prodQty += cartItem.prodQty; + return mockCartItems[existingIndex]; + } else { + // 새 상품 추가 + mockCartItems.push(cartItem); + return cartItem; + } +}; + +/** + * Mock 장바구니에서 상품 제거 + * + * @param {string} prodSno - 상품 고유번호 + * @returns {Object|null} 제거된 상품 정보 + */ +export const removeMockCartItem = (prodSno) => { + if (!BUYNOW_CONFIG.isMockMode() || !prodSno) { + return null; + } + + const index = mockCartItems.findIndex(item => item.prodSno === prodSno); + if (index !== -1) { + const removedItem = mockCartItems.splice(index, 1)[0]; + return removedItem; + } + + return null; +}; + +/** + * Mock 장바구니 상품 수량 업데이트 + * + * @param {string} prodSno - 상품 고유번호 + * @param {number} quantity - 새로운 수량 + * @returns {Object|null} 업데이트된 상품 정보 + */ +export const updateMockCartItemQuantity = (prodSno, quantity) => { + if (!BUYNOW_CONFIG.isMockMode() || !prodSno || quantity <= 0) { + return null; + } + + const item = mockCartItems.find(item => item.prodSno === prodSno); + if (item) { + item.prodQty = quantity; + return { ...item }; + } + + return null; +}; + +/** + * Mock 장바구니 초기화 + */ +export const clearMockCartData = () => { + if (BUYNOW_CONFIG.isMockMode()) { + mockCartItems = []; + } +}; + /** * Mock Mode에서 가격 정보 포맷팅 * priceInfo는 "원가|할인가" 형식 (예: "100000|80000") diff --git a/com.twin.app.shoptime/src/utils/Config.js b/com.twin.app.shoptime/src/utils/Config.js index 22bec291..26a19760 100644 --- a/com.twin.app.shoptime/src/utils/Config.js +++ b/com.twin.app.shoptime/src/utils/Config.js @@ -43,6 +43,19 @@ export const panel_names = { USER_REVIEW_PANEL: 'userreviewpanel', }; +// 단독으로 렌더링되어야 하는 패널 목록 +// 이 패널들은 항상 isOnTop=true로 설정되고 다른 패널들과 함께 표시되지 않음 +export const STANDALONE_PANELS = [ + panel_names.CHECKOUT_PANEL, + panel_names.CART_PANEL, + // 향후 추가될 다른 단독 패널들 여기에 추가 +]; + +// 단독 패널인지 확인하는 유틸리티 함수 +export const isStandalonePanel = (panelName) => { + return STANDALONE_PANELS.includes(panelName); +}; + //button export const TBUTTON_PRESS_DELAY = 100; diff --git a/com.twin.app.shoptime/src/utils/mockDataSafetyUtils.js b/com.twin.app.shoptime/src/utils/mockDataSafetyUtils.js index b9014391..f779645f 100644 --- a/com.twin.app.shoptime/src/utils/mockDataSafetyUtils.js +++ b/com.twin.app.shoptime/src/utils/mockDataSafetyUtils.js @@ -129,33 +129,46 @@ export const getSafeProductOptions = (product) => { * @returns {string} 이미지 URL 또는 기본값 */ export const getSafeImageUrl = (product) => { - // 1순위: imgUrls 배열 (productInfo의 imgUrls) - const imgUrls = product?.imgUrls; - if (Array.isArray(imgUrls) && imgUrls.length > 0) { - return imgUrls[0]; + // ✅ 1순위: imgUrls600 배열 (ProductAllSection의 고품질 이미지) + const imgUrls600 = product?.imgUrls600; + if (Array.isArray(imgUrls600) && imgUrls600.length > 0) { + return imgUrls600[0]; } - // 2순위: imgUrl 직접 필드 - if (product?.imgUrl) { - return product.imgUrl; - } - // 3순위: thumbnailUrl (productInfo의 썸네일) - if (product?.thumbnailUrl) { - return product.thumbnailUrl; - } - // 4순위: thumbnailUrl960 (productInfo의 960px 썸네일) + + // ✅ 2순위: thumbnailUrl960 (ProductAllSection의 960px 썸네일) if (product?.thumbnailUrl960) { return product.thumbnailUrl960; } - // 5순위: imgUrls 배열의 imgUrl 필드 (기존 방식) - const imgUrlsWithImgUrl = product?.imgUrls; - if (Array.isArray(imgUrlsWithImgUrl) && imgUrlsWithImgUrl.length > 0) { - return imgUrlsWithImgUrl[0]?.imgUrl || '/mock/image.jpg'; + + // 3순위: imgUrl 직접 필드 + if (product?.imgUrl) { + return product.imgUrl; } - // 6순위: patncLogPath (파트너 로고) + + // 4순위: thumbnailUrl (productInfo의 썸네일) + if (product?.thumbnailUrl) { + return product.thumbnailUrl; + } + + // 5순위: imgUrls 배열 (productInfo의 imgUrls) + const imgUrls = product?.imgUrls; + if (Array.isArray(imgUrls) && imgUrls.length > 0) { + // imgUrls[0]이 객체면 imgUrl 필드 추출, 문자열이면 직접 사용 + return typeof imgUrls[0] === 'string' ? imgUrls[0] : imgUrls[0]?.imgUrl; + } + + // 6순위: imgList 배열의 imgUrl 필드 + const imgList = product?.imgList; + if (Array.isArray(imgList) && imgList.length > 0) { + return imgList[0]?.imgUrl || '/mock/image.jpg'; + } + + // 7순위: patncLogPath (파트너 로고) if (product?.patncLogPath) { return product.patncLogPath; } - // 7순위: 기본 이미지 + + // 8순위: 기본 이미지 return '/mock/image.jpg'; }; diff --git a/com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx b/com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx index 2f32a6f7..90b3047e 100644 --- a/com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx +++ b/com.twin.app.shoptime/src/views/CartPanel/CartPanel.jsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, + useMemo, } from 'react'; import { @@ -9,7 +10,10 @@ import { } from 'react-redux'; import { getMyInfoCartSearch } from '../../actions/cartActions'; +import { initializeMockCart, resetMockCart } from '../../actions/mockCartActions'; import { popPanel } from '../../actions/panelActions'; +import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig'; +import * as Config from '../../utils/Config'; import TBody from '../../components/TBody/TBody'; import THeader from '../../components/THeader/THeader'; import TPanel from '../../components/TPanel/TPanel'; @@ -20,12 +24,58 @@ import css from './CartPanel.module.less'; import CartProductBar from './CartProductBar'; import CartSidebar from './CartSidebar'; -export default function CartPanel({ spotlightId, scrollOptions = [] }) { +export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }) { + const dispatch = useDispatch(); + + // 실제 장바구니 데이터 const cartData = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo); + // Mock 장바구니 데이터 + const mockCartData = useSelector((state) => state.mockCart.cartInfo); + + // 패널 상태 확인 (충돌 방지용) + const panels = useSelector((state) => state.panels.panels); + const { userNumber } = useSelector( (state) => state.common.appStatus.loginUserData ); - const dispatch = useDispatch(); + + // Mock Mode 여부 확인 및 적절한 데이터 선택 + const isMockMode = BUYNOW_CONFIG.isMockMode(); + const displayCartData = useMemo(() => { + return isMockMode ? mockCartData : cartData; + }, [isMockMode, mockCartData, cartData]); + + // PlayerPanel/MediaPanel 충돌 방지 로직 + useEffect(() => { + 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 + ); + + if (playerPanelIndex >= 0) { + console.log('[CartPanel] 🚨 PlayerPanel/MediaPanel detected at index:', playerPanelIndex); + console.log('[CartPanel] PlayerPanel info:', panels[playerPanelIndex]); + + // PlayerPanel/MediaPanel 상태를 비활성화하여 CartPanel과의 충돌 방지 + if (panels[playerPanelIndex].panelInfo?.modal) { + console.log('[CartPanel] 🔄 Disabling modal PlayerPanel to prevent conflicts'); + // 필요하다면 여기서 PlayerPanel 상태를 비활성화하는 액션을 디스패치할 수 있음 + // dispatch(updatePanel({ + // name: panels[playerPanelIndex].name, + // panelInfo: { ...panels[playerPanelIndex].panelInfo, isActive: false } + // })); + } + } + + return () => { + console.log('[CartPanel] 🔄 Component unmounting - cleaning up'); + }; + }, [panels]); const onBackClick = useCallback(() => { dispatch(popPanel()); @@ -33,10 +83,32 @@ export default function CartPanel({ spotlightId, scrollOptions = [] }) { // 장바구니 데이터 로드 useEffect(() => { - if (userNumber) { + console.log('[CartPanel] Component mounted - isMockMode:', isMockMode, 'panelInfo:', panelInfo); + + if (isMockMode) { + // Mock Mode: panelInfo가 있으면 해당 상품 추가, 없으면 기본 Mock 데이터 설정 + 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 장바구니 초기화 (기존 데이터 유지) + dispatch(initializeMockCart( + panelInfo.productInfo, + panelInfo.optionInfo || {}, + panelInfo.quantity || 1 + )); + } else { + // panelInfo가 없으면 기본 Mock 장바구니 데이터로 설정 + console.log('[CartPanel] Mock Mode - Initializing with default mock data'); + dispatch(resetMockCart()); + } + } else if (userNumber) { + // API Mode: 실제 API 호출 + console.log('[CartPanel] API Mode - Loading cart from API for user:', userNumber); dispatch(getMyInfoCartSearch({ mbrNo: userNumber })); } - }, [dispatch, userNumber]); + }, [dispatch, userNumber, isMockMode, panelInfo]); const { getScrollTo, @@ -56,17 +128,17 @@ export default function CartPanel({ spotlightId, scrollOptions = [] }) { />