- buyoption : ,로 인해서 잘리는 부분수정. - mock데이터 말고 정식 상품이 바로 구매로 넘어올떄 관련 부분 수정. - 장바구니 금액 노출부분 수정 및 스타일 수정 - 구매 과정중 금액 노출및 정상적이지 않는 스타일에 대한수정 - 갯수 관련 수정
1454 lines
56 KiB
JavaScript
1454 lines
56 KiB
JavaScript
import React, {
|
||
useCallback,
|
||
useEffect,
|
||
useMemo,
|
||
useRef,
|
||
useState,
|
||
} from 'react';
|
||
|
||
import {
|
||
useDispatch,
|
||
useSelector,
|
||
} from 'react-redux';
|
||
|
||
import Spotlight from '@enact/spotlight';
|
||
import SpotlightContainerDecorator
|
||
from '@enact/spotlight/SpotlightContainerDecorator';
|
||
|
||
import { addToCart } from '../../../actions/cartActions';
|
||
import { getMyInfoCheckoutInfo } from '../../../actions/checkoutActions';
|
||
import {
|
||
changeAppStatus,
|
||
setHidePopup,
|
||
setShowPopup,
|
||
showError,
|
||
} from '../../../actions/commonActions';
|
||
import { getProductCouponSearch } from '../../../actions/couponActions';
|
||
import {
|
||
sendLogPaymentEntry,
|
||
sendLogTotalRecommend,
|
||
} from '../../../actions/logActions';
|
||
import { finishMediaPreview } from '../../../actions/mediaActions';
|
||
import { addToMockCart } from '../../../actions/mockCartActions';
|
||
import {
|
||
popPanel,
|
||
pushPanel,
|
||
} from '../../../actions/panelActions';
|
||
import { clearAllVideoTimers } from '../../../actions/playActions';
|
||
import {
|
||
getProductOption,
|
||
getProductOptionId,
|
||
} from '../../../actions/productActions';
|
||
import { clearAllToasts } from '../../../actions/toastActions';
|
||
import TButton from '../../../components/TButton/TButton';
|
||
import TPopUp from '../../../components/TPopUp/TPopUp';
|
||
import { BUYNOW_CONFIG } from '../../../utils/BuyNowConfig';
|
||
import {
|
||
createMockProductOptionData,
|
||
} from '../../../utils/BuyNowDataManipulator';
|
||
import * as Config from '../../../utils/Config';
|
||
import { $L } from '../../../utils/helperMethods';
|
||
import { getSafeProductPrice } from '../../../utils/mockDataSafetyUtils';
|
||
import FavoriteBtn from '../components/FavoriteBtn';
|
||
import styles from './BuyOption.module.less';
|
||
import BuyOptionPriceBlock from './BuyOptionPriceBlock';
|
||
// import CustomDropDown from './CustomDropDown';
|
||
import CustomDropDown from './CustomDropDown.new';
|
||
import ProductQuantity from './ProductQuantity';
|
||
|
||
const Container = SpotlightContainerDecorator(
|
||
{
|
||
spotlightRestrict: 'self-only',
|
||
leaveFor: {}, // arrow key로 빠져나가지 않도록 설정
|
||
},
|
||
'div'
|
||
);
|
||
|
||
const BuyOption = ({
|
||
patncNm,
|
||
productInfo: propsProductInfo,
|
||
isSpotlight,
|
||
selectedPatnrId: propsSelectedPatnrId,
|
||
selectedPrdtId: propsSelectedPrdtId,
|
||
selectedIndex,
|
||
logMenu,
|
||
type,
|
||
}) => {
|
||
// DEBUG_LOG 설정 - 이 값이 true일 때만 console.log가 실행됨
|
||
const DEBUG_LOG = false;
|
||
|
||
// console.log 오버라이드를 위한 ref
|
||
const originalConsoleLog = useRef(console.log);
|
||
|
||
// 컴포넌트 마운트 시 console.log 오버라이드
|
||
useEffect(() => {
|
||
// 원래 함수 저장
|
||
originalConsoleLog.current = console.log;
|
||
|
||
// console.log 오버라이드 - 이 컴포넌트에서만 DEBUG_LOG 조건을 체크하도록 설정
|
||
console.log = (...args) => {
|
||
if (DEBUG_LOG) {
|
||
originalConsoleLog.current.apply(console, args);
|
||
}
|
||
};
|
||
|
||
return () => {
|
||
// console.log 원래 함수로 복원
|
||
console.log = originalConsoleLog.current;
|
||
};
|
||
}, []); // 의존성 배열이 비어있어 컴포넌트 마운트 시 한 번만 실행
|
||
|
||
const dispatch = useDispatch();
|
||
|
||
// Redux 상태 (props가 있으면 props 우선, 없으면 Redux에서)
|
||
const { userId, userNumber } = useSelector(
|
||
(state) => state.common.appStatus.loginUserData
|
||
);
|
||
const reduxProductInfo = useSelector((state) => state.main.productData);
|
||
const productInfo = propsProductInfo || reduxProductInfo;
|
||
const reduxProductOptionInfos = useSelector((state) => state.product.prdtOptInfo);
|
||
const productData = useSelector((state) => state.main.productData);
|
||
const { partnerCoupon } = useSelector(
|
||
(state) => state.coupon.productCouponSearchData || {}
|
||
);
|
||
const { popupVisible, activePopup } = useSelector(
|
||
(state) => state.common.popup
|
||
);
|
||
const nowMenu = useSelector((state) => state.common.menu.nowMenu);
|
||
const webOSVersion = useSelector(
|
||
(state) => state.common.appStatus.webOSVersion
|
||
);
|
||
|
||
// 옵션 선택 상태 관리 (SingleOption과 동일한 구조)
|
||
const [selectedBtnOptIdx, setSelectedBtnOptIdx] = useState(0);
|
||
const [selectedOptionItemIndex, setSelectedOptionItemIndex] = useState(0);
|
||
const [selectedOptions, setSelectedOptions] = useState();
|
||
const [isOptionValue, setIsOptionValue] = useState(false);
|
||
const [isOptionSelect, setIsOptionSelect] = useState(false);
|
||
const [quantity, setQuantity] = useState(1);
|
||
const [favoriteFlag, setFavoriteFlag] = useState(productInfo?.favorYn);
|
||
const [hasProductOptionArray, setHasProductOptionArray] = useState(true);
|
||
const [selectedOptionInfo, setSelectedOptionInfo] = useState();
|
||
|
||
const [selectFirstOptionIndex, setSelectFirstOptionIndex] = useState(0);
|
||
const [selectSecondOptionIndex, setSelectSecondOptionIndex] = useState(0);
|
||
|
||
// 상품 정보 (props 우선, 없으면 productInfo에서)
|
||
const selectedPatnrId = propsSelectedPatnrId || productInfo?.patnrId;
|
||
const selectedPrdtId = propsSelectedPrdtId || productInfo?.prdtId;
|
||
|
||
// Mock Mode에서 옵션 데이터 처리
|
||
const productOptionInfos = useMemo(() => {
|
||
console.log('[BuyOption] productOptionInfos useMemo - isMockMode:', BUYNOW_CONFIG.isMockMode());
|
||
console.log('[BuyOption] productOptionInfos useMemo - reduxProductOptionInfos:', reduxProductOptionInfos);
|
||
|
||
// API Mode: 기존 로직 100% 유지
|
||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||
console.log('[BuyOption] API Mode - using reduxProductOptionInfos');
|
||
return reduxProductOptionInfos;
|
||
}
|
||
|
||
// Mock Mode: 옵션 데이터가 없거나 배열이 아니면 Mock 데이터 생성
|
||
const isValidReduxData = Array.isArray(reduxProductOptionInfos) && reduxProductOptionInfos.length > 0;
|
||
|
||
if (!isValidReduxData) {
|
||
console.log('[BuyOption] Mock Mode - generating mock option data (reduxData invalid)');
|
||
const mockOptionData = createMockProductOptionData(productData);
|
||
console.log('[BuyOption] Mock Mode - createMockProductOptionData result:', mockOptionData);
|
||
// Mock 옵션 데이터는 이미 배열 구조로 반환됨
|
||
return mockOptionData || [];
|
||
}
|
||
|
||
// Mock Mode이고 유효한 옵션 데이터가 있으면 그대로 사용
|
||
console.log('[BuyOption] Mock Mode - using existing valid reduxProductOptionInfos');
|
||
return reduxProductOptionInfos;
|
||
}, [reduxProductOptionInfos, productData]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
|
||
const logInfo = useMemo(() => {
|
||
if (productData) {
|
||
// productData가 있으면 SingleOption과 동일하게 처리
|
||
let couponInfo;
|
||
|
||
if (partnerCoupon && partnerCoupon.length > 0) {
|
||
couponInfo = partnerCoupon[0];
|
||
}
|
||
|
||
const { catCd, catNm, patncNm, patnrId, prdtId, prdtNm, priceInfo } =
|
||
productData;
|
||
const { cpnSno, cpnTtl } = couponInfo || {};
|
||
const prodOptSno =
|
||
(productOptionInfos &&
|
||
productOptionInfos.length > 0 &&
|
||
productOptionInfos[0]?.prodOptSno) ||
|
||
'';
|
||
const prodOptTpCdCval =
|
||
(productOptionInfos &&
|
||
productOptionInfos.length > 0 &&
|
||
productOptionInfos[0]?.prodOptTpCdCval) ||
|
||
'';
|
||
|
||
return {
|
||
cpnSno: String(cpnSno) || '',
|
||
cpnTtl: cpnTtl || '',
|
||
dcAftrPrc: priceInfo.split('|')[1],
|
||
dcBefPrc: priceInfo.split('|')[0],
|
||
lgCatCd: catCd || '',
|
||
lgCatNm: catNm || '',
|
||
patncNm,
|
||
patnrId,
|
||
prodId: prdtId,
|
||
prodNm: prdtNm,
|
||
prodOptSno: prodOptSno,
|
||
prodOptTpCdCval: prodOptTpCdCval,
|
||
qty: String(quantity),
|
||
};
|
||
} else if (productInfo) {
|
||
// productData가 없으면 productInfo 사용
|
||
let couponInfo;
|
||
|
||
if (partnerCoupon && partnerCoupon.length > 0) {
|
||
couponInfo = partnerCoupon[0];
|
||
}
|
||
|
||
const { catCd, catNm, patncNm, patnrId, prdtId, prdtNm, priceInfo } =
|
||
productInfo;
|
||
const { cpnSno, cpnTtl } = couponInfo || {};
|
||
const prodOptSno =
|
||
(productOptionInfos &&
|
||
productOptionInfos.length > 0 &&
|
||
productOptionInfos[0]?.prodOptSno) ||
|
||
'';
|
||
const prodOptTpCdCval =
|
||
(productOptionInfos &&
|
||
productOptionInfos.length > 0 &&
|
||
productOptionInfos[0]?.prodOptTpCdCval) ||
|
||
'';
|
||
|
||
return {
|
||
cpnSno: String(cpnSno) || '',
|
||
cpnTtl: cpnTtl || '',
|
||
dcAftrPrc: priceInfo.split('|')[1],
|
||
dcBefPrc: priceInfo.split('|')[0],
|
||
lgCatCd: catCd || '',
|
||
lgCatNm: catNm || '',
|
||
patncNm,
|
||
patnrId,
|
||
prodId: prdtId,
|
||
prodNm: prdtNm,
|
||
prodOptSno: prodOptSno,
|
||
prodOptTpCdCval: prodOptTpCdCval,
|
||
qty: String(quantity),
|
||
};
|
||
}
|
||
|
||
return {};
|
||
}, [partnerCoupon, productData, productInfo, productOptionInfos, quantity]);
|
||
|
||
// 옵션 리셋 로직 (SingleOption과 동일)
|
||
useEffect(() => {
|
||
if (type !== 'theme') {
|
||
return;
|
||
}
|
||
|
||
setSelectedOptions();
|
||
setIsOptionValue(false);
|
||
setSelectedOptionItemIndex(0);
|
||
setIsOptionSelect(false);
|
||
setQuantity(1);
|
||
setSelectedBtnOptIdx(0);
|
||
}, [selectedIndex, productOptionInfos, type]);
|
||
|
||
// 옵션 자동 선택 로직 (SingleOption과 동일)
|
||
// Mock Mode: 항상 첫 번째 옵션을 자동으로 선택
|
||
useEffect(() => {
|
||
console.log('[BuyOption] autoSelect useEffect - productOptionInfos:', productOptionInfos);
|
||
console.log('[BuyOption] autoSelect useEffect - selectedBtnOptIdx:', selectedBtnOptIdx);
|
||
console.log('[BuyOption] autoSelect useEffect - isOptionValue:', isOptionValue);
|
||
|
||
if (!productOptionInfos || productOptionInfos.length === 0) {
|
||
console.log('[BuyOption] autoSelect - productOptionInfos is empty, returning');
|
||
return;
|
||
}
|
||
|
||
const currentOptionGroup = productOptionInfos[selectedBtnOptIdx];
|
||
if (!currentOptionGroup) {
|
||
console.log('[BuyOption] autoSelect - currentOptionGroup is not found at index:', selectedBtnOptIdx);
|
||
return;
|
||
}
|
||
|
||
const optionDetails = currentOptionGroup.prdtOptDtl;
|
||
console.log('[BuyOption] autoSelect - optionDetails:', optionDetails);
|
||
|
||
if (!optionDetails || optionDetails.length === 0) {
|
||
console.log('[BuyOption] autoSelect - optionDetails is empty');
|
||
return;
|
||
}
|
||
|
||
// 이미 선택되었으면 스킵
|
||
if (isOptionValue) {
|
||
console.log('[BuyOption] autoSelect - already selected, skipping');
|
||
return;
|
||
}
|
||
|
||
// Mock Mode: 첫 번째 옵션 자동 선택
|
||
if (BUYNOW_CONFIG.isMockMode()) {
|
||
console.log('[BuyOption] Mock Mode - Auto selecting first option:', optionDetails[0]);
|
||
setSelectedOptions(optionDetails[0]);
|
||
setIsOptionValue(true);
|
||
}
|
||
// API Mode: 옵션이 1개일 때만 자동 선택
|
||
else if (optionDetails.length === 1) {
|
||
console.log('[BuyOption] API Mode - Auto selecting only option:', optionDetails[0]);
|
||
setSelectedOptions(optionDetails[0]);
|
||
setIsOptionValue(true);
|
||
}
|
||
}, [productOptionInfos, selectedBtnOptIdx]);
|
||
|
||
// 필수 데이터 로드 (SingleOption과 동일)
|
||
// Mock Mode: API 호출 스킵 (Mock 데이터만 사용)
|
||
useEffect(() => {
|
||
// API Mode: 실제 API 호출
|
||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||
dispatch(
|
||
getProductOption({
|
||
patnrId: selectedPatnrId,
|
||
prdtId: selectedPrdtId,
|
||
})
|
||
);
|
||
|
||
dispatch(
|
||
getProductCouponSearch({
|
||
patnrId: selectedPatnrId,
|
||
prdtId: selectedPrdtId,
|
||
mbrNo: userNumber,
|
||
})
|
||
);
|
||
}
|
||
// Mock Mode: API 호출 하지 않음
|
||
}, [dispatch, selectedPatnrId, selectedPrdtId, userNumber]);
|
||
|
||
// 포커스 관리 로직 (SingleOption과 유사)
|
||
useEffect(() => {
|
||
// if (!isSpotlight) {
|
||
// // isSpotlight이 false면 일반적인 BuyOption 포커스
|
||
// console.log('[BuyOption] Component mounted - focusing BUY NOW button');
|
||
|
||
// Spotlight.focus('buy-option-buy-now-button');
|
||
|
||
// return;
|
||
// }
|
||
|
||
// isSpotlight이 true이고 SingleOption 동작이 필요한 경우 요부분
|
||
if (productInfo?.optProdYn === 'N') {
|
||
Spotlight.focus('buy-option-buy-now-button');
|
||
} else if (productInfo?.optProdYn === 'Y') {
|
||
if(
|
||
productOptionInfos &&
|
||
productOptionInfos?.length > 1
|
||
){
|
||
Spotlight.focus('buy-option-first-dropdown');
|
||
} else if (
|
||
productOptionInfos?.legnth > 0 &&
|
||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1
|
||
){
|
||
Spotlight.focus('buy-option-second-dropdown');
|
||
} else {
|
||
Spotlight.focus('buy-option-buy-now-button');
|
||
}
|
||
} else {
|
||
Spotlight.focus('buy-option-buy-now-button');
|
||
}
|
||
}, [productOptionInfos?.length, productInfo?.length]);
|
||
|
||
// checkOutValidate 콜백 함수 (SingleOption과 동일한 로직)
|
||
function checkOutValidate(response) {
|
||
if (response) {
|
||
if (response.retCode === 0) {
|
||
if (
|
||
response.data.cardInfo === null ||
|
||
response.data.billingAddressList.length === 0 ||
|
||
response.data.shippingAddressList.length === 0
|
||
) {
|
||
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
|
||
dispatch(changeAppStatus({ isLoading: false }));
|
||
return;
|
||
} else {
|
||
const { mbrId, prdtId, prodSno } = response.data.productList[0];
|
||
const cartTpSno = `${mbrId}_${prdtId}_${prodSno}`;
|
||
|
||
// CheckOutPanel 이동 전에 ProductVideoV2 타이머 및 MediaPanel/PlayerPanel 정리
|
||
console.log('[BuyOption] API Mode - Cleaning up media panels before checkout');
|
||
clearAllVideoTimers(); // ProductVideoV2의 타이머 정리
|
||
dispatch(finishMediaPreview()); // MediaPanel 정리
|
||
dispatch(popPanel(Config.panel_names.PLAYER_PANEL)); // PlayerPanel 제거
|
||
|
||
// 🔴 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 } },
|
||
}));
|
||
});
|
||
|
||
dispatch(sendLogPaymentEntry({ ...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;
|
||
}
|
||
}
|
||
|
||
Spotlight.focus('buy-option-buy-now-button');
|
||
}
|
||
|
||
const handleBuyNowClick = useCallback(() => {
|
||
console.log('%c🔥🔥🔥 BUY NOW CLICKED! FUNCTION CALLED! 🔥🔥🔥', 'background: red; color: white; font-size: 16px; font-weight: bold; padding: 5px;');
|
||
console.log('%cproductInfo exists:', 'background: red; color: white; padding: 3px;', !!productInfo);
|
||
console.log('%cuserNumber exists:', 'background: red; color: white; padding: 3px;', !!userNumber);
|
||
console.log('%cselectedOptions exists:', 'background: red; color: white; padding: 3px;', !!selectedOptions);
|
||
console.log('%cselectedPatnrId:', 'background: red; color: white; padding: 3px;', selectedPatnrId);
|
||
console.log('%cselectedPrdtId:', 'background: red; color: white; padding: 3px;', selectedPrdtId);
|
||
|
||
// const optionName = renderOptionName();
|
||
// const optionValueName = renderOptionValue();
|
||
|
||
if (productInfo && productInfo?.soldoutFlag === 'Y') {
|
||
console.log('%c[BuyOption] ❌ Product is sold out', 'background: red; color: white; padding: 3px;');
|
||
return;
|
||
}
|
||
if (!userNumber || userNumber === '') {
|
||
console.log('%c[BuyOption] ❌ User not logged in - showing login popup', 'background: red; color: white; padding: 3px;');
|
||
dispatch(setShowPopup(Config.ACTIVE_POPUP.loginPopup));
|
||
return;
|
||
}
|
||
|
||
// // 옵션 선택 검증 (SingleOption과 동일)
|
||
// if (
|
||
// productOptionInfos &&
|
||
// productOptionInfos.length > 0 &&
|
||
// (optionName === "SELECT" || optionValueName === "SELECT")
|
||
// ) {
|
||
// console.log("###Test pjh 2", optionName);
|
||
// return dispatch(setShowPopup(Config.ACTIVE_POPUP.loginPopup));
|
||
// }
|
||
|
||
// Mock Mode가 아닐 때만 옵션 선택 검증
|
||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||
//옵션 선택 안하면 구매 안돼도록.
|
||
if (
|
||
productOptionInfos &&
|
||
productOptionInfos.length > 1 &&
|
||
productInfo?.optProdYn === 'Y'
|
||
) {
|
||
|
||
if (selectFirstOptionIndex === 0) {
|
||
dispatch(
|
||
showError(
|
||
null,
|
||
"PLEASE SELECT OPTION",
|
||
false,
|
||
null,
|
||
null
|
||
)
|
||
);
|
||
|
||
return;
|
||
}
|
||
}
|
||
if (
|
||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1 &&
|
||
productInfo?.optProdYn === 'Y'
|
||
) {
|
||
|
||
if (selectSecondOptionIndex === 0) {
|
||
dispatch(
|
||
showError(
|
||
null,
|
||
"PLEASE SELECT OPTION",
|
||
false,
|
||
null,
|
||
null
|
||
)
|
||
);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 필수 데이터 검증 (API Mode만)
|
||
if (!userNumber || !selectedPatnrId || !selectedPrdtId || !quantity) {
|
||
console.error('%c[BuyOption] ❌ Missing required data:', 'background: red; color: white; padding: 3px;', {
|
||
userNumber: !!userNumber,
|
||
selectedPatnrId: !!selectedPatnrId,
|
||
selectedPrdtId: !!selectedPrdtId,
|
||
quantity: !!quantity
|
||
});
|
||
dispatch(showError(null, 'Missing required information', false, null, null));
|
||
return;
|
||
}
|
||
} else {
|
||
// Mock Mode: 최소한의 검증만 수행
|
||
console.log('%c[BuyOption] Mock Mode - Bypassing option validation', 'background: blue; color: white; padding: 3px;');
|
||
if (!selectedPatnrId || !selectedPrdtId) {
|
||
console.error('%c[BuyOption] ❌ Mock Mode - Missing critical IDs:', 'background: red; color: white; padding: 3px;', {
|
||
selectedPatnrId: !!selectedPatnrId,
|
||
selectedPrdtId: !!selectedPrdtId
|
||
});
|
||
dispatch(showError(null, 'Product ID is missing', false, null, null));
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Mock Mode: 조건 완화 (userNumber 체크 제외)
|
||
const shouldProceed = BUYNOW_CONFIG.isMockMode()
|
||
? (selectedPatnrId && selectedPrdtId && quantity)
|
||
: (userNumber && selectedPatnrId && selectedPrdtId && quantity);
|
||
|
||
console.log('%c[BuyOption] shouldProceed check:', 'background: purple; color: white; padding: 3px;', {
|
||
shouldProceed,
|
||
isMockMode: BUYNOW_CONFIG.isMockMode(),
|
||
userNumber: !!userNumber,
|
||
selectedPatnrId: !!selectedPatnrId,
|
||
selectedPrdtId: !!selectedPrdtId,
|
||
quantity
|
||
});
|
||
|
||
if (shouldProceed) {
|
||
const { prodOptCval, priceInfo } = selectedOptions || {};
|
||
const { patncNm, brndNm, catNm, prdtNm, prdtId } = productInfo;
|
||
|
||
console.log('[BuyOption] handleClickOrder - productInfo:', productInfo);
|
||
console.log('[BuyOption] handleClickOrder - selectedOptions:', selectedOptions);
|
||
console.log('[BuyOption] handleClickOrder - priceInfo:', priceInfo);
|
||
console.log('[BuyOption] handleClickOrder - logInfo:', logInfo);
|
||
|
||
// productInfo에서 직접 price2, price5 추출 (가장 신뢰할 수 있는 소스)
|
||
// price2 = 상품 가격 ("$ 521.66" 형식)
|
||
// price5 = 쿠폰 할인 ("$ 104.33" 형식)
|
||
const extractNumericPrice = (priceStr) => {
|
||
if (typeof priceStr === 'number') return priceStr;
|
||
if (typeof priceStr === 'string') {
|
||
const cleanedValue = priceStr.replace(/,/g, '');
|
||
const numMatch = cleanedValue.match(/[\d.]+/);
|
||
return numMatch ? parseFloat(numMatch[0]) : 0;
|
||
}
|
||
return 0;
|
||
};
|
||
|
||
const price3Value = extractNumericPrice(productInfo?.price3 ? productInfo?.price3 : productInfo?.price2);
|
||
const price5Value = extractNumericPrice(productInfo?.price5);
|
||
|
||
console.log('[BuyOption] handleClickOrder - productInfo.price2:', productInfo?.price3, '-> extracted:', price3Value);
|
||
console.log('[BuyOption] handleClickOrder - productInfo.price5:', productInfo?.price5, '-> extracted:', price5Value);
|
||
|
||
// 가격 계산:
|
||
// discountPrice = price2 (상품 가격)
|
||
// regularPrice = price2 + price5 (상품 가격 + 할인액 = 원래 가격)
|
||
let discountPrice = price3Value > 0 ? price3Value : 0;
|
||
let regularPrice = price3Value + price5Value; // price2 + price5 = 원래 가격
|
||
|
||
const discountRate = priceInfo?.split('|')[4];
|
||
|
||
console.log('[BuyOption] handleClickOrder - Calculated prices - regularPrice:', regularPrice, 'discountPrice:', discountPrice);
|
||
|
||
// selectedOptions가 없고 logInfo가 있으면, logInfo의 가격도 고려
|
||
if (!selectedOptions && logInfo) {
|
||
console.log('[BuyOption] handleClickOrder - selectedOptions is undefined, checking logInfo prices');
|
||
// logInfo의 dcBefPrc와 dcAftrPrc는 "$ 521.66" 형식이므로 숫자만 추출 (소수점 포함)
|
||
const dcBefPrcMatch = logInfo.dcBefPrc?.match(/[\d.]+/);
|
||
const dcAftrPrcMatch = logInfo.dcAftrPrc?.match(/[\d.]+/);
|
||
const logRegularPrice = dcBefPrcMatch ? parseFloat(dcBefPrcMatch[0]) : 0;
|
||
const logDiscountPrice = dcAftrPrcMatch ? parseFloat(dcAftrPrcMatch[0]) : logRegularPrice;
|
||
|
||
// logInfo의 가격이 있으면 우선
|
||
if (logRegularPrice > 0 || logDiscountPrice > 0) {
|
||
regularPrice = logRegularPrice > 0 ? logRegularPrice : regularPrice;
|
||
discountPrice = logDiscountPrice > 0 ? logDiscountPrice : discountPrice;
|
||
console.log('[BuyOption] handleClickOrder - Using logInfo prices - regularPrice:', regularPrice, 'discountPrice:', discountPrice);
|
||
}
|
||
}
|
||
|
||
console.log('[BuyOption] handleClickOrder - Final prices - regularPrice:', regularPrice, 'discountPrice:', discountPrice, 'discountRate:', discountRate);
|
||
|
||
dispatch(
|
||
sendLogTotalRecommend({
|
||
nowMenu: nowMenu,
|
||
productId: prdtId,
|
||
productTitle: prdtNm,
|
||
partner: patncNm,
|
||
price: discountRate ? discountPrice : regularPrice,
|
||
discount: discountRate,
|
||
brand: brndNm,
|
||
productOption: prodOptCval || '',
|
||
category: catNm,
|
||
contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
|
||
messageId: Config.LOG_MESSAGE_ID.BUY_NOW,
|
||
})
|
||
);
|
||
|
||
// Mock Mode: API 호출 스킵
|
||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||
dispatch(
|
||
getMyInfoCheckoutInfo(
|
||
{
|
||
mbrNo: userNumber,
|
||
dirPurcSelYn: 'Y',
|
||
cartList: [
|
||
{
|
||
patnrId: selectedPatnrId,
|
||
prdtId: selectedPrdtId,
|
||
prodOptCdCval: selectedOptions?.prodOptCdCval
|
||
? selectedOptions.prodOptCdCval
|
||
: null,
|
||
prodQty: String(quantity),
|
||
prodOptTpCdCval: productOptionInfos[0]?.prodOptTpCdCval,
|
||
},
|
||
],
|
||
},
|
||
checkOutValidate
|
||
)
|
||
);
|
||
} else {
|
||
// Mock Mode: 체크아웃 페이지로 이동 (시뮬레이션)
|
||
// panelInfo의 logInfo에 선택한 상품 정보를 포함시켜 CheckOutPanel에서 사용
|
||
console.log('[BuyOption] Mock Mode - Simulating checkout');
|
||
console.log('[BuyOption] logInfo:', logInfo);
|
||
console.log('[BuyOption] Dispatching pushPanel to CHECKOUT_PANEL');
|
||
|
||
// CheckOutPanel 이동 전에 ProductVideoV2 타이머 및 MediaPanel/PlayerPanel 정리
|
||
console.log('[BuyOption] Mock Mode - Cleaning up media panels before checkout');
|
||
clearAllVideoTimers(); // ProductVideoV2의 타이머 정리
|
||
dispatch(finishMediaPreview()); // MediaPanel 정리
|
||
dispatch(popPanel(Config.panel_names.PLAYER_PANEL)); // PlayerPanel 제거
|
||
|
||
// Mock 모드: 선택 상품의 정보를 panelInfo에 담아서 전달
|
||
// CheckOutPanel에서 이 정보로 Mock 상품 데이터 생성
|
||
// 이미지 URL 추출 (productInfo의 imgList 또는 thumbnailUrl 사용)
|
||
const imgUrl = productInfo?.imgList?.[0]?.imgUrl ||
|
||
productInfo?.thumbnailUrl ||
|
||
productInfo?.patncLogPath ||
|
||
'/mock/image.jpg';
|
||
|
||
const mockProductInfo = {
|
||
prdtId: selectedPrdtId,
|
||
prdtNm: productInfo?.prdtNm,
|
||
patnrId: selectedPatnrId,
|
||
patncNm: patncNm,
|
||
prodQty: quantity,
|
||
origPrice: regularPrice || 99999, // 원래 가격 (숫자)
|
||
discountPrice: discountPrice || regularPrice || 99999, // 할인된 가격 (실제 판매 가격, 숫자)
|
||
finalPrice: discountPrice || regularPrice || 99999, // 최종 가격 (숫자)
|
||
currSign: '$',
|
||
currSignLoc: 'left',
|
||
imgUrl: imgUrl, // 상품 이미지 URL 추가
|
||
};
|
||
|
||
// Mock Mode: productInfo 최소 검증만 수행
|
||
if (!productInfo || !productInfo.prdtId) {
|
||
console.error('%c[BuyOption] ❌ Mock Mode - productInfo is invalid:', 'background: red; color: white; font-weight: bold; padding: 5px;', productInfo);
|
||
// Mock Mode에서는 기본값으로 진행 시도
|
||
console.log('%c[BuyOption] Mock Mode - Attempting to proceed with fallback data', 'background: orange; color: white; padding: 3px;');
|
||
} else {
|
||
console.log('%c[BuyOption] ✅ Mock Mode - productInfo is valid:', 'background: green; color: white; font-weight: bold; padding: 5px;', productInfo.prdtId);
|
||
}
|
||
|
||
const checkoutPanelInfo = {
|
||
logInfo: { ...logInfo, cartTpSno: `MOCK_${Date.now()}` },
|
||
productInfo: {
|
||
prdtId: productInfo?.prdtId || selectedPrdtId || 'MOCK_PRODUCT',
|
||
prdtNm: productInfo?.prdtNm || 'Mock Product',
|
||
patnrId: selectedPatnrId || '1',
|
||
patncNm: patncNm || 'Mock Partner',
|
||
// calculateOrderSummaryFromProductInfo 함수가 필요한 필드들
|
||
// productInfo에서 직접 추출한 값을 전달
|
||
price2: price3Value, // Items (상품 가격)
|
||
price5: price5Value, // Coupon Savings (할인액)
|
||
finalPrice: discountPrice,
|
||
discount: price5Value,
|
||
origPrice: regularPrice,
|
||
discountPrice: discountPrice,
|
||
prodQty:quantity,
|
||
// 추가 가격 필드들 (fallback용)
|
||
price: discountPrice,
|
||
originalPrice: regularPrice,
|
||
// 이미지 정보
|
||
...(productInfo.imgList && { imgList: productInfo.imgList }),
|
||
...(productInfo.thumbnailUrl && { thumbnailUrl: productInfo.thumbnailUrl }),
|
||
...(productInfo.imgUrls && { imgUrls: productInfo.imgUrls }),
|
||
shippingCharge: productInfo?.shippingFee || '12.99', // 배송비
|
||
shippingFee: productInfo?.shippingFee || '12.99',
|
||
currSign: '$',
|
||
currSignLoc: 'left',
|
||
},
|
||
defaultPrice: discountPrice,
|
||
};
|
||
|
||
console.log('[BuyOption] Mock Mode - checkoutPanelInfo.productInfo price fields:', {
|
||
price2: price3Value,
|
||
price5: price5Value,
|
||
finalPrice: discountPrice,
|
||
origPrice: regularPrice,
|
||
discountPrice: discountPrice,
|
||
});
|
||
|
||
console.log('[BuyOption] Mock Mode - checkoutPanelInfo:', checkoutPanelInfo);
|
||
console.log('[BuyOption] Mock Mode - regularPrice:', regularPrice, 'discountPrice:', discountPrice);
|
||
|
||
console.log('%c[BuyOption] 🔍 Determining UPDATE vs PUSH for CheckOutPanel', 'background: orange; color: white; font-weight: bold; padding: 5px;');
|
||
|
||
// Mock Mode: checkoutPanelInfo가 있으면 무조건 진행
|
||
if (!checkoutPanelInfo) {
|
||
console.error('%c[BuyOption] ❌ Mock Mode - checkoutPanelInfo is null/undefined', 'background: red; color: white; padding: 3px;');
|
||
// 최소한의 fallback 데이터로 진행 시도
|
||
const fallbackPanelInfo = {
|
||
logInfo: { cartTpSno: `MOCK_FALLBACK_${Date.now()}` },
|
||
productInfo: {
|
||
prdtId: selectedPrdtId || 'MOCK_PRODUCT',
|
||
prdtNm: 'Mock Product',
|
||
patnrId: selectedPatnrId || '1',
|
||
patncNm: 'Mock Partner',
|
||
finalPrice: 99999,
|
||
origPrice: 99999,
|
||
discountPrice: 99999,
|
||
currSign: '$',
|
||
currSignLoc: 'left',
|
||
},
|
||
defaultPrice: 99999,
|
||
};
|
||
console.log('%c[BuyOption] Mock Mode - Using fallback panelInfo', 'background: orange; color: white; padding: 3px;', fallbackPanelInfo);
|
||
|
||
// 🔴 CRITICAL: DetailPanel Pop 완료 후 CheckOutPanel Push (Promise 체인)
|
||
dispatch((dispatchFn) => {
|
||
return new Promise((resolve) => {
|
||
console.log('[BuyOption] 1️⃣ Popping DetailPanel...');
|
||
dispatchFn(popPanel(Config.panel_names.DETAIL_PANEL));
|
||
|
||
// Pop이 끝난 후 Push 실행
|
||
Promise.resolve().then(() => {
|
||
console.log('[BuyOption] 2️⃣ Pushing CheckOutPanel...');
|
||
dispatchFn(
|
||
pushPanel({
|
||
name: Config.panel_names.CHECKOUT_PANEL,
|
||
panelInfo: fallbackPanelInfo,
|
||
})
|
||
);
|
||
console.log('[BuyOption] ✅ Panel transition complete');
|
||
resolve();
|
||
});
|
||
});
|
||
});
|
||
} else {
|
||
// 정상 케이스: checkoutPanelInfo 사용
|
||
// 🔴 CRITICAL: DetailPanel Pop 완료 후 CheckOutPanel Push (Promise 체인)
|
||
dispatch((dispatchFn) => {
|
||
return new Promise((resolve) => {
|
||
console.log('[BuyOption] 1️⃣ Normal - Popping DetailPanel...');
|
||
dispatchFn(popPanel(Config.panel_names.DETAIL_PANEL));
|
||
|
||
// Pop이 끝난 후 Push 실행
|
||
Promise.resolve().then(() => {
|
||
console.log('[BuyOption] 2️⃣ Normal - Pushing CheckOutPanel...');
|
||
dispatchFn(
|
||
pushPanel({
|
||
name: Config.panel_names.CHECKOUT_PANEL,
|
||
panelInfo: checkoutPanelInfo,
|
||
})
|
||
);
|
||
console.log('[BuyOption] ✅ Normal - Panel transition complete');
|
||
resolve();
|
||
});
|
||
});
|
||
});
|
||
}
|
||
console.log('%c[BuyOption] ✅ AFTER pushPanel dispatch', 'background: orange; color: white; font-weight: bold; padding: 5px;');
|
||
|
||
// Toast 정리는 성공적으로 패널 이동 후에만 실행
|
||
dispatch(clearAllToasts());
|
||
}
|
||
}
|
||
}, [
|
||
dispatch,
|
||
userNumber,
|
||
selectedPatnrId,
|
||
selectedPrdtId,
|
||
selectedOptions,
|
||
productInfo,
|
||
productOptionInfos,
|
||
quantity,
|
||
logInfo,
|
||
selectFirstOptionIndex,
|
||
selectSecondOptionIndex,
|
||
]);
|
||
|
||
// ADD TO CART 버튼 클릭 핸들러
|
||
const handleAddToCartClick = useCallback(() => {
|
||
console.log('[BuyOption] ADD TO CART clicked');
|
||
|
||
// 상품 품절 체크
|
||
if (productInfo && productInfo?.soldoutFlag === 'Y') {
|
||
return;
|
||
}
|
||
|
||
// 로그인 체크
|
||
if (!userNumber || userNumber === '') {
|
||
return dispatch(setShowPopup(Config.ACTIVE_POPUP.loginPopup));
|
||
}
|
||
|
||
// 장바구니에 상품 추가
|
||
if (userNumber && selectedPatnrId && selectedPrdtId && quantity) {
|
||
const { prodOptCval, priceInfo } = selectedOptions || {};
|
||
const { patncNm, brndNm, catNm, prdtNm, prdtId } = productInfo;
|
||
const regularPrice = priceInfo?.split('|')[0];
|
||
const discountPrice = priceInfo?.split('|')[1];
|
||
const discountRate = priceInfo?.split('|')[4];
|
||
|
||
// 로그 전송
|
||
// dispatch(
|
||
// sendLogTotalRecommend({
|
||
// nowMenu: nowMenu,
|
||
// productId: prdtId,
|
||
// productTitle: prdtNm,
|
||
// partner: patncNm,
|
||
// price: discountRate ? discountPrice : regularPrice,
|
||
// discount: discountRate,
|
||
// brand: brndNm,
|
||
// productOption: prodOptCval || '',
|
||
// category: catNm,
|
||
// contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
|
||
// messageId: Config.LOG_MESSAGE_ID.ADD_TO_CART,
|
||
// })
|
||
// );
|
||
|
||
// API Mode: 실제 API 호출 후 CartPanel로 이동
|
||
if (!BUYNOW_CONFIG.isMockMode()) {
|
||
// 장바구니에 추가
|
||
dispatch(
|
||
addToCart({
|
||
mbrNo: userNumber,
|
||
patnrId: selectedPatnrId,
|
||
prdtId: selectedPrdtId,
|
||
prodQty: String(quantity),
|
||
prdtOpt: {
|
||
prodOptCdCval: selectedOptions?.prodOptCdCval
|
||
? selectedOptions.prodOptCdCval
|
||
: "",
|
||
prodOptCval:productOptionInfos[0]?.optNm,
|
||
prodOptSno: productOptionInfos[0]?.prodOptSno,
|
||
prodOptTpCdCval: productOptionInfos[0]?.prodOptTpCdCval,
|
||
}
|
||
})
|
||
);
|
||
|
||
// CartPanel로 이동 (productInfo 포함) - API에서는 이미 addToCart 호출됨
|
||
// 이미지 URL 구성 (CheckOutPanel과 동일한 방식)
|
||
// ✅ 이미지 URL 추출 (ProductAllSection의 고품질 이미지 우선)
|
||
const imgUrl = productInfo?.imgUrls600?.[0] || // ✅ ProductAllSection에서 사용하는 고품질 이미지
|
||
productInfo?.thumbnailUrl960 || // ✅ 960px 썸네일
|
||
productInfo?.imgList?.[0]?.imgUrl ||
|
||
productInfo?.thumbnailUrl ||
|
||
productInfo?.patncLogPath ||
|
||
'/assets/images/img-thumb-empty-144@3x.png';
|
||
|
||
// ✅ 정확한 가격 추출 (Mock Mode와 동일한 로직)
|
||
console.log('[BuyOption] API Mode ADD TO CART - Extracting accurate prices from productInfo');
|
||
console.log('[BuyOption] API Mode ADD TO CART - productInfo.price2:', productInfo?.price2);
|
||
console.log('[BuyOption] API Mode ADD TO CART - productInfo.price5:', productInfo?.price5);
|
||
console.log('[BuyOption] API Mode ADD TO CART - productInfo.priceInfo:', productInfo?.priceInfo);
|
||
|
||
// 정확한 가격 추출 로직
|
||
const extractNumericPrice = (value) => {
|
||
if (typeof value === 'number') return value;
|
||
if (typeof value === 'string') {
|
||
const cleanedValue = value.replace(/,/g, '');
|
||
const numMatch = cleanedValue.match(/[\d.]+/);
|
||
return numMatch ? parseFloat(numMatch[0]) : 0;
|
||
}
|
||
return 0;
|
||
};
|
||
|
||
const price2Value = extractNumericPrice(productInfo?.price2);
|
||
const price5Value = extractNumericPrice(productInfo?.price5);
|
||
|
||
console.log('[BuyOption] API Mode ADD TO CART - extracted price2Value:', price2Value);
|
||
console.log('[BuyOption] API Mode ADD TO CART - extracted price5Value:', price5Value);
|
||
|
||
// 정확한 가격 계산 (ProductAllSection의 가격 정보 사용)
|
||
let discountPrice = price2Value > 0 ? price2Value : 521.66; // ProductAllSection 가격 fallback
|
||
let regularPrice = price2Value + price5Value;
|
||
|
||
// priceInfo에서 직접 가격 추출 시도
|
||
if (productInfo?.priceInfo) {
|
||
const priceParts = productInfo.priceInfo.split('|');
|
||
if (priceParts.length >= 2) {
|
||
const infoDiscountPrice = extractNumericPrice(priceParts[1]);
|
||
const infoRegularPrice = extractNumericPrice(priceParts[0]);
|
||
if (infoDiscountPrice > 0) {
|
||
discountPrice = infoDiscountPrice;
|
||
regularPrice = infoRegularPrice > 0 ? infoRegularPrice : discountPrice;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('[BuyOption] API Mode ADD TO CART - Final calculated prices:', {
|
||
discountPrice,
|
||
regularPrice,
|
||
price2Value,
|
||
price5Value
|
||
});
|
||
|
||
const productInfoForCart = {
|
||
// 기본 정보
|
||
prdtId: selectedPrdtId,
|
||
prdtNm: productInfo?.prdtNm || 'Product',
|
||
patnrId: selectedPatnrId,
|
||
patncNm: productInfo?.patncNm || 'Partner',
|
||
patncLogPath: productInfo?.patncLogPath || '',
|
||
|
||
// ✅ 이미지 정보 (ProductAllSection의 고품질 이미지 포함)
|
||
imgUrl: imgUrl,
|
||
thumbnailUrl: productInfo?.thumbnailUrl960 || imgUrl,
|
||
thumbnailUrl960: productInfo?.thumbnailUrl960,
|
||
imgList: productInfo?.imgList || [{ imgUrl: imgUrl }],
|
||
imgUrls: productInfo?.imgUrls || [{ imgUrl: imgUrl }], // imgUrls 배열 구조 추가 (CheckOutPanel 호환성)
|
||
imgUrls600: productInfo?.imgUrls600, // ✅ 고품질 이미지 배열 포함
|
||
|
||
// 가격 정보 (정확한 가격 정보 - 문자열 형식으로)
|
||
price2: regularPrice.toFixed(2), // 원가 (ProductAllSection 가격)
|
||
price3: discountPrice.toFixed(2), // 할인가/판매가 (ProductAllSection 가격)
|
||
// 추가 가격 필드들 (다양한 fallback 지원)
|
||
finalPrice: discountPrice,
|
||
discountPrice: discountPrice,
|
||
origPrice: regularPrice,
|
||
discount: Math.max(0, regularPrice - discountPrice),
|
||
priceInfo: productInfo?.priceInfo, // 원본 priceInfo 보존
|
||
|
||
// 수량 정보
|
||
prodQty: quantity,
|
||
|
||
// 옵션 정보
|
||
optNm: productOptionInfos[0]?.optNm || '',
|
||
|
||
// 배송비 정보
|
||
shippingCharge: productInfo?.shippingFee || '12.99',
|
||
|
||
// 기타 정보
|
||
soldoutFlag: productInfo?.soldoutFlag || 'N',
|
||
};
|
||
|
||
const optionInfoForCart = {
|
||
name: productOptionInfos[0]?.optNm || '',
|
||
price: productOptionInfos[0]?.prdtOptDtl[0]?.optPrc || '0.00'
|
||
};
|
||
|
||
console.log('[BuyOption] 🛒 API Mode - Pushing CART_PANEL with name:', Config.panel_names.CART_PANEL);
|
||
console.log('[BuyOption] 🛒 API Mode - productInfoForCart:', productInfoForCart);
|
||
console.log('[BuyOption] 🛒 API Mode - optionInfoForCart:', optionInfoForCart);
|
||
console.log('[BuyOption] 🛒 API Mode - quantity:', quantity);
|
||
|
||
dispatch(
|
||
pushPanel({
|
||
name: Config.panel_names.CART_PANEL,
|
||
panelInfo: {
|
||
productInfo: productInfoForCart,
|
||
optionInfo: optionInfoForCart,
|
||
quantity: quantity
|
||
}
|
||
})
|
||
);
|
||
} else {
|
||
// Mock Mode: Mock 장바구니에 상품 추가 후 CartPanel로 이동
|
||
console.log('[BuyOption] Mock Mode - Adding to cart (Mock)');
|
||
|
||
// ✅ 이미지 URL 구성 (ProductAllSection의 고품질 이미지 우선)
|
||
const imgUrl = productInfo?.imgUrls600?.[0] || // ✅ ProductAllSection에서 사용하는 고품질 이미지
|
||
productInfo?.thumbnailUrl960 || // ✅ 960px 썸네일
|
||
productInfo?.imgList?.[0]?.imgUrl ||
|
||
productInfo?.thumbnailUrl ||
|
||
productInfo?.patncLogPath ||
|
||
'/assets/images/img-thumb-empty-144@3x.png';
|
||
|
||
// ✅ 정확한 가격 추출 (handleBuyNowClick와 동일한 로직 사용)
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - Extracting accurate prices from productInfo');
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.price2:', productInfo?.price2);
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.price5:', productInfo?.price5);
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - productInfo.priceInfo:', productInfo?.priceInfo);
|
||
|
||
// handleBuyNowClick의 가격 추출 로직과 동일하게 적용
|
||
const extractNumericPrice = (value) => {
|
||
if (typeof value === 'number') return value;
|
||
if (typeof value === 'string') {
|
||
const cleanedValue = value.replace(/,/g, '');
|
||
const numMatch = cleanedValue.match(/[\d.]+/);
|
||
return numMatch ? parseFloat(numMatch[0]) : 0;
|
||
}
|
||
return 0;
|
||
};
|
||
|
||
const price2Value = extractNumericPrice(productInfo?.price2);
|
||
const price5Value = extractNumericPrice(productInfo?.price5);
|
||
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - extracted price2Value:', price2Value);
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - extracted price5Value:', price5Value);
|
||
|
||
// 정확한 가격 계산 (ProductAllSection의 가격 정보 사용)
|
||
let discountPrice = price2Value > 0 ? price2Value : 521.66; // ProductAllSection 가격 fallback
|
||
let regularPrice = price2Value + price5Value;
|
||
|
||
// priceInfo에서 직접 가격 추출 시도
|
||
if (productInfo?.priceInfo) {
|
||
const priceParts = productInfo.priceInfo.split('|');
|
||
if (priceParts.length >= 2) {
|
||
const infoDiscountPrice = extractNumericPrice(priceParts[1]);
|
||
const infoRegularPrice = extractNumericPrice(priceParts[0]);
|
||
if (infoDiscountPrice > 0) {
|
||
discountPrice = infoDiscountPrice;
|
||
regularPrice = infoRegularPrice > 0 ? infoRegularPrice : discountPrice;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('[BuyOption] Mock Mode ADD TO CART - Final calculated prices:', {
|
||
discountPrice,
|
||
regularPrice,
|
||
price2Value,
|
||
price5Value
|
||
});
|
||
|
||
// Mock 상품 정보 구성 (CheckOutPanel 구조 참고)
|
||
const mockProductInfo = {
|
||
// 기본 정보
|
||
prdtId: selectedPrdtId,
|
||
prdtNm: productInfo?.prdtNm || 'Mock Product',
|
||
patnrId: selectedPatnrId,
|
||
patncNm: productInfo?.patncNm || 'Mock Partner',
|
||
patncLogPath: productInfo?.patncLogPath || '',
|
||
|
||
// ✅ 이미지 정보 (ProductAllSection의 고품질 이미지 포함)
|
||
imgUrl: imgUrl,
|
||
thumbnailUrl: productInfo?.thumbnailUrl960 || imgUrl,
|
||
thumbnailUrl960: productInfo?.thumbnailUrl960,
|
||
imgList: productInfo?.imgList || [{ imgUrl: imgUrl }], // imgList 배열 구조 추가
|
||
imgUrls: productInfo?.imgUrls || [{ imgUrl: imgUrl }], // imgUrls 배열 구조 추가 (CheckOutPanel 호환성)
|
||
imgUrls600: productInfo?.imgUrls600, // ✅ 고품질 이미지 배열 포함
|
||
|
||
// 가격 정보 (정확한 가격 정보 - 문자열 형식으로)
|
||
price2: regularPrice.toFixed(2), // 원가 (ProductAllSection 가격)
|
||
price3: discountPrice.toFixed(2), // 할인가/판매가 (ProductAllSection 가격)
|
||
// 추가 가격 필드들 (다양한 fallback 지원)
|
||
finalPrice: discountPrice,
|
||
discountPrice: discountPrice,
|
||
origPrice: regularPrice,
|
||
discount: Math.max(0, regularPrice - discountPrice),
|
||
priceInfo: productInfo?.priceInfo, // 원본 priceInfo 보존
|
||
|
||
// 수량 정보
|
||
prodQty: quantity,
|
||
|
||
// 옵션 정보 (필요시)
|
||
optNm: productOptionInfos[0]?.optNm || 'Default Option',
|
||
|
||
// 배송비 정보
|
||
shippingCharge: productInfo?.shippingFee || '12.99',
|
||
shippingFee: productInfo?.shippingFee || '12.99',
|
||
|
||
// 기타 정보
|
||
soldoutFlag: productInfo?.soldoutFlag || 'N',
|
||
};
|
||
|
||
// 옵션 정보 구성
|
||
const optionInfo = {
|
||
name: productOptionInfos[0]?.optNm || 'Default Option',
|
||
price: productOptionInfos[0]?.prdtOptDtl[0]?.optPrc || '0.00'
|
||
};
|
||
|
||
// CartPanel로 이동 (productInfo 포함) - CartPanel에서 직접 상품 추가
|
||
console.log('[BuyOption] 🛒 Pushing CART_PANEL with name:', Config.panel_names.CART_PANEL);
|
||
console.log('[BuyOption] 🛒 mockProductInfo:', mockProductInfo);
|
||
console.log('[BuyOption] 🛒 optionInfo:', optionInfo);
|
||
console.log('[BuyOption] 🛒 quantity:', quantity);
|
||
|
||
dispatch(
|
||
pushPanel({
|
||
name: Config.panel_names.CART_PANEL,
|
||
panelInfo: {
|
||
productInfo: mockProductInfo,
|
||
optionInfo: optionInfo,
|
||
quantity: quantity
|
||
}
|
||
})
|
||
);
|
||
}
|
||
}
|
||
|
||
dispatch(clearAllToasts());
|
||
}, [
|
||
dispatch,
|
||
userNumber,
|
||
selectedPatnrId,
|
||
selectedPrdtId,
|
||
selectedOptions,
|
||
productInfo,
|
||
productOptionInfos,
|
||
quantity,
|
||
nowMenu,
|
||
]);
|
||
|
||
const handleFirstOptionSelect = (selected) => {
|
||
const selectedIndex = selected.selected;
|
||
console.log('[BuyOption] First option selected:', selectedIndex);
|
||
|
||
// "Select Option"이 선택된 경우 (index 0)
|
||
if (selectedIndex === 0) {
|
||
// 초기 상태로 리셋
|
||
setSelectFirstOptionIndex(0);
|
||
setSelectedBtnOptIdx(0);
|
||
setSelectedOptionItemIndex(0);
|
||
setSelectedOptions(undefined);
|
||
setIsOptionValue(false);
|
||
setIsOptionSelect(false);
|
||
return;
|
||
}
|
||
|
||
// selectedBtnOptIdx는 UI 인덱스 그대로 저장 (드롭다운 표시용)
|
||
setSelectedBtnOptIdx(selectedIndex);
|
||
setSelectFirstOptionIndex(selectedIndex);
|
||
// 실제 데이터 접근 시에는 selectedIndex - 1 사용
|
||
const dataIndex = selectedIndex - 1;
|
||
setSelectedOptionItemIndex(0);
|
||
setSelectedOptions(productOptionInfos[dataIndex]?.prdtOptDtl[0]);
|
||
setIsOptionValue(false);
|
||
setIsOptionSelect(true);
|
||
};
|
||
|
||
const handleSecondOptionSelect = (selected) => {
|
||
const selectedIndex = selected.selected;
|
||
console.log('[BuyOption] Second option selected:', selectedIndex);
|
||
|
||
// selectedOptionItemIndex는 UI 인덱스 그대로 저장
|
||
setSelectedOptionItemIndex(selectedIndex);
|
||
setSelectSecondOptionIndex(selectedIndex);
|
||
// 실제 데이터 접근 시에는 -1 적용
|
||
const firstOptionDataIndex = selectedBtnOptIdx - 1; // 첫 번째 옵션의 실제 데이터 인덱스
|
||
const secondOptionDataIndex = selectedIndex - 1; // 두 번째 옵션의 실제 데이터 인덱스
|
||
|
||
setSelectedOptions(
|
||
productOptionInfos.length > 1
|
||
? productOptionInfos[firstOptionDataIndex]?.prdtOptDtl[
|
||
secondOptionDataIndex
|
||
]
|
||
: productOptionInfos[0]?.prdtOptDtl[secondOptionDataIndex]
|
||
);
|
||
dispatch(
|
||
getProductOptionId(
|
||
productOptionInfos.length > 1
|
||
? productOptionInfos[firstOptionDataIndex]?.prdtOptDtl[
|
||
secondOptionDataIndex
|
||
]?.prodOptCdCval
|
||
: productOptionInfos[0]?.prdtOptDtl[secondOptionDataIndex]
|
||
?.prodOptCdCval
|
||
)
|
||
);
|
||
setIsOptionValue(true);
|
||
};
|
||
|
||
// 수량 선택 핸들러
|
||
const handleQuantitySelect = (selected) => {
|
||
const qty = selected.selected + 1;
|
||
console.log('[BuyOption] Quantity selected:', qty);
|
||
setQuantity(qty);
|
||
};
|
||
|
||
// // 옵션명 렌더링 함수 (SingleOption과 동일)
|
||
// const renderOptionName = useCallback(() => {
|
||
// if (selectedOptions) {
|
||
// return productOptionInfos[selectedBtnOptIdx]?.optNm || null;
|
||
// }
|
||
// return $L("SELECT");
|
||
// }, [productOptionInfos, selectedOptions, selectedBtnOptIdx]);
|
||
|
||
// // 옵션값 렌더링 함수 (SingleOption과 동일)
|
||
// const renderOptionValue = useCallback(() => {
|
||
// if (
|
||
// productOptionInfos &&
|
||
// productOptionInfos[selectedBtnOptIdx] &&
|
||
// productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||
// productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 0 &&
|
||
// isOptionValue
|
||
// ) {
|
||
// return (
|
||
// productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl[
|
||
// selectedOptionItemIndex
|
||
// ]?.prodOptCval || null
|
||
// );
|
||
// }
|
||
// return $L("SELECT");
|
||
// }, [
|
||
// productOptionInfos,
|
||
// selectedBtnOptIdx,
|
||
// isOptionValue,
|
||
// selectedOptionItemIndex,
|
||
// ]);
|
||
|
||
// Favorite 플래그 업데이트 (SingleOption과 동일)
|
||
useEffect(() => {
|
||
setFavoriteFlag(productInfo?.favorYn ? productInfo?.favorYn : 'N');
|
||
}, [productInfo]);
|
||
|
||
// Favorite 플래그 변경 콜백 (SingleOption과 동일)
|
||
const onFavoriteFlagChanged = useCallback((ev) => {
|
||
setFavoriteFlag(ev);
|
||
}, []);
|
||
|
||
// 컴포넌트 마운트 시 상품 정보 출력
|
||
useEffect(() => {
|
||
console.log('[BuyOption]', '상품 정보:', JSON.stringify(productInfo));
|
||
}, []); // 컴포넌트 마운트 시 한 번만 출력
|
||
|
||
// hasOnClose 로직 (SingleOption과 동일)
|
||
const hasOnClose = useMemo(() => {
|
||
if (productOptionInfos && productOptionInfos.length > 0) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}, [productOptionInfos, isOptionValue, isOptionSelect, selectedOptions]);
|
||
|
||
// 로그인 팝업 텍스트 로직 (SingleOption과 동일)
|
||
const loginPopupText = useMemo(() => {
|
||
if (!userNumber) {
|
||
return $L('Would you like to sign in?');
|
||
}
|
||
if (!hasOnClose) {
|
||
return $L('Please select Option');
|
||
}
|
||
return $L('Would you like to sign in?');
|
||
}, [hasOnClose, userNumber]);
|
||
|
||
// BuyOption 내에서 arrow up으로 빠져나가지 않도록 처리
|
||
const handleArrowUpWithinBuyOption = useCallback((e) => {
|
||
// e.stopPropagation();
|
||
// arrow up 이벤트를 막음 (컨테이너의 leaveFor: {}와 함께 작동)
|
||
}, []);
|
||
|
||
// 팝업 닫기 핸들러 (SingleOption과 동일)
|
||
const onClose = useCallback(
|
||
(spotlightId) => {
|
||
dispatch(setHidePopup());
|
||
|
||
let currentSpot;
|
||
if (typeof spotlightId === 'string') {
|
||
currentSpot = spotlightId;
|
||
} else {
|
||
currentSpot = 'buy-option-buy-now-button';
|
||
}
|
||
|
||
if (currentSpot) {
|
||
setTimeout(() => {
|
||
Spotlight.focus(currentSpot);
|
||
});
|
||
}
|
||
},
|
||
[dispatch]
|
||
);
|
||
|
||
// 로그인 팝업 열기 핸들러 (SingleOption과 동일)
|
||
const handleLoginPopUpOpen = useCallback(() => {
|
||
if (!userNumber) {
|
||
if (webOSVersion >= '6.0') {
|
||
setTimeout(() => {
|
||
Spotlight.focus('buy-option-buy-now-button');
|
||
});
|
||
|
||
dispatch(setHidePopup());
|
||
// dispatch(launchMembershipApp()); // 필요시 추가
|
||
} else {
|
||
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (hasOnClose) {
|
||
dispatch(setHidePopup());
|
||
|
||
let spotlightId = 'buy-option-first-dropdown';
|
||
|
||
//옵션이 하나만 있는경우 isOptionValue === false
|
||
if (!isOptionValue) {
|
||
spotlightId = 'buy-option-second-dropdown';
|
||
}
|
||
setTimeout(() => {
|
||
Spotlight.focus(spotlightId);
|
||
}, 100);
|
||
return;
|
||
}
|
||
}, [dispatch, hasOnClose, isOptionValue, webOSVersion, userNumber]);
|
||
|
||
return (
|
||
<Container className={styles.buy_option}>
|
||
<div className={styles.buy_option__left_section}>
|
||
{/* 동적 옵션 렌더링 */}
|
||
{productOptionInfos &&
|
||
productOptionInfos?.length > 0 &&
|
||
productInfo?.optProdYn === 'Y' && (
|
||
<>
|
||
{/* 첫번째 옵션 (여러 옵션이 있을 때만) */}
|
||
{productOptionInfos?.length > 1 && (
|
||
<div className={styles.buy_option__option_row}>
|
||
<div className={styles.buy_option__option_label}>
|
||
<div className={styles.buy_option__label_text}>
|
||
OPTION 1
|
||
</div>
|
||
</div>
|
||
<div className={styles.buy_option__option_control}>
|
||
<CustomDropDown
|
||
options={[
|
||
'Select Option',
|
||
...(productOptionInfos?.map((option) => option.optNm) ||
|
||
[]),
|
||
]}
|
||
selectedIndex={selectedBtnOptIdx}
|
||
onSelect={handleFirstOptionSelect}
|
||
spotlightId="buy-option-first-dropdown"
|
||
onSpotlightUp={handleArrowUpWithinBuyOption}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 두번째 옵션 (옵션 상세값들) */}
|
||
{productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
|
||
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl?.length >
|
||
1 && (
|
||
<div className={styles.buy_option__option_row}>
|
||
<div className={styles.buy_option__option_label}>
|
||
<div className={styles.buy_option__label_text}>
|
||
{productOptionInfos.length === 1
|
||
? 'OPTION'
|
||
: 'OPTION 2'}
|
||
</div>
|
||
</div>
|
||
<div className={styles.buy_option__option_control}>
|
||
<CustomDropDown
|
||
options={[
|
||
'Select Option',
|
||
...(productOptionInfos[
|
||
productOptionInfos.length > 1
|
||
? selectedBtnOptIdx - 1
|
||
: selectedBtnOptIdx
|
||
]?.prdtOptDtl.map((detail) => ({
|
||
label: detail.prodOptCval,
|
||
disabled: detail.optStkQty <= 0,
|
||
imageUrl: detail.optImgUrl || null,
|
||
price: detail.priceInfo.split('|')[1],
|
||
})) || []),
|
||
]}
|
||
selectedIndex={selectedOptionItemIndex}
|
||
onSelect={handleSecondOptionSelect}
|
||
spotlightId="buy-option-second-dropdown"
|
||
onSpotlightUp={handleArrowUpWithinBuyOption}
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
|
||
{/* 수량 선택 */}
|
||
<div className={styles.buy_option__option_row}>
|
||
<div className={styles.buy_option__option_label}>
|
||
<div className={styles.buy_option__label_text}>QUANTITY</div>
|
||
</div>
|
||
<div className={styles.buy_option__option_control}>
|
||
{/* <CustomDropDown
|
||
options={["1", "2", "3", "4", "5"]}
|
||
selectedIndex={quantity - 1}
|
||
onSelect={handleQuantitySelect}
|
||
spotlightId="buy-option-quantity-dropdown"
|
||
/> */}
|
||
<ProductQuantity
|
||
onSpotlightUp={handleArrowUpWithinBuyOption}
|
||
setQuantity={setQuantity}
|
||
quantity={quantity}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className={styles.buy_option__right_section}>
|
||
<BuyOptionPriceBlock
|
||
className={styles.buy_option__price_block}
|
||
productInfo={productInfo || productData}
|
||
selectedOptions={selectedOptions}
|
||
patncNm={patncNm || productData?.patncNm || productInfo?.patncNm}
|
||
/>
|
||
<div className={styles.buy_option__button_section}>
|
||
<TButton
|
||
className={styles.buy_option__buy_button}
|
||
spotlightId="buy-option-buy-now-button"
|
||
size="detailButton"
|
||
onClick={handleBuyNowClick}
|
||
onSpotlightUp={handleArrowUpWithinBuyOption}
|
||
>
|
||
<span className={styles.buy_option__button_text}>BUY NOW</span>
|
||
</TButton>
|
||
<TButton
|
||
className={styles.buy_option__cart_button}
|
||
spotlightId="buy-option-add-to-cart-button"
|
||
size="detailButton"
|
||
onClick={handleAddToCartClick}
|
||
onSpotlightUp={handleArrowUpWithinBuyOption}
|
||
>
|
||
<span className={styles.buy_option__button_text}>ADD TO CART</span>
|
||
</TButton>
|
||
</div>
|
||
</div>
|
||
|
||
{/* LOGIN POPUP */}
|
||
{activePopup === Config.ACTIVE_POPUP.loginPopup && (
|
||
<TPopUp
|
||
kind="textPopup"
|
||
hasText
|
||
open={popupVisible}
|
||
text={loginPopupText}
|
||
hasButton
|
||
hasOnClose={hasOnClose}
|
||
button1Text={$L('OK')}
|
||
button2Text={$L('CANCEL')}
|
||
onClick={handleLoginPopUpOpen}
|
||
onClose={onClose}
|
||
/>
|
||
)}
|
||
</Container>
|
||
);
|
||
};
|
||
|
||
export default BuyOption;
|