[카트, 체크아웃 수정]#1
- 금액 노출 및 나오는 값 수정중. - 체크박스 선택해서 주문 처리 - 체크박스 선택시 금액 노출 처리.
This commit is contained in:
@@ -86,6 +86,8 @@ export const types = {
|
||||
DELETE_MY_INFO_CART : "DELETE_MY_INFO_CART",
|
||||
DELETE_ALL_MY_INFO_CART : "DELETE_ALL_MY_INFO_CART",
|
||||
UPDATE_MY_INFO_CART : "UPDATE_MY_INFO_CART",
|
||||
//cart checkbox toggle action
|
||||
TOGGLE_CHECK_CART : "TOGGLE_CHECK_CART",
|
||||
|
||||
// appData actions
|
||||
ADD_MAIN_INDEX: 'ADD_MAIN_INDEX',
|
||||
|
||||
@@ -171,6 +171,22 @@ export const deleteAllMyinfoCart = (props) => (dispatch, getState) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 장바구니 상품 체크박스 토글
|
||||
* @param {Object} item - 선택된 상품 정보
|
||||
* @param {Boolean} isChecked - 선택 여부
|
||||
*/
|
||||
export const toggleCheckCart = (item, isChecked) => (dispatch) => {
|
||||
dispatch({
|
||||
type: types.TOGGLE_CHECK_CART,
|
||||
payload: {
|
||||
item: item,
|
||||
isChecked: isChecked,
|
||||
timestamp: Date.now(),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 장바구니 상품 수정
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { URLS } from "../api/apiConfig";
|
||||
import { TAxios } from "../api/TAxios";
|
||||
import { types } from "./actionTypes";
|
||||
import { changeAppStatus, showError } from "./commonActions";
|
||||
import { URLS } from '../api/apiConfig';
|
||||
import { TAxios } from '../api/TAxios';
|
||||
import { types } from './actionTypes';
|
||||
import {
|
||||
changeAppStatus,
|
||||
showError,
|
||||
} from './commonActions';
|
||||
|
||||
// 회원 체크아웃 정보 조회 IF-LGSP-345
|
||||
export const getMyInfoCheckoutInfo =
|
||||
@@ -150,6 +153,7 @@ export const getCheckoutTotalAmt =
|
||||
dirPurcSelYn,
|
||||
bilAddrSno,
|
||||
dlvrAddrSno,
|
||||
isPageLoading,
|
||||
orderProductCoupontUse,
|
||||
} = params;
|
||||
|
||||
@@ -195,7 +199,7 @@ export const getCheckoutTotalAmt =
|
||||
"post",
|
||||
URLS.GET_CHECKOUT_TOTAL_AMT,
|
||||
{},
|
||||
{ mbrNo, dirPurcSelYn, bilAddrSno, dlvrAddrSno, orderProductCoupontUse },
|
||||
{ mbrNo, dirPurcSelYn, bilAddrSno, dlvrAddrSno, isPageLoading, orderProductCoupontUse },
|
||||
onSuccess,
|
||||
onFail
|
||||
);
|
||||
|
||||
@@ -9,6 +9,11 @@ const initialState = {
|
||||
cartList: [],
|
||||
totalCount: 0,
|
||||
},
|
||||
selectCart : {
|
||||
cartList: [],
|
||||
checkedItems: [], // ✅ 체크된 상품 정보 저장
|
||||
totalCount: 0,
|
||||
},
|
||||
// 추가/수정/삭제 결과
|
||||
lastAction: null,
|
||||
error: null,
|
||||
@@ -35,13 +40,43 @@ export const cartReducer = (state = initialState, action) => {
|
||||
case types.INSERT_MY_INFO_CART:
|
||||
return {
|
||||
...state,
|
||||
getMyinfoCartSearch: {
|
||||
...state.getMyinfoCartSearch,
|
||||
cartList: [...state.getMyinfoCartSearch.cartList, action.payload],
|
||||
selectCart: {
|
||||
...state.selectCart,
|
||||
cartList: [...state.selectCart.cartList, action.payload],
|
||||
totalCount: (state.getMyinfoCartSearch.totalCount || 0) + 1,
|
||||
},
|
||||
};
|
||||
|
||||
//체크박스 토글시 상품 처리
|
||||
case types.TOGGLE_CHECK_CART: {
|
||||
const checkedItem = action.payload.item;
|
||||
const isChecked = action.payload.isChecked;
|
||||
|
||||
let updatedCheckedList = state.selectCart?.checkedItems || [];
|
||||
|
||||
if (isChecked) {
|
||||
const itemExists = updatedCheckedList.some(
|
||||
item => item.prodSno === checkedItem.prodSno
|
||||
);
|
||||
if (!itemExists) {
|
||||
updatedCheckedList = [...updatedCheckedList, checkedItem];
|
||||
}
|
||||
} else {
|
||||
updatedCheckedList = updatedCheckedList.filter(
|
||||
item => item.prodSno !== checkedItem.prodSno
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
selectCart: {
|
||||
...state.selectCart,
|
||||
checkedItems: updatedCheckedList,
|
||||
totalCount: updatedCheckedList.length,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 장바구니에서 상품 삭제
|
||||
case types.DELETE_MY_INFO_CART:
|
||||
return {
|
||||
|
||||
@@ -202,8 +202,7 @@ export const isAuctionProduct = (product) => {
|
||||
* @returns {Object} 정규화된 상품 객체
|
||||
*/
|
||||
export const normalizeProductDataForDisplay = (product) => {
|
||||
// Mock Mode: product가 없어도 기본값으로 진행
|
||||
console.log("###product 확인용", product)
|
||||
// Mock Mode: product가 없어도 기본값으로 진행
|
||||
if (!product) {
|
||||
console.log('[mockDataSafetyUtils] normalizeProductDataForDisplay - product is null/undefined, using defaults');
|
||||
return {
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
} from 'react-redux';
|
||||
|
||||
import { getMyInfoCartSearch } from '../../actions/cartActions';
|
||||
import {
|
||||
setHidePopup,
|
||||
setShowPopup,
|
||||
} from '../../actions/commonActions';
|
||||
import {
|
||||
initializeMockCart,
|
||||
resetMockCart,
|
||||
@@ -21,10 +25,12 @@ import {
|
||||
import TBody from '../../components/TBody/TBody';
|
||||
import THeader from '../../components/THeader/THeader';
|
||||
import TPanel from '../../components/TPanel/TPanel';
|
||||
import TPopUp from '../../components/TPopUp/TPopUp';
|
||||
import TScroller from '../../components/TScroller/TScroller';
|
||||
import useScrollTo from '../../hooks/useScrollTo';
|
||||
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
|
||||
import { launchMembershipApp } from '../../lunaSend';
|
||||
import * as Config from '../../utils/Config';
|
||||
import { $L } from '../../utils/helperMethods';
|
||||
import CartEmpty from './CartEmpty';
|
||||
import css from './CartPanel.module.less';
|
||||
import CartProductBar from './CartProductBar';
|
||||
@@ -44,13 +50,19 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
|
||||
const { userNumber } = useSelector(
|
||||
(state) => state.common.appStatus.loginUserData
|
||||
);
|
||||
const { popupVisible, activePopup } = useSelector(
|
||||
(state) => state.common.popup
|
||||
);
|
||||
const webOSVersion = useSelector(
|
||||
(state) => state.common.appStatus.webOSVersion
|
||||
);
|
||||
|
||||
// Mock Mode 여부 확인 및 적절한 데이터 선택
|
||||
// const isMockMode = BUYNOW_CONFIG.isMockMode();
|
||||
const isMockMode = false;
|
||||
const displayCartData = useMemo(() => {
|
||||
return isMockMode ? mockCartData : cartData;
|
||||
}, [isMockMode, mockCartData, cartData]);
|
||||
}, [isMockMode, mockCartData, cartData]);
|
||||
|
||||
// PlayerPanel/MediaPanel 충돌 방지 로직 (CheckOutPanel 전환 시에만 활성화)
|
||||
useEffect(() => {
|
||||
@@ -166,6 +178,35 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
|
||||
|
||||
const { getScrollTo, scrollTop } = useScrollTo();
|
||||
|
||||
// 로그인 팝업 텍스트 로직 (SingleOption과 동일)
|
||||
const loginPopupText = useMemo(() => {
|
||||
return $L('Would you like to sign in?');
|
||||
}, [userNumber]);
|
||||
|
||||
// 로그인 팝업 열기 핸들러 (SingleOption과 동일)
|
||||
const handleLoginPopUpOpen = useCallback(() => {
|
||||
if (!userNumber) {
|
||||
if (webOSVersion >= '6.0') {
|
||||
dispatch(setHidePopup());
|
||||
dispatch(launchMembershipApp()); // 필요시 추가
|
||||
}
|
||||
return;
|
||||
}
|
||||
}, [dispatch, webOSVersion, userNumber]);
|
||||
|
||||
// 팝업 닫기 핸들러 (SingleOption과 동일)
|
||||
const onClose = useCallback(() => {
|
||||
dispatch(setHidePopup());
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
useEffect(()=>{
|
||||
if(!userNumber || userNumber === ''){
|
||||
dispatch(setShowPopup(Config.ACTIVE_POPUP.loginPopup));
|
||||
}
|
||||
},[userNumber, dispatch])
|
||||
|
||||
return (
|
||||
<TPanel spotlightId={spotlightId} isTabActivated={true}>
|
||||
<TBody className={css.tbody} cbScrollTo={getScrollTo}>
|
||||
@@ -182,7 +223,7 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
|
||||
</div>
|
||||
<div className={css.rightSection}>
|
||||
{/* 오른쪽 상품 영역 */}
|
||||
{displayCartData && displayCartData?.length > 0 ? (
|
||||
{userNumber && displayCartData && displayCartData?.length > 0 ? (
|
||||
<TScroller
|
||||
className={css.tScroller}
|
||||
{...scrollOptions}
|
||||
@@ -191,6 +232,22 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
|
||||
</TScroller>
|
||||
) : (
|
||||
<CartEmpty />
|
||||
|
||||
|
||||
)}
|
||||
{/* LOGIN POPUP */}
|
||||
{(!userNumber || userNumber === '') && activePopup === Config.ACTIVE_POPUP.loginPopup && (
|
||||
<TPopUp
|
||||
kind="textPopup"
|
||||
hasText
|
||||
open={popupVisible}
|
||||
text={loginPopupText}
|
||||
hasButton
|
||||
button1Text={$L('OK')}
|
||||
button2Text={$L('CANCEL')}
|
||||
onClick={handleLoginPopUpOpen}
|
||||
onClose={onClose}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,6 +18,7 @@ import defaultImage from '../../../assets/images/img-thumb-empty-144@3x.png';
|
||||
import {
|
||||
deleteMyinfoCart,
|
||||
removeFromCart,
|
||||
toggleCheckCart,
|
||||
updateMyinfoCart,
|
||||
} from '../../actions/cartActions';
|
||||
import {
|
||||
@@ -94,6 +95,7 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
|
||||
// 항상 호출되어야 하는 Hook들
|
||||
const fallbackCartData = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
|
||||
const checkedItems = useSelector((state) => state.cart.selectCart.checkedItems || []); // ✅ Redux에서 체크된 아이템 가져오기
|
||||
const selectedItems = useSelector((state) => state.mockCart.selectedItems || []);
|
||||
const userNumber = useSelector((state) => state.common.appStatus.loginUserData.userNumber);
|
||||
|
||||
@@ -103,7 +105,6 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
// Mock Mode 확인
|
||||
const isMockMode = BUYNOW_CONFIG.isMockMode();
|
||||
|
||||
//카트 데이타 그룹화 - 수정된 부분
|
||||
const groupedCartData = useMemo(() => {
|
||||
if (!cartData || !Array.isArray(cartData)) return {};
|
||||
|
||||
@@ -111,7 +112,6 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
const groupKey = item.patncNm || item.patnrId || 'unknown';
|
||||
|
||||
if (!acc[groupKey]) {
|
||||
// 객체 구조로 초기화 (수정됨)
|
||||
acc[groupKey] = {
|
||||
partnerInfo: {
|
||||
id: item.patnrId,
|
||||
@@ -137,14 +137,15 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
// 파트너사별 총합 계산
|
||||
const calculatePartnerTotal = (items) => {
|
||||
const productTotal = items.reduce((sum, item) =>
|
||||
sum + (parseFloat(Number(item.price3) !== 0 ? Number(item.price3) : Number(item.price2) !== 0 ? Number(item.price2) : 0) * item.prodQty), 0
|
||||
sum + (Number(item.price3) !== 0 ? Number(item.price3) : Number(item.price2) !== 0 ? Number(item.price2) : 0) * item.prodQty, 0
|
||||
);
|
||||
const optionTotal = items.reduce((sum, item) =>
|
||||
sum + (parseFloat(Number(item.price5) !== 0 ? Number(item.price5) : Number(item.optPrc) !== 0 ? Number(item.optPrc) : 0) * item.prodQty), 0
|
||||
//sum + (parseFloat(Number(item.price5) !== 0 ? Number(item.price5) : Number(item.optPrc) !== 0 ? Number(item.optPrc) : 0) * item.prodQty), 0
|
||||
sum + (Number(item.optPrc) !== 0 && item.optPrc !== undefined) ? Number(item.optPrc) : 0 * item.prodQty, 0
|
||||
);
|
||||
|
||||
const shippingTotal = items.reduce((sum, item) =>
|
||||
sum + parseFloat((item.shippingCharge) * item.prodQty || 0), 0
|
||||
sum + parseFloat(item.shippingCharge || 0), 0
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -176,7 +177,7 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [dispatch, isMockMode]);
|
||||
}, [dispatch, isMockMode, userNumber]);
|
||||
|
||||
const handleIncreseClick = useCallback((prodSno, patnrId, prdtId, currentQty) => {
|
||||
const newQty = currentQty + 1;
|
||||
@@ -190,7 +191,7 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
dispatch(updateMyinfoCart({ mbrNo: userNumber, patnrId, prdtId, prodSno, prodQty: newQty }));
|
||||
}
|
||||
}
|
||||
}, [dispatch, isMockMode]);
|
||||
}, [dispatch, isMockMode, userNumber]);
|
||||
|
||||
// 상품 삭제 핸들러
|
||||
const handleDeleteClick = useCallback((prodSno) => {
|
||||
@@ -205,46 +206,29 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
}
|
||||
}, [dispatch, isMockMode]);
|
||||
|
||||
// 체크박스 선택 핸들러 (TCheckBoxSquare onToggle 형식에 맞춤) - debounce 적용
|
||||
const debouncedUpdateSelectedItems = useCallback(
|
||||
debounce((newSelectedItems) => {
|
||||
dispatch(updateSelectedItems(newSelectedItems));
|
||||
}, 100), // 100ms debounce
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const handleCheckboxToggle = useCallback((prodSno) => {
|
||||
// 체크박스 선택 핸들러 - ✅ Redux 상태만 사용하도록 수정
|
||||
const handleCheckboxToggle = useCallback((prodSno, fullItemData) => {
|
||||
return ({ selected: isChecked }) => {
|
||||
const DEBUG_LOG = false; // 수동 설정: false로 비활성화
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartProduct] handleCheckboxToggle called - prodSno:', prodSno, 'selected:', isChecked);
|
||||
}
|
||||
|
||||
if (isMockMode) {
|
||||
let newSelectedItems;
|
||||
if (isChecked) {
|
||||
// 상품 선택
|
||||
newSelectedItems = [...selectedItems, prodSno];
|
||||
} else {
|
||||
// 상품 선택 해제
|
||||
newSelectedItems = selectedItems.filter(id => id !== prodSno);
|
||||
}
|
||||
|
||||
// debounced 호출 사용
|
||||
debouncedUpdateSelectedItems(newSelectedItems);
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartProduct] Checkbox toggled - prodSno:', prodSno, 'isChecked:', isChecked, 'selectedItems:', newSelectedItems);
|
||||
}
|
||||
}
|
||||
// Redux 상태에만 저장
|
||||
dispatch(toggleCheckCart(fullItemData, isChecked));
|
||||
};
|
||||
}, [dispatch, isMockMode, selectedItems, debouncedUpdateSelectedItems]);
|
||||
}, [dispatch]);
|
||||
|
||||
// 장바구니 삭제
|
||||
const deleteCart = useCallback((patnrId, prdtId, prodSno, item) => {
|
||||
dispatch(toggleCheckCart(item, false));
|
||||
dispatch(deleteMyinfoCart({
|
||||
mbrNo : userNumber,
|
||||
patnrId: String(patnrId),
|
||||
prdtId : String(prdtId),
|
||||
prodSno : String(prodSno)
|
||||
}))
|
||||
},[dispatch, userNumber])
|
||||
|
||||
// 상품이 선택되었는지 확인
|
||||
const isItemSelected = useCallback((prodSno) => {
|
||||
return selectedItems.includes(prodSno);
|
||||
}, [selectedItems]);
|
||||
const isItemSelected = useCallback((prodSno) => {
|
||||
return checkedItems.some(item => item.prodSno === prodSno);
|
||||
}, [checkedItems]);
|
||||
|
||||
const handleFocus = useCallback((index, groupIndex) => {
|
||||
if(index === 0 && groupIndex === 0){
|
||||
@@ -259,9 +243,7 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
andThen(() => document.getElementById(sectionId)),
|
||||
when(isNil, () => null),
|
||||
andThen((targetElement) => {
|
||||
// offsetTop: 부모 컨테이너 기준 절대 위치 사용
|
||||
// const y = targetElement.offsetTop;
|
||||
const y = 0; //0으로 들어가는것이 깔끔.
|
||||
const y = 0;
|
||||
return scrollTop({ y, animate: true });
|
||||
})
|
||||
)(sectionId)
|
||||
@@ -269,22 +251,14 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
[scrollTop]
|
||||
);
|
||||
|
||||
//장바구니 삭제
|
||||
const deleteCart = useCallback((patnrId, prdtId, prodSno) => {
|
||||
dispatch(deleteMyinfoCart({
|
||||
mbrNo : userNumber,
|
||||
patnrId: String(patnrId),
|
||||
prdtId : String(prdtId),
|
||||
prodSno : String(prodSno)
|
||||
}))
|
||||
},[dispatch])
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.entries(groupedCartData).map(([partnerKey, group], index) => {
|
||||
const totals = calculatePartnerTotal(group.items);
|
||||
return (
|
||||
<Container key={partnerKey} className={css.productBox}>
|
||||
<>
|
||||
{Object.entries(groupedCartData).map(([partnerKey, group], index) => {
|
||||
const totals = calculatePartnerTotal(group.items);
|
||||
return (
|
||||
<Container key={partnerKey} className={css.productBox}>
|
||||
{index === 0 && (
|
||||
<div className={css.scrollMarker} id="cartFocus_0"></div>
|
||||
)}
|
||||
@@ -310,9 +284,9 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
<div className={css.productPrice}>
|
||||
<div className={css.leftSection}>
|
||||
Product Total ${totals.productTotal.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})} +
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})} +
|
||||
{totals.optionTotal > 0 && (
|
||||
<>
|
||||
Option ${totals.optionTotal.toLocaleString('en-US', {
|
||||
@@ -322,9 +296,9 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
</>
|
||||
)}
|
||||
S&H ${totals.shippingTotal.toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</div>
|
||||
<div className={css.rightSection}>
|
||||
Total
|
||||
@@ -345,8 +319,8 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
const normalizedItem = normalizeProductDataForDisplay(item);
|
||||
|
||||
// ✅ 이미지 우선순위: ProductAllSection 고품질 이미지 → 기타 모든 이미지 필드
|
||||
const imageSrc = (item.imgUrls600 && item.imgUrls600[0]) || // ✅ ProductAllSection의 고품질 이미지
|
||||
(item.thumbnailUrl960) || // ✅ 960px 썸네일
|
||||
const imageSrc = (item.imgUrls600 && item.imgUrls600[0]) ||
|
||||
(item.thumbnailUrl960) ||
|
||||
(normalizedItem.imgUrls && normalizedItem.imgUrls[0]?.imgUrl) ||
|
||||
(item.imgUrls && item.imgUrls[0]?.imgUrl) ||
|
||||
(item.imgList && item.imgList[0]?.imgUrl) ||
|
||||
@@ -356,109 +330,109 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
|
||||
defaultImage;
|
||||
|
||||
return (
|
||||
<div key={item.prodSno} className={css.product}>
|
||||
<div className={css.leftBox}>
|
||||
<div className={css.checkBox}>
|
||||
<TCheckBoxSquare
|
||||
className={css.customeCheckbox}
|
||||
spotlightId={`productCheckbox-${item.prodSno}`}
|
||||
selected={isItemSelected(item.prodSno)}
|
||||
onToggle={handleCheckboxToggle(item.prodSno)}
|
||||
onFocus={()=> {handleFocus(index, groupIndex)}}
|
||||
/>
|
||||
<span className={css.productId}>
|
||||
ID : {item.prdtId}
|
||||
</span>
|
||||
</div>
|
||||
<div className={css.productInfo}>
|
||||
<div className={css.leftSection}>
|
||||
<OptimizedImage
|
||||
className={css.productImage}
|
||||
src={imageSrc}
|
||||
fallbackSrc={defaultImage}
|
||||
alt={item.prdtNm || 'Product Image'}
|
||||
<div key={item.prodSno} className={css.product}>
|
||||
<div className={css.leftBox}>
|
||||
<div className={css.checkBox}>
|
||||
<TCheckBoxSquare
|
||||
className={css.customeCheckbox}
|
||||
spotlightId={`productCheckbox-${item.prodSno}`}
|
||||
selected={isItemSelected(item.prodSno)} // ✅ Redux 상태 기반
|
||||
onToggle={handleCheckboxToggle(item.prodSno, item)} // ✅ item 전체 데이터 전달
|
||||
onFocus={()=> {handleFocus(index, groupIndex)}}
|
||||
/>
|
||||
<span className={css.productId}>
|
||||
ID : {item.prdtId}
|
||||
</span>
|
||||
</div>
|
||||
<div className={css.rightSection}>
|
||||
<div className={css.productNm}>
|
||||
{item.prdtNm}
|
||||
<div className={css.productInfo}>
|
||||
<div className={css.leftSection}>
|
||||
<OptimizedImage
|
||||
className={css.productImage}
|
||||
src={imageSrc}
|
||||
fallbackSrc={defaultImage}
|
||||
alt={item.prdtNm || 'Product Image'}
|
||||
/>
|
||||
</div>
|
||||
{item.optNm && (
|
||||
<div className={css.optionNm}>{item.optNm}</div>
|
||||
)}
|
||||
<div className={css.accountBox}>
|
||||
<span className={css.account}>
|
||||
${parseFloat(Number(item.price3) > 0 ? Number(item.price3) : Number(item.price2) > 0 ? Number(item.price2) : 0).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
<div className={css.accountInfo}>
|
||||
<div className={css.rightSection}>
|
||||
<div className={css.productNm}>
|
||||
{item.prdtNm}
|
||||
</div>
|
||||
{item.optNm && (
|
||||
<div className={css.optionNm}>{item.optNm}</div>
|
||||
)}
|
||||
<div className={css.accountBox}>
|
||||
<span className={css.account}>
|
||||
${parseFloat(Number(item.price3) > 0 ? Number(item.price3) : Number(item.price2) > 0 ? Number(item.price2) : 0).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
<div className={css.accountInfo}>
|
||||
<span className={css.originalAcc}>
|
||||
{item.price2 && (parseFloat(item.price2) > 0 && parseFloat(item.price3) > 0 && parseFloat(item.price2) !== parseFloat(item.price3)) && (
|
||||
<>
|
||||
${parseFloat(item.price2).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{item.price2 && (parseFloat(item.price2) > 0 && parseFloat(item.price3) > 0 && parseFloat(item.price2) !== parseFloat(item.price3)) && (
|
||||
<>
|
||||
${parseFloat(item.price2).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
{item.price5 && parseFloat(item.price5) > 0 && (
|
||||
<span className={css.optionAcc}>
|
||||
OPTION : ${parseFloat(item.price5).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
{item.shippingCharge && parseFloat(item.shippingCharge) > 0 && (
|
||||
<span className={css.shippingAcc}>
|
||||
S&H: ${parseFloat(item.shippingCharge).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
{item.optPrc && parseFloat(item.optPrc) > 0 && parseFloat(item.optPrc) !== parseFloat(item.price2) && parseFloat(item.optPrc) !== parseFloat(item.price3) && (
|
||||
<span className={css.optionAcc}>
|
||||
OPTION : ${parseFloat(item.optPrc).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
{item.shippingCharge && parseFloat(item.shippingCharge) > 0 && (
|
||||
<span className={css.shippingAcc}>
|
||||
S&H: ${parseFloat(item.shippingCharge).toLocaleString('en-US', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.eaBox}>
|
||||
<TButton
|
||||
className={classNames(
|
||||
css.minusBox,
|
||||
item.prodQty === 1 ? css.dimm : ""
|
||||
)}
|
||||
size="cartEa"
|
||||
onClick={() => handleDecreseClick(item.prodSno, item.patnrId, item.prdtId, item.prodQty)}
|
||||
spotlightId={"pd_ea_decrese"}
|
||||
spotlightDisabled={item.prodQty === 1}
|
||||
/>
|
||||
<div className={css.ea}>{item.prodQty}</div>
|
||||
<TButton
|
||||
onClick={() => handleIncreseClick(item.prodSno, item.patnrId, item.prdtId, item.prodQty)}
|
||||
className={css.plusBox}
|
||||
spotlightId={"pd_ea_increse"}
|
||||
size="cartEa"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.eaBox}>
|
||||
<TButton
|
||||
className={classNames(
|
||||
css.minusBox,
|
||||
item.prodQty === 1 ? css.dimm : ""
|
||||
)}
|
||||
size="cartEa"
|
||||
onClick={() => handleDecreseClick(item.prodSno, item.patnrId, item.prdtId, item.prodQty)}
|
||||
spotlightId={"pd_ea_decrese"}
|
||||
spotlightDisabled={item.prodQty === 1}
|
||||
/>
|
||||
<div className={css.ea}>{item.prodQty}</div>
|
||||
<TButton
|
||||
onClick={() => handleIncreseClick(item.prodSno, item.patnrId, item.prdtId, item.prodQty)}
|
||||
className={css.plusBox}
|
||||
spotlightId={"pd_ea_increse"}
|
||||
size="cartEa"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.rightBox}>
|
||||
<TButton
|
||||
className={css.trashImg}
|
||||
size="cartTrash"
|
||||
onClick={() => deleteCart(item.patnrId, item.prdtId, item.prodSno, item)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.rightBox}>
|
||||
<TButton
|
||||
className={css.trashImg}
|
||||
size="cartTrash"
|
||||
onClick={() => deleteCart(item.patnrId, item.prdtId, item.prodSno)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,16 @@ import {
|
||||
useSelector,
|
||||
} from 'react-redux';
|
||||
|
||||
import { pushPanel } from '../../actions/panelActions';
|
||||
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';
|
||||
@@ -26,175 +35,174 @@ const CartSidebar = ({ cartInfo }) => {
|
||||
|
||||
// 실제 장바구니 데이터 (API 모드일 때만 사용)
|
||||
const fallbackCartInfo = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
|
||||
const selectedItems = useSelector((state) => state.mockCart.selectedItems || []);
|
||||
// ✅ Redux에서 체크된 상품 정보 가져오기
|
||||
const checkedItems = useSelector((state) => state.cart.selectCart.checkedItems || []);
|
||||
|
||||
const { userNumber } = useSelector(
|
||||
(state) => state.common.appStatus.loginUserData
|
||||
);
|
||||
|
||||
// 사용할 장바구니 데이터 결정
|
||||
const displayCartInfo = cartInfo || (isMockMode ? null : fallbackCartInfo);
|
||||
|
||||
// 선택된 상품들만 필터링 - 항상 선택된 상품들만 반환
|
||||
const getSelectedItems = useCallback((items) => {
|
||||
const DEBUG_LOG = false; // 수동 설정: false로 비활성화
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] getSelectedItems called - isMockMode:', isMockMode, 'selectedItems:', selectedItems);
|
||||
// ✅ 계산할 상품 결정 (체크된 상품이 있으면 체크된 상품, 없으면 전체 상품)
|
||||
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]);
|
||||
|
||||
if (!items || !Array.isArray(items)) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] No items provided, returning empty array');
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isMockMode) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] API Mode - returning all items');
|
||||
}
|
||||
return items;
|
||||
}
|
||||
// ✅ Mock 데이터 또는 실제 데이터 계산 - 최적화 버전
|
||||
const calculatedData = useMemo(() => {
|
||||
const DEBUG_LOG = false;
|
||||
|
||||
if (selectedItems.length === 0) {
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] No items selected, returning empty array');
|
||||
}
|
||||
return []; // 선택된 상품이 없으면 빈 배열 반환
|
||||
}
|
||||
if (itemsToCalculate && itemsToCalculate.length > 0) {
|
||||
let totalItems = 0;
|
||||
let totalOption = 0;
|
||||
let totalShipping = 0;
|
||||
let totalQuantity = 0;
|
||||
|
||||
const filtered = items.filter(item => {
|
||||
const itemId = item.prodSno || item.cartId;
|
||||
const isSelected = selectedItems.includes(itemId);
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Item filter:', {
|
||||
itemName: item.prdtNm,
|
||||
itemId,
|
||||
isSelected
|
||||
});
|
||||
}
|
||||
return isSelected;
|
||||
});
|
||||
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);
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Filtered selected items:', filtered.length, 'out of', items.length);
|
||||
}
|
||||
return filtered;
|
||||
}, [isMockMode, selectedItems]);
|
||||
const qty = item.prodQty || item.qty || 1;
|
||||
totalItems += productPrice * qty;
|
||||
totalOption += optionPrice * qty;
|
||||
totalShipping += shippingPrice;
|
||||
totalQuantity += qty;
|
||||
|
||||
// 개별 상품 가격 캐싱 (성능 최적화)
|
||||
const itemPriceCache = useMemo(() => {
|
||||
const cache = new Map();
|
||||
|
||||
if (isMockMode && displayCartInfo) {
|
||||
displayCartInfo.forEach(item => {
|
||||
if (!cache.has(item.prodSno || item.cartId)) {
|
||||
|
||||
const orderSummary = calculateOrderSummaryFromProductInfo(item);
|
||||
cache.set(item.prodSno || item.cartId, {
|
||||
price: orderSummary.items,
|
||||
coupon: 0,
|
||||
shipping: orderSummary.shipping
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Item calculation:', {
|
||||
name: item.prdtNm,
|
||||
qty,
|
||||
itemPrice: productPrice,
|
||||
itemOption: optionPrice,
|
||||
itemShipping: shippingPrice,
|
||||
runningTotal: totalItems
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return cache;
|
||||
}, [isMockMode, displayCartInfo]);
|
||||
const subtotal = Math.max(0, totalItems + totalOption + totalShipping);
|
||||
|
||||
// Mock 데이터 또는 실제 데이터 계산 (선택된 상품만) - 최적화 버전
|
||||
const calculatedData = useMemo(() => {
|
||||
const DEBUG_LOG = false; // 수동 설정: false로 비활성화
|
||||
|
||||
if (isMockMode) {
|
||||
// Mock Mode: 선택된 상품들로 개별 가격 계산 (캐시 사용)
|
||||
if (displayCartInfo && Array.isArray(displayCartInfo) && displayCartInfo.length > 0) {
|
||||
const selectedCartItems = getSelectedItems(displayCartInfo);
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Selected items for calculation:', selectedCartItems);
|
||||
}
|
||||
|
||||
// 캐시된 가격 정보 사용
|
||||
let totalItems = 0;
|
||||
let totalCoupon = 0;
|
||||
let totalShipping = 0;
|
||||
let totalQuantity = 0;
|
||||
|
||||
selectedCartItems.forEach((item) => {
|
||||
const itemId = item.prodSno || item.cartId;
|
||||
const cachedPrice = itemPriceCache.get(itemId);
|
||||
|
||||
if (cachedPrice) {
|
||||
const qty = item.prodQty || item.qty || 1;
|
||||
totalItems += cachedPrice.price * qty;
|
||||
totalCoupon += cachedPrice.coupon * qty;
|
||||
totalShipping += cachedPrice.shipping * qty;
|
||||
totalQuantity += qty;
|
||||
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Item calculation (cached):', {
|
||||
name: item.prdtNm,
|
||||
qty,
|
||||
itemPrice: cachedPrice.price,
|
||||
itemCoupon: cachedPrice.coupon,
|
||||
itemShipping: cachedPrice.shipping,
|
||||
runningTotal: totalItems
|
||||
});
|
||||
}
|
||||
}
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Final calculation:', {
|
||||
isCheckedMode: checkedItems && checkedItems.length > 0,
|
||||
itemCount: itemsToCalculate.length,
|
||||
totalQuantity,
|
||||
totalItems,
|
||||
totalOption,
|
||||
totalShipping,
|
||||
subtotal,
|
||||
});
|
||||
|
||||
const subtotal = Math.max(0, totalItems - totalCoupon + totalShipping);
|
||||
if (DEBUG_LOG) {
|
||||
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);
|
||||
const subtotal = displayCartInfo.reduce((sum, item) => {
|
||||
const price = parseFloat(Number(item.price3) !== 0 ? Number(item.price3) : Number(item.price2) !== 0 ? Number(item.price2) : 0);
|
||||
return sum + (price * (item.prodQty || 1));
|
||||
}, 0);
|
||||
const optionTotal = displayCartInfo.reduce((sum, item) => {
|
||||
const optionPrice = parseFloat(Number(item.price5) || Number(item.optPrc) || 0);
|
||||
return sum + (optionPrice * (item.prodQty || 1));
|
||||
}, 0);
|
||||
const shippingHandling = displayCartInfo.reduce((sum, item) => {
|
||||
return sum + parseFloat(Number(item.shippingCharge) * (item.prodQty || 1))
|
||||
}, 0);
|
||||
|
||||
return {
|
||||
itemCount,
|
||||
subtotal,
|
||||
optionTotal,
|
||||
shippingHandling,
|
||||
orderTotalBeforeTax: subtotal + optionTotal + shippingHandling,
|
||||
itemCount: totalQuantity,
|
||||
subtotal: totalItems,
|
||||
optionTotal: totalOption,
|
||||
shippingHandling: totalShipping,
|
||||
orderTotalBeforeTax: subtotal,
|
||||
};
|
||||
} else {
|
||||
// 데이터가 없는 경우
|
||||
// 상품이 없는 경우
|
||||
return {
|
||||
itemCount: 0,
|
||||
subtotal: 0,
|
||||
@@ -203,175 +211,129 @@ const CartSidebar = ({ cartInfo }) => {
|
||||
orderTotalBeforeTax: 0,
|
||||
};
|
||||
}
|
||||
}, [isMockMode, displayCartInfo, getSelectedItems, itemPriceCache]);
|
||||
}, [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,
|
||||
totalItems: cartInfo?.length || 0,
|
||||
selectedItemsCount: selectedItems.length
|
||||
checkedItemsCount: checkedItems.length,
|
||||
itemsToCalculateCount: itemsToCalculate.length
|
||||
});
|
||||
|
||||
if (isMockMode) {
|
||||
// Mock Mode: 선택된 상품들로 CheckOutPanel로 이동
|
||||
if (DEBUG_LOG) {
|
||||
console.log('[CartSidebar] Mock Mode - Going to Checkout');
|
||||
console.log('[CartSidebar] Mock Mode - cartInfo:', JSON.stringify(cartInfo));
|
||||
}
|
||||
|
||||
// ✅ 선택된 상품들만 필터링 (체크박스 선택된 상품)
|
||||
const allCartItems = cartInfo && Array.isArray(cartInfo) ? cartInfo : [];
|
||||
const selectedCartItems = getSelectedItems(allCartItems);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
// 선택된 상품이 없는 경우
|
||||
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: 실제 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
|
||||
}));
|
||||
// ✅ 계산할 상품이 없는 경우
|
||||
if (itemsToCalculate.length === 0) {
|
||||
console.log('[CartSidebar] ❌ No items to checkout');
|
||||
return;
|
||||
}
|
||||
}, [dispatch, isMockMode, cartInfo, getSelectedItems, orderTotalBeforeTax, itemCount, subtotal, optionTotal, shippingHandling]);
|
||||
|
||||
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
|
||||
})}`;
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}`;
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -429,6 +391,7 @@ const CartSidebar = ({ cartInfo }) => {
|
||||
className={css.checkoutButton}
|
||||
spotlightId="cart-checkout-button"
|
||||
onClick={handleCheckoutClick}
|
||||
disabled={itemsToCalculate.length === 0}
|
||||
>
|
||||
Checkout
|
||||
</TButton>
|
||||
@@ -437,4 +400,4 @@ const CartSidebar = ({ cartInfo }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default CartSidebar;
|
||||
export default CartSidebar;
|
||||
@@ -138,6 +138,7 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
const empTermsData = useSelector((state) => state.emp.empTermsData.terms);
|
||||
const { popupVisible, activePopup } = useSelector((state) => state.common.popup);
|
||||
const popup = useSelector((state) => state.common.popup);
|
||||
const cartSelectInfo = useSelector((state) => state.cart.selectCart.checkedItems);
|
||||
|
||||
// Mock Mode: panelInfo.productInfo 또는 Redux에서 상품 데이터 사용
|
||||
const productData = BUYNOW_CONFIG.isMockMode()
|
||||
@@ -360,9 +361,10 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
getCheckoutTotalAmt(
|
||||
{
|
||||
mbrNo: userNumber,
|
||||
dirPurcSelYn: 'Y',
|
||||
dirPurcSelYn: cartSelectInfo.length > 0 ? 'N' : 'Y',
|
||||
bilAddrSno: infoForCheckoutData.bilAddrSno,
|
||||
dlvrAddrSno: infoForCheckoutData.dlvrAddrSno,
|
||||
isPageLoading: "Y",
|
||||
orderProductCoupontUse,
|
||||
},
|
||||
totalAmtValidate
|
||||
@@ -667,7 +669,7 @@ export default function CheckOutPanel({ panelInfo }) {
|
||||
closeSideBar={toggleOrderSideBar}
|
||||
productData={safeProductData}
|
||||
rawProductData={productData}
|
||||
productInfo={panelInfo?.productInfo}
|
||||
productInfo={reduxProductData}
|
||||
fromCartPanel={panelInfo?.fromCartPanel}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -165,7 +165,7 @@ export default function OrderItemsSideBar({
|
||||
patncLogPath={item.patncLogPath}
|
||||
prdtId={item.prdtId}
|
||||
expsPrdtNo={item.expsPrdtNo}
|
||||
price={item.price2 >= item.price3 ? item.price3 : item.price2}
|
||||
price={(item.price2 >= item.price3 && item.price3 !== 0) ? item.price3 : item.price2}
|
||||
currSign={item.currSign}
|
||||
currSignLoc={item.currSignLoc}
|
||||
shippingCharge={item.shippingCharge}
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function SummaryContainer({
|
||||
|
||||
// Check if priceTotalData has actual data (ordPmtReqAmt is the key field)
|
||||
const hasValidPriceTotalData = priceTotalData && Object.keys(priceTotalData).length > 0 && priceTotalData.ordPmtReqAmt;
|
||||
|
||||
console.log("###hasValidPriceTotalData",hasValidPriceTotalData)
|
||||
// Mock Mode: priceTotalData가 없으면 가짜 데이터 제공
|
||||
const effectivePriceTotalData = hasValidPriceTotalData ? priceTotalData : {
|
||||
totProdPrc: 0.00,
|
||||
|
||||
Reference in New Issue
Block a user