[251105] fix: BuyOption 동영상 정리 및 검색 썸네일 옵션 추가

- BuyOption 체크아웃 진입 시 동영상 및 플레이어 패널 정리 로직 추가
- 선택된 옵션 기본값 설정 로직 개선
- SearchResults 썸네일 최적화 옵션 필드 추가

Generated with coding tools

Co-Authored-By: Dev Team <noreply@dev.team>
This commit is contained in:
2025-11-05 08:51:27 +09:00
parent cedc965319
commit 2d58f7b35f
2 changed files with 298 additions and 334 deletions

View File

@@ -29,12 +29,11 @@ import {
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 { clearAllVideoTimers, finishVideoPreview } from '../../../actions/playActions';
import {
getProductOption,
getProductOptionId,
@@ -163,9 +162,103 @@ const BuyOption = ({
// Mock Mode이고 유효한 옵션 데이터가 있으면 그대로 사용
console.log('[BuyOption] Mock Mode - using existing valid reduxProductOptionInfos');
return reduxProductOptionInfos;
}, [reduxProductOptionInfos, productData, isMockMode]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
}, [reduxProductOptionInfos, productData, productInfo, isMockMode]); // logInfo 생성 (SingleOption과 동일한 로직, productData 우선 사용)
const hasMockOptions =
isMockMode && Array.isArray(productOptionInfos) && productOptionInfos.length > 0;
const cleanupVideoAndPlayerPanels = useCallback(() => {
clearAllVideoTimers();
dispatch((dispatchFn, getState) => {
const getPanels = () => getState().panels?.panels || [];
const popIfTopMatches = (predicate, action) => {
const panels = getPanels();
if (!panels.length) {
return;
}
const topPanel = panels[panels.length - 1];
if (predicate(topPanel)) {
action();
}
};
popIfTopMatches(
(panel) =>
panel?.name === Config.panel_names.MEDIA_PANEL &&
panel.panelInfo?.modal,
() => dispatchFn(finishMediaPreview())
);
popIfTopMatches(
(panel) =>
panel &&
(panel.name === Config.panel_names.PLAYER_PANEL ||
panel.name === Config.panel_names.PLAYER_PANEL_NEW) &&
panel.panelInfo?.modal,
() => dispatchFn(finishVideoPreview())
);
});
}, [dispatch]);
const selectDefaultOptionForMock = useCallback(() => {
if (!isMockMode) {
return selectedOptions;
}
if (selectedOptions) {
return selectedOptions;
}
const firstGroup = Array.isArray(productOptionInfos)
? productOptionInfos[0]
: null;
const firstDetail = firstGroup?.prdtOptDtl?.[0];
if (firstDetail) {
const hasSecondDropdown = (firstGroup?.prdtOptDtl?.length || 0) > 1;
const hasMultipleGroups = (productOptionInfos?.length || 0) > 1;
setSelectedOptions((prev) => prev || firstDetail);
setIsOptionValue(true);
setIsOptionSelect(true);
setSelectedBtnOptIdx((prev) => prev || 0);
setSelectedOptionItemIndex((prev) =>
hasSecondDropdown ? (prev > 0 ? prev : 1) : 0
);
setSelectSecondOptionIndex((prev) =>
hasSecondDropdown ? (prev > 0 ? prev : 1) : 0
);
if (hasMultipleGroups) {
setSelectFirstOptionIndex((prev) => (prev > 0 ? prev : 1));
}
return firstDetail;
}
const fallbackLabel =
productInfo?.optNm || productInfo?.prdtNm || 'Mock Option';
const fallbackPriceInfo =
productInfo?.priceInfo || '999.99|999.99|0|0%|0';
const fallbackOption = {
prodOptCdCval: 'MOCK_DEFAULT_OPT',
prodOptCval: fallbackLabel,
optNm: fallbackLabel,
optPrc: productInfo?.price3 || productInfo?.price2 || '0.00',
priceInfo: fallbackPriceInfo,
};
setSelectedOptions((prev) => prev || fallbackOption);
setIsOptionValue(true);
setIsOptionSelect(true);
setSelectedBtnOptIdx((prev) => prev || 0);
setSelectedOptionItemIndex((prev) => (prev > 0 ? prev : 0));
setSelectSecondOptionIndex((prev) => (prev > 0 ? prev : 0));
return fallbackOption;
}, [
isMockMode,
selectedOptions,
productOptionInfos,
productInfo,
]);
const logInfo = useMemo(() => {
if (productData) {
// productData가 있으면 SingleOption과 동일하게 처리
@@ -375,9 +468,7 @@ const BuyOption = ({
// 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 제거
cleanupVideoAndPlayerPanels();
// 🔴 CRITICAL: 기존 CheckOutPanel 있으면 제거 후 새로 push (API Mode)
dispatch((dispatchFn, getState) => {
@@ -443,9 +534,11 @@ const BuyOption = ({
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;
if (!isMockMode) {
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과 동일)
@@ -459,7 +552,7 @@ const BuyOption = ({
// }
// Mock Mode가 아닐 때만 옵션 선택 검증
if (!BUYNOW_CONFIG.isMockMode()) {
if (!isMockMode) {
//옵션 선택 안하면 구매 안돼도록.
if (
productOptionInfos &&
@@ -513,39 +606,37 @@ const BuyOption = ({
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;
}
}
const effectivePatnrId =
selectedPatnrId || productInfo?.patnrId || 'MOCK_PARTNER';
const effectivePrdtId =
selectedPrdtId || productInfo?.prdtId || 'MOCK_PRODUCT';
const optionForUse = isMockMode
? selectDefaultOptionForMock()
: selectedOptions;
const effectiveQuantity = quantity > 0 ? quantity : 1;
// Mock Mode: 조건 완화 (userNumber 체크 제외)
const shouldProceed = BUYNOW_CONFIG.isMockMode()
? (selectedPatnrId && selectedPrdtId && quantity)
const shouldProceed = isMockMode
? (effectivePatnrId && effectivePrdtId && effectiveQuantity)
: (userNumber && selectedPatnrId && selectedPrdtId && quantity);
console.log('%c[BuyOption] shouldProceed check:', 'background: purple; color: white; padding: 3px;', {
shouldProceed,
isMockMode: BUYNOW_CONFIG.isMockMode(),
isMockMode,
userNumber: !!userNumber,
selectedPatnrId: !!selectedPatnrId,
selectedPrdtId: !!selectedPrdtId,
selectedPatnrId: !!effectivePatnrId,
selectedPrdtId: !!effectivePrdtId,
quantity
});
if (shouldProceed) {
const { prodOptCval, priceInfo } = selectedOptions || {};
const { prodOptCval, priceInfo } = optionForUse || {};
const { patncNm, brndNm, catNm, prdtNm, prdtId } = productInfo;
console.log('[BuyOption] handleClickOrder - productInfo:', productInfo);
console.log('[BuyOption] handleClickOrder - selectedOptions:', selectedOptions);
console.log('[BuyOption] handleClickOrder - selectedOptions:', optionForUse);
console.log('[BuyOption] handleClickOrder - priceInfo:', priceInfo);
console.log('[BuyOption] handleClickOrder - logInfo:', logInfo);
@@ -579,7 +670,7 @@ const BuyOption = ({
console.log('[BuyOption] handleClickOrder - Calculated prices - regularPrice:', regularPrice, 'discountPrice:', discountPrice);
// selectedOptions가 없고 logInfo가 있으면, logInfo의 가격도 고려
if (!selectedOptions && logInfo) {
if (!optionForUse && logInfo) {
console.log('[BuyOption] handleClickOrder - selectedOptions is undefined, checking logInfo prices');
// logInfo의 dcBefPrc와 dcAftrPrc는 "$ 521.66" 형식이므로 숫자만 추출 (소수점 포함)
const dcBefPrcMatch = logInfo.dcBefPrc?.match(/[\d.]+/);
@@ -606,7 +697,7 @@ const BuyOption = ({
price: discountRate ? discountPrice : regularPrice,
discount: discountRate,
brand: brndNm,
productOption: prodOptCval || '',
productOption: prodOptCval || optionForUse?.optNm || optionForUse?.prodOptCval || '',
category: catNm,
contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
messageId: Config.LOG_MESSAGE_ID.BUY_NOW,
@@ -624,8 +715,8 @@ const BuyOption = ({
{
patnrId: selectedPatnrId,
prdtId: selectedPrdtId,
prodOptCdCval: selectedOptions?.prodOptCdCval
? selectedOptions.prodOptCdCval
prodOptCdCval: optionForUse?.prodOptCdCval
? optionForUse.prodOptCdCval
: null,
prodQty: String(quantity),
prodOptTpCdCval: productOptionInfos[0]?.prodOptTpCdCval,
@@ -644,9 +735,7 @@ const BuyOption = ({
// 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 제거
cleanupVideoAndPlayerPanels();
// Mock 모드: 선택 상품의 정보를 panelInfo에 담아서 전달
// CheckOutPanel에서 이 정보로 Mock 상품 데이터 생성
@@ -657,11 +746,11 @@ const BuyOption = ({
'/mock/image.jpg';
const mockProductInfo = {
prdtId: selectedPrdtId,
prdtId: effectivePrdtId,
prdtNm: productInfo?.prdtNm,
patnrId: selectedPatnrId,
patnrId: effectivePatnrId,
patncNm: patncNm,
prodQty: quantity,
prodQty: effectiveQuantity,
origPrice: regularPrice || 99999, // 원래 가격 (숫자)
discountPrice: discountPrice || regularPrice || 99999, // 할인된 가격 (실제 판매 가격, 숫자)
finalPrice: discountPrice || regularPrice || 99999, // 최종 가격 (숫자)
@@ -681,11 +770,11 @@ const BuyOption = ({
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',
productInfo: {
prdtId: productInfo?.prdtId || effectivePrdtId || 'MOCK_PRODUCT',
prdtNm: productInfo?.prdtNm || 'Mock Product',
patnrId: effectivePatnrId || '1',
patncNm: patncNm || 'Mock Partner',
// calculateOrderSummaryFromProductInfo 함수가 필요한 필드들
// productInfo에서 직접 추출한 값을 전달
price2: price3Value, // Items (상품 가격)
@@ -694,7 +783,7 @@ const BuyOption = ({
discount: price5Value,
origPrice: regularPrice,
discountPrice: discountPrice,
prodQty:quantity,
prodQty: quantity,
// 추가 가격 필드들 (fallback용)
price: discountPrice,
originalPrice: regularPrice,
@@ -730,9 +819,9 @@ const BuyOption = ({
const fallbackPanelInfo = {
logInfo: { cartTpSno: `MOCK_FALLBACK_${Date.now()}` },
productInfo: {
prdtId: selectedPrdtId || 'MOCK_PRODUCT',
prdtId: effectivePrdtId || 'MOCK_PRODUCT',
prdtNm: 'Mock Product',
patnrId: selectedPatnrId || '1',
patnrId: effectivePatnrId || '1',
patncNm: 'Mock Partner',
finalPrice: 99999,
origPrice: 99999,
@@ -805,308 +894,181 @@ const BuyOption = ({
logInfo,
selectFirstOptionIndex,
selectSecondOptionIndex,
nowMenu,
isMockMode,
selectDefaultOptionForMock,
cleanupVideoAndPlayerPanels,
]);
// ADD TO CART 버튼 클릭 핸들러
const handleAddToCartClick = useCallback(() => {
console.log('[BuyOption] ADD TO CART clicked');
// 상품 품절 체크
if (productInfo && productInfo?.soldoutFlag === 'Y') {
const isMock = isMockMode;
// 상품 품절 체크 (Mock Mode에서는 우회)
if (productInfo && productInfo?.soldoutFlag === 'Y' && !isMock) {
return;
}
// 로그인 체크
if (!userNumber || userNumber === '') {
// 로그인 체크 (Mock Mode에서는 우회)
if ((!userNumber || userNumber === '') && !isMock) {
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];
const effectivePatnrId =
selectedPatnrId || productInfo?.patnrId || 'MOCK_PARTNER';
const effectivePrdtId =
selectedPrdtId || productInfo?.prdtId || 'MOCK_PRODUCT';
const effectiveQuantity = quantity > 0 ? quantity : 1;
const optionForUse = isMock ? selectDefaultOptionForMock() : selectedOptions;
// 로그 전송
// 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
}
})
);
}
// API Mode 조건 검증
if (
!isMock &&
(!userNumber || !selectedPatnrId || !selectedPrdtId || !quantity)
) {
return;
}
const parseNumericPrice = (value) => {
if (typeof value === 'number') return value;
if (!value) return 0;
const cleaned = String(value).replace(/,/g, '');
const match = cleaned.match(/-?[\d.]+/);
return match ? parseFloat(match[0]) : 0;
};
const priceInfoSource =
optionForUse?.priceInfo ||
productInfo?.priceInfo ||
'0|0|0|0%|0';
const priceParts = priceInfoSource.split('|');
const regularPriceValue = parseNumericPrice(priceParts[0]);
const discountPriceValue =
priceParts.length > 1
? parseNumericPrice(priceParts[1])
: regularPriceValue;
const optionLabel =
optionForUse?.optNm || optionForUse?.prodOptCval || '';
if (!isMock) {
dispatch(
addToCart({
mbrNo: userNumber,
patnrId: selectedPatnrId,
prdtId: selectedPrdtId,
prodQty: String(effectiveQuantity),
prdtOpt: {
prodOptCdCval: optionForUse?.prodOptCdCval || '',
prodOptCval: optionLabel,
prodOptSno: productOptionInfos?.[0]?.prodOptSno || '',
prodOptTpCdCval: productOptionInfos?.[0]?.prodOptTpCdCval || '',
},
})
);
const imgUrl =
productInfo?.imgUrls600?.[0] ||
productInfo?.thumbnailUrl960 ||
productInfo?.imgList?.[0]?.imgUrl ||
productInfo?.thumbnailUrl ||
productInfo?.patncLogPath ||
'/assets/images/img-thumb-empty-144@3x.png';
const productInfoForCart = {
prdtId: effectivePrdtId,
prdtNm: productInfo?.prdtNm || 'Product',
patnrId: effectivePatnrId,
patncNm: productInfo?.patncNm || 'Partner',
patncLogPath: productInfo?.patncLogPath || '',
imgUrl,
thumbnailUrl: productInfo?.thumbnailUrl960 || imgUrl,
thumbnailUrl960: productInfo?.thumbnailUrl960,
imgList: productInfo?.imgList || [{ imgUrl }],
imgUrls: productInfo?.imgUrls || [{ imgUrl }],
imgUrls600: productInfo?.imgUrls600,
price2: regularPriceValue.toFixed(2),
price3: discountPriceValue.toFixed(2),
finalPrice: discountPriceValue,
discountPrice: discountPriceValue,
origPrice: regularPriceValue,
discount: Math.max(0, regularPriceValue - discountPriceValue),
priceInfo: priceInfoSource,
prodQty: effectiveQuantity,
optNm: optionLabel,
shippingCharge: productInfo?.shippingFee || '12.99',
soldoutFlag: productInfo?.soldoutFlag || 'N',
};
const optionInfoForCart = {
name: optionLabel,
price: optionForUse?.optPrc || '0.00',
};
dispatch(
pushPanel({
name: Config.panel_names.CART_PANEL,
panelInfo: {
productInfo: productInfoForCart,
optionInfo: optionInfoForCart,
quantity: effectiveQuantity,
},
})
);
} else {
console.log('[BuyOption] Mock Mode - Adding to cart (Mock)');
const imgUrl =
productInfo?.imgUrls600?.[0] ||
productInfo?.thumbnailUrl960 ||
productInfo?.imgList?.[0]?.imgUrl ||
productInfo?.thumbnailUrl ||
productInfo?.patncLogPath ||
'/assets/images/img-thumb-empty-144@3x.png';
const mockProductInfo = {
prdtId: effectivePrdtId,
prdtNm: productInfo?.prdtNm || 'Mock Product',
patnrId: effectivePatnrId,
patncNm: productInfo?.patncNm || 'Mock Partner',
patncLogPath: productInfo?.patncLogPath || '',
imgUrl,
thumbnailUrl: productInfo?.thumbnailUrl960 || imgUrl,
thumbnailUrl960: productInfo?.thumbnailUrl960,
imgList: productInfo?.imgList || [{ imgUrl }],
imgUrls: productInfo?.imgUrls || [{ imgUrl }],
imgUrls600: productInfo?.imgUrls600,
price2: regularPriceValue.toFixed(2),
price3: discountPriceValue.toFixed(2),
finalPrice: discountPriceValue,
discountPrice: discountPriceValue,
origPrice: regularPriceValue,
discount: Math.max(0, regularPriceValue - discountPriceValue),
priceInfo: priceInfoSource,
prodQty: effectiveQuantity,
optNm: optionLabel || 'Default Option',
shippingCharge: productInfo?.shippingFee || '12.99',
shippingFee: productInfo?.shippingFee || '12.99',
soldoutFlag: productInfo?.soldoutFlag || 'N',
};
const optionInfo = {
name: optionLabel || 'Default Option',
price: optionForUse?.optPrc || discountPriceValue.toFixed(2),
};
dispatch(
pushPanel({
name: Config.panel_names.CART_PANEL,
panelInfo: {
productInfo: mockProductInfo,
optionInfo,
quantity: effectiveQuantity,
},
})
);
}
dispatch(clearAllToasts());
}, [
dispatch,
@@ -1117,7 +1079,8 @@ const BuyOption = ({
productInfo,
productOptionInfos,
quantity,
nowMenu,
isMockMode,
selectDefaultOptionForMock,
]);
const handleFirstOptionSelect = (selected) => {