[251102] fix: MainView rendering Panel Update
🕐 커밋 시간: 2025. 11. 02. 06:59:07 📊 변경 통계: • 총 파일: 4개 • 추가: +86줄 • 삭제: -35줄 📁 추가된 파일: + com.twin.app.shoptime/src/actions/mockCartActions.js 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.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:
202
com.twin.app.shoptime/src/actions/mockCartActions.js
Normal file
202
com.twin.app.shoptime/src/actions/mockCartActions.js
Normal file
@@ -0,0 +1,202 @@
|
||||
import { BUYNOW_CONFIG } from '../utils/BuyNowConfig';
|
||||
import { createMockCartListData, addMockCartItem, removeMockCartItem, updateMockCartItemQuantity } from '../utils/BuyNowDataManipulator';
|
||||
|
||||
// Mock Cart Action Types
|
||||
export const MOCK_CART_TYPES = {
|
||||
INIT_MOCK_CART: 'mockCart/INIT_MOCK_CART',
|
||||
ADD_TO_MOCK_CART: 'mockCart/ADD_TO_MOCK_CART',
|
||||
REMOVE_FROM_MOCK_CART: 'mockCart/REMOVE_FROM_MOCK_CART',
|
||||
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',
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니 초기화
|
||||
* BuyOption에서 ADD TO CART 시 호출
|
||||
*/
|
||||
export const initializeMockCart = (productData, optionInfo = {}, quantity = 1) => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] initializeMockCart - productData:', productData);
|
||||
|
||||
// 초기 Mock 장바구니 데이터 생성
|
||||
const mockCartData = createMockCartListData(productData, optionInfo, quantity);
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.INIT_MOCK_CART,
|
||||
payload: {
|
||||
items: mockCartData,
|
||||
lastAction: {
|
||||
type: 'init',
|
||||
data: productData,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니에 상품 추가
|
||||
* @param {Object} productData - 상품 데이터
|
||||
* @param {Object} optionInfo - 옵션 정보
|
||||
* @param {number} quantity - 수량
|
||||
*/
|
||||
export const addToMockCart = (productData, optionInfo = {}, quantity = 1) => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] addToMockCart - productData:', productData);
|
||||
|
||||
// Mock 장바구니 데이터 생성
|
||||
const newCartItem = addMockCartItem(productData, optionInfo, quantity);
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.ADD_TO_MOCK_CART,
|
||||
payload: {
|
||||
item: newCartItem,
|
||||
lastAction: {
|
||||
type: 'add',
|
||||
data: newCartItem,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니에서 상품 제거
|
||||
* @param {string} prodSno - 상품 고유번호
|
||||
*/
|
||||
export const removeFromMockCart = (prodSno) => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] removeFromMockCart - prodSno:', prodSno);
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.REMOVE_FROM_MOCK_CART,
|
||||
payload: {
|
||||
prodSno,
|
||||
lastAction: {
|
||||
type: 'remove',
|
||||
data: { prodSno },
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니 상품 수량 업데이트
|
||||
* @param {string} prodSno - 상품 고유번호
|
||||
* @param {number} quantity - 새로운 수량
|
||||
*/
|
||||
export const updateMockCartItem = (prodSno, quantity) => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] updateMockCartItem - prodSno:', prodSno, 'quantity:', quantity);
|
||||
|
||||
const updatedItem = updateMockCartItemQuantity(prodSno, quantity);
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.UPDATE_MOCK_CART_ITEM,
|
||||
payload: {
|
||||
item: updatedItem,
|
||||
prodSno,
|
||||
quantity,
|
||||
lastAction: {
|
||||
type: 'update',
|
||||
data: { prodSno, quantity },
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니 상품 수량 직접 설정
|
||||
* @param {string} prodSno - 상품 고유번호
|
||||
* @param {number} quantity - 설정할 수량
|
||||
*/
|
||||
export const setMockCartItemQuantity = (prodSno, quantity) => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (quantity <= 0) {
|
||||
// 수량이 0이면 상품 제거
|
||||
dispatch(removeFromMockCart(prodSno));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] setMockCartItemQuantity - prodSno:', prodSno, 'quantity:', quantity);
|
||||
|
||||
const updatedItem = updateMockCartItemQuantity(prodSno, quantity);
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.SET_MOCK_CART_QUANTITY,
|
||||
payload: {
|
||||
item: updatedItem,
|
||||
prodSno,
|
||||
quantity,
|
||||
lastAction: {
|
||||
type: 'set_quantity',
|
||||
data: { prodSno, quantity },
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니 전체 비우기
|
||||
*/
|
||||
export const clearMockCart = () => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] clearMockCart');
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.CLEAR_MOCK_CART,
|
||||
payload: {
|
||||
lastAction: {
|
||||
type: 'clear',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock 장바구니 데이터 재설정 (초기 상태로 복원)
|
||||
*/
|
||||
export const resetMockCart = () => (dispatch, getState) => {
|
||||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[MockCartActions] resetMockCart');
|
||||
|
||||
// 기본 Mock 장바구니 데이터로 재설정
|
||||
const defaultMockCartData = createMockCartListData();
|
||||
|
||||
dispatch({
|
||||
type: MOCK_CART_TYPES.INIT_MOCK_CART,
|
||||
payload: {
|
||||
items: defaultMockCartData,
|
||||
lastAction: {
|
||||
type: 'reset',
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -51,7 +51,7 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
// console.log 오버라이드를 위한 ref
|
||||
const originalConsoleLog = useRef();
|
||||
|
||||
// 컴포넌트 마운트 시 console.log 오버라이드
|
||||
// 컴포넌트 마운트 시 console.log 오버라이드 및 PlayerPanel 충돌 방지
|
||||
useEffect(() => {
|
||||
// 원래 함수 저장
|
||||
originalConsoleLog.current = console.log;
|
||||
@@ -66,12 +66,36 @@ 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('[BuyOption][CheckOutPanel] 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('[BuyOption][CheckOutPanel] 🚨 PlayerPanel/MediaPanel detected at index:', playerPanelIndex);
|
||||
console.log('[BuyOption][CheckOutPanel] PlayerPanel info:', panels[playerPanelIndex]);
|
||||
|
||||
// PlayerPanel/MediaPanel 상태를 비활성화하여 CheckOutPanel과의 충돌 방지
|
||||
if (panels[playerPanelIndex].panelInfo?.modal) {
|
||||
console.log('[BuyOption][CheckOutPanel] 🔄 Disabling modal PlayerPanel to prevent conflicts');
|
||||
// 필요하다면 여기서 PlayerPanel 상태를 비활성화하는 액션을 디스패치할 수 있음
|
||||
// dispatch(updatePanel({
|
||||
// name: panels[playerPanelIndex].name,
|
||||
// panelInfo: { ...panels[playerPanelIndex].panelInfo, isActive: false }
|
||||
// }));
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
// console.log 원래 함수로 복원
|
||||
console.log = originalConsoleLog.current;
|
||||
console.log('[BuyOption][CheckOutPanel] 🔄 Component unmounting - cleaning up');
|
||||
};
|
||||
}, []); // 의존성 배열이 비어있어 컴포넌트 마운트 시 한 번만 실행
|
||||
}, [panels, panelInfo]); // panels와 panelInfo를 의존성에 추가
|
||||
const dispatch = useDispatch();
|
||||
const panels = useSelector((state) => state.panels.panels);
|
||||
console.log(
|
||||
@@ -317,8 +341,13 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
|
||||
const onBackClick = useCallback(() => {
|
||||
console.log('[BuyOption][CheckOutPanel] onBackClick called - dispatching popPanel');
|
||||
dispatch(popPanel());
|
||||
}, [dispatch]);
|
||||
console.log('[BuyOption][CheckOutPanel] Current panels before pop:', panels?.map((p) => p.name));
|
||||
|
||||
// 확실한 popPanel 호출 - CheckOutPanel만 제거
|
||||
dispatch(popPanel(Config.panel_names.CHECKOUT_PANEL));
|
||||
|
||||
console.log('[BuyOption][CheckOutPanel] popPanel dispatched for CHECKOUT_PANEL');
|
||||
}, [dispatch, panels]);
|
||||
|
||||
const toggleOrderSideBar = useCallback(() => {
|
||||
console.log('[BuyOption][CheckOutPanel] toggleOrderSideBar called - current state:', orderSideBarOpen);
|
||||
@@ -440,12 +469,17 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
}
|
||||
|
||||
if (!orderSideBarOpen && !offerSideBarOpen) {
|
||||
console.log('[BuyOption][CheckOutPanel] Calling popPanel()');
|
||||
dispatch(popPanel());
|
||||
console.log('[BuyOption][CheckOutPanel] onCancelCheckoutPanel - calling popPanel for CHECKOUT_PANEL');
|
||||
console.log('[BuyOption][CheckOutPanel] Current panels before cancel pop:', panels?.map((p) => p.name));
|
||||
|
||||
// 확실한 popPanel 호출 - CheckOutPanel만 제거
|
||||
dispatch(popPanel(Config.panel_names.CHECKOUT_PANEL));
|
||||
e.stopPropagation();
|
||||
|
||||
console.log('[BuyOption][CheckOutPanel] popPanel dispatched for CHECKOUT_PANEL from cancel');
|
||||
}
|
||||
},
|
||||
[orderSideBarOpen, offerSideBarOpen, dispatch]
|
||||
[orderSideBarOpen, offerSideBarOpen, dispatch, panels]
|
||||
);
|
||||
|
||||
const doSendLogPaymentEntry = useCallback(() => {
|
||||
|
||||
@@ -618,6 +618,9 @@ const BuyOption = ({
|
||||
console.log('[BuyOption] Mock Mode - Simulating checkout');
|
||||
console.log('[BuyOption] logInfo:', logInfo);
|
||||
console.log('[BuyOption] Dispatching pushPanel to CHECKOUT_PANEL');
|
||||
|
||||
// CheckOutPanel 이동 전에 PlayerPanel/MediaPanel 상태 정리
|
||||
console.log('[BuyOption] Mock Mode - Cleaning up PlayerPanel/MediaPanel before checkout');
|
||||
dispatch(finishVideoPreview());
|
||||
dispatch(finishMediaPreview());
|
||||
|
||||
|
||||
@@ -198,33 +198,42 @@ export default function MainView({ className, initService }) {
|
||||
if (panels && panels.length > 0) {
|
||||
let renderingPanels = [];
|
||||
|
||||
// 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
|
||||
const hasThreeLayerStructure =
|
||||
panels.length >= 3 &&
|
||||
// PlayerPanel이 맨 아래
|
||||
(panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL_NEW) &&
|
||||
// DetailPanel이 중간
|
||||
panels[panels.length - 2]?.name === Config.panel_names.DETAIL_PANEL &&
|
||||
// MediaPanel modal이 맨 위
|
||||
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL &&
|
||||
panels[panels.length - 1]?.panelInfo?.modal === true;
|
||||
const topPanel = panels[panels.length - 1];
|
||||
|
||||
if (hasThreeLayerStructure) {
|
||||
console.log(
|
||||
'[MainView] Rendering 3-layer structure: PlayerPanel + DetailPanel + MediaPanel'
|
||||
);
|
||||
renderingPanels = panels.slice(-3);
|
||||
} else if (
|
||||
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
||||
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL ||
|
||||
panels[panels.length - 2]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 2]?.name === Config.panel_names.MEDIA_PANEL
|
||||
) {
|
||||
renderingPanels = panels.slice(-2);
|
||||
} else {
|
||||
renderingPanels = panels.slice(-1);
|
||||
// CheckOutPanel은 독립적으로 항상 단독 렌더링
|
||||
if (topPanel?.name === Config.panel_names.CHECKOUT_PANEL) {
|
||||
console.log('[MainView] CheckOutPanel detected - rendering independently');
|
||||
renderingPanels = [topPanel]; // CheckOutPanel만 단독으로 렌더링
|
||||
}
|
||||
// 기존 3-layer 구조 체크: PlayerPanel + DetailPanel + MediaPanel(modal)
|
||||
else {
|
||||
const hasThreeLayerStructure =
|
||||
panels.length >= 3 &&
|
||||
// PlayerPanel이 맨 아래
|
||||
(panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 3]?.name === Config.panel_names.PLAYER_PANEL_NEW) &&
|
||||
// DetailPanel이 중간
|
||||
panels[panels.length - 2]?.name === Config.panel_names.DETAIL_PANEL &&
|
||||
// MediaPanel modal이 맨 위
|
||||
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL &&
|
||||
panels[panels.length - 1]?.panelInfo?.modal === true;
|
||||
|
||||
if (hasThreeLayerStructure) {
|
||||
console.log(
|
||||
'[MainView] Rendering 3-layer structure: PlayerPanel + DetailPanel + MediaPanel'
|
||||
);
|
||||
renderingPanels = panels.slice(-3);
|
||||
} else if (
|
||||
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 1]?.name === Config.panel_names.PLAYER_PANEL_NEW ||
|
||||
panels[panels.length - 1]?.name === Config.panel_names.MEDIA_PANEL ||
|
||||
panels[panels.length - 2]?.name === Config.panel_names.PLAYER_PANEL ||
|
||||
panels[panels.length - 2]?.name === Config.panel_names.MEDIA_PANEL
|
||||
) {
|
||||
renderingPanels = panels.slice(-2);
|
||||
} else {
|
||||
renderingPanels = panels.slice(-1);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
@@ -239,8 +248,13 @@ export default function MainView({ className, initService }) {
|
||||
const Component = panelMap[panel.name];
|
||||
let isPanelOnTop = false;
|
||||
|
||||
// CheckOutPanel은 항상 onTop
|
||||
if (panel.name === Config.panel_names.CHECKOUT_PANEL) {
|
||||
isPanelOnTop = true;
|
||||
console.log('[MainView] CheckOutPanel is always onTop');
|
||||
}
|
||||
// 3-layer 케이스: 중간 패널(DetailPanel)이 onTop
|
||||
if (renderingPanels.length === 3) {
|
||||
else if (renderingPanels.length === 3) {
|
||||
if (index === 1) {
|
||||
// DetailPanel (중간)
|
||||
isPanelOnTop = true;
|
||||
|
||||
Reference in New Issue
Block a user