[카트, 체크아웃 수정]#1

- 금액 노출 및 나오는 값 수정중.
 - 체크박스 선택해서 주문 처리
 - 체크박스 선택시 금액 노출 처리.
This commit is contained in:
junghoon86.park
2025-11-14 14:41:56 +09:00
parent fdb9507024
commit 5d587dbdeb
11 changed files with 527 additions and 475 deletions

View File

@@ -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',

View File

@@ -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(),
},
});
};
/**
* 장바구니 상품 수정
*/

View File

@@ -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
);

View File

@@ -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 {

View File

@@ -203,7 +203,6 @@ export const isAuctionProduct = (product) => {
*/
export const normalizeProductDataForDisplay = (product) => {
// Mock Mode: product가 없어도 기본값으로 진행
console.log("###product 확인용", product)
if (!product) {
console.log('[mockDataSafetyUtils] normalizeProductDataForDisplay - product is null/undefined, using defaults');
return {

View File

@@ -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,6 +50,12 @@ 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();
@@ -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>

View File

@@ -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]);
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,15 +251,7 @@ 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 (
<>
@@ -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) ||
@@ -362,8 +336,8 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId={`productCheckbox-${item.prodSno}`}
selected={isItemSelected(item.prodSno)}
onToggle={handleCheckboxToggle(item.prodSno)}
selected={isItemSelected(item.prodSno)} // Redux 상태 기반
onToggle={handleCheckboxToggle(item.prodSno, item)} // item 전체 데이터 전달
onFocus={()=> {handleFocus(index, groupIndex)}}
/>
<span className={css.productId}>
@@ -404,9 +378,9 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
</>
)}
</span>
{item.price5 && parseFloat(item.price5) > 0 && (
{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.price5).toLocaleString('en-US', {
OPTION : ${parseFloat(item.optPrc).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
@@ -448,7 +422,7 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
<TButton
className={css.trashImg}
size="cartTrash"
onClick={() => deleteCart(item.patnrId, item.prdtId, item.prodSno)}
onClick={() => deleteCart(item.patnrId, item.prdtId, item.prodSno, item)}
/>
</div>
</div>

View File

@@ -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,129 +35,160 @@ 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);
}
if (!items || !Array.isArray(items)) {
if (DEBUG_LOG) {
console.log('[CartSidebar] No items provided, returning empty array');
// ✅ 계산할 상품 결정 (체크된 상품이 있으면 체크된 상품, 없으면 전체 상품)
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 (!isMockMode) {
if (DEBUG_LOG) {
console.log('[CartSidebar] API Mode - returning all items');
}
return items;
}
if (selectedItems.length === 0) {
if (DEBUG_LOG) {
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);
if (DEBUG_LOG) {
console.log('[CartSidebar] Item filter:', {
itemName: item.prdtNm,
itemId,
isSelected
});
}
return isSelected;
// 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 (DEBUG_LOG) {
console.log('[CartSidebar] Filtered selected items:', filtered.length, 'out of', items.length);
}
return filtered;
}, [isMockMode, selectedItems]);
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;
// 개별 상품 가격 캐싱 (성능 최적화)
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
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 cache;
}, [isMockMode, displayCartInfo]);
// Mock 데이터 또는 실제 데이터 계산 (선택된 상품만) - 최적화 버전
// ✅ Mock 데이터 또는 실제 데이터 계산 - 최적화 버전
const calculatedData = useMemo(() => {
const DEBUG_LOG = false; // 수동 설정: false로 비활성화
const DEBUG_LOG = 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);
}
// 캐시된 가격 정보 사용
if (itemsToCalculate && itemsToCalculate.length > 0) {
let totalItems = 0;
let totalCoupon = 0;
let totalOption = 0;
let totalShipping = 0;
let totalQuantity = 0;
selectedCartItems.forEach((item) => {
const itemId = item.prodSno || item.cartId;
const cachedPrice = itemPriceCache.get(itemId);
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 (cachedPrice) {
const qty = item.prodQty || item.qty || 1;
totalItems += cachedPrice.price * qty;
totalCoupon += cachedPrice.coupon * qty;
totalShipping += cachedPrice.shipping * qty;
totalItems += productPrice * qty;
totalOption += optionPrice * qty;
totalShipping += shippingPrice;
totalQuantity += qty;
if (DEBUG_LOG) {
console.log('[CartSidebar] Item calculation (cached):', {
console.log('[CartSidebar] Item calculation:', {
name: item.prdtNm,
qty,
itemPrice: cachedPrice.price,
itemCoupon: cachedPrice.coupon,
itemShipping: cachedPrice.shipping,
itemPrice: productPrice,
itemOption: optionPrice,
itemShipping: shippingPrice,
runningTotal: totalItems
});
}
}
});
const subtotal = Math.max(0, totalItems - totalCoupon + totalShipping);
const subtotal = Math.max(0, totalItems + totalOption + totalShipping);
if (DEBUG_LOG) {
console.log('[CartSidebar] Final calculation for selected items:', {
console.log('[CartSidebar] Final calculation:', {
isCheckedMode: checkedItems && checkedItems.length > 0,
itemCount: itemsToCalculate.length,
totalQuantity,
totalItems,
totalCoupon,
totalOption,
totalShipping,
subtotal,
});
@@ -157,12 +197,12 @@ const CartSidebar = ({ cartInfo }) => {
return {
itemCount: totalQuantity,
subtotal: totalItems,
optionTotal: totalCoupon,
optionTotal: totalOption,
shippingHandling: totalShipping,
orderTotalBeforeTax: subtotal,
};
} else {
// Mock Mode에 데이터가 없는 경우
// 상품이 없는 경우
return {
itemCount: 0,
subtotal: 0,
@@ -171,90 +211,45 @@ const CartSidebar = ({ cartInfo }) => {
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);
}, [itemsToCalculate, checkedItems]);
return {
itemCount,
subtotal,
optionTotal,
shippingHandling,
orderTotalBeforeTax: subtotal + optionTotal + shippingHandling,
};
} else {
// 데이터가 없는 경우
return {
itemCount: 0,
subtotal: 0,
optionTotal: 0,
shippingHandling: 0,
orderTotalBeforeTax: 0,
};
}
}, [isMockMode, displayCartInfo, getSelectedItems, itemPriceCache]);
// 체크아웃 버튼 클릭 핸들러 - 완전한 데이터 전송
// 체크아웃 버튼 클릭 핸들러
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));
// ✅ 계산할 상품이 없는 경우
if (itemsToCalculate.length === 0) {
console.log('[CartSidebar] ❌ No items to checkout');
return;
}
// ✅ 선택된 상품들만 필터링 (체크박스 선택된 상품)
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));
console.log('[CartSidebar] Items to checkout:', JSON.stringify(itemsToCalculate));
}
// 선택된 상품이 없는 경우
if (selectedCartItems.length === 0) {
if (DEBUG_LOG) {
console.log('[CartSidebar] Mock Mode - No items selected, cannot proceed to checkout');
}
return; // 아무것도 하지 않음
}
// ✅ CheckOutPanel 전송용 데이터 구성
const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData;
// ✅ CheckOutPanel 전송용 데이터 구성 (CheckOutPanel 기대 구조에 맞춤)
const checkoutData = {
// 하위 호환성: 첫 번째 상품을 productInfo로
productInfo: selectedCartItems[0],
productInfo: itemsToCalculate[0],
// ✅ CheckOutPanel이 기대하는 cartItems 필드 (isFromCart=true일 때 사용)
cartItems: selectedCartItems.map(item => ({
// ✅ 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, // 쿠폰
price5: item.price5, // 옵션가
prodQty: item.prodQty || item.qty || 1,
optNm: item.optNm,
shippingCharge: item.shippingCharge || '0',
@@ -266,104 +261,71 @@ const CartSidebar = ({ cartInfo }) => {
// 이미지 정보
imgUrl: item.imgUrl,
thumbnailUrl: item.thumbnailUrl,
imgUrls600: item.imgUrls600
imgUrls600: item.imgUrls600,
prodSno: item.prodSno,
})),
// 메타데이터
isFromCart: true,
fromCartPanel: 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 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('%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('%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;');
console.log('%cCalculated totals:', 'background: yellow; color: black; padding: 3px;', {
itemCount,
subtotal,
optionTotal,
shippingHandling,
orderTotalBeforeTax
});
}
// ✅ 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
}));
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);
}
} 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
}));
}
}, [dispatch, isMockMode, cartInfo, getSelectedItems, orderTotalBeforeTax, itemCount, subtotal, optionTotal, shippingHandling]);
}, [dispatch, checkedItems, itemsToCalculate, calculatedData]);
const { itemCount, subtotal, optionTotal, shippingHandling, orderTotalBeforeTax } = calculatedData;
@@ -429,6 +391,7 @@ const CartSidebar = ({ cartInfo }) => {
className={css.checkoutButton}
spotlightId="cart-checkout-button"
onClick={handleCheckoutClick}
disabled={itemsToCalculate.length === 0}
>
Checkout
</TButton>

View File

@@ -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}
/>
)}

View File

@@ -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}

View File

@@ -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,