[구매부분수정]

- 상품상세 비디오 노출 제거.
- 구매 옵션 미선택시 무반응처리
- 구매옵션넘겼을때 상품정보없을시에 목업상품으로 연결하여 구매쪽으로 넘어가도록 변경.
This commit is contained in:
junghoon86.park
2025-10-28 12:55:37 +09:00
parent 4056d1e2a1
commit 0eb21efe1c
2 changed files with 165 additions and 63 deletions

View File

@@ -1,19 +1,31 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import classNames from 'classnames';
// import { throttle } from 'lodash';
import { PropTypes } from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import {
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from '@enact/spotlight';
/* eslint-disable react/jsx-no-bind */
// src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
// import Spottable from '@enact/spotlight/Spottable';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
// import Spottable from '@enact/spotlight/Spottable';
//image
import arrowDown from '../../../../assets/images/icons/ic_arrow_down_3x_new.png';
import indicatorDefaultImage from '../../../../assets/images/img-thumb-empty-144@3x.png';
import arrowDown
from '../../../../assets/images/icons/ic_arrow_down_3x_new.png';
import indicatorDefaultImage
from '../../../../assets/images/img-thumb-empty-144@3x.png';
// import { pushPanel } from '../../../actions/panelActions';
import { minimizeModalMedia } from '../../../actions/mediaActions';
import { pauseFullscreenVideo } from '../../../actions/playActions';
@@ -49,13 +61,16 @@ import ProductTag from '../components/ProductTag';
import StarRating from '../components/StarRating';
// ProductContentSection imports
import TScrollerDetail from '../components/TScroller/TScrollerDetail';
import ProductDescription from '../ProductContentSection/ProductDescription/ProductDescription';
import ProductDetail from '../ProductContentSection/ProductDetail/ProductDetail.new';
import ProductVideo from '../ProductContentSection/ProductVideo/ProductVideo';
import ProductDescription
from '../ProductContentSection/ProductDescription/ProductDescription';
import ProductDetail
from '../ProductContentSection/ProductDetail/ProductDetail.new';
import { ProductVideoV2 } from '../ProductContentSection/ProductVideo';
import ProductVideo from '../ProductContentSection/ProductVideo/ProductVideo';
import UserReviews from '../ProductContentSection/UserReviews/UserReviews';
// import ViewAllReviewsButton from '../ProductContentSection/UserReviews/ViewAllReviewsButton';
import YouMayAlsoLike from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
import YouMayAlsoLike
from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
import QRCode from '../ProductInfoSection/QRCode/QRCode';
import ProductOverview from '../ProductOverview/ProductOverview';
// CSS imports
@@ -99,7 +114,10 @@ const HorizontalContainer = SpotlightContainerDecorator(
const getProductData = curry((productType, themeProductInfo, productInfo) =>
pipe(
when(
() => isVal(productType) && productType === 'theme' && isVal(themeProductInfo),
() =>
isVal(productType) &&
productType === 'theme' &&
isVal(themeProductInfo),
() => themeProductInfo
),
defaultTo(productInfo),
@@ -141,14 +159,16 @@ export default function ProductAllSection({
const dispatch = useDispatch();
// Redux 상태
const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion);
const webOSVersion = useSelector(
(state) => state.common.appStatus.webOSVersion
);
const groupInfos = useSelector((state) => state.product.groupInfo);
// YouMayLike 데이터는 API 응답 시간이 걸리므로 직접 구독
const youmaylikeData = useSelector((state) => state.main.youmaylikeData);
// ProductVideo 버전 관리 (1: 기존 modal 방식, 2: 내장 방식 , 3: 비디오 생략)
const [productVideoVersion, setProductVideoVersion] = useState(2);
const [productVideoVersion, setProductVideoVersion] = useState(3);
// const [currentHeight, setCurrentHeight] = useState(0);
//하단부분까지 갔을때 체크용
@@ -229,7 +249,10 @@ export default function ProductAllSection({
// 여행/테마 상품 - DetailPanel.backup.jsx와 동일한 로직
const isTravelProductVisible = useMemo(() => {
return panelInfo?.curationId && (panelInfo?.type === 'theme' || panelInfo?.type === 'hotel');
return (
panelInfo?.curationId &&
(panelInfo?.type === 'theme' || panelInfo?.type === 'hotel')
);
}, [panelInfo]);
// useReviews Hook 사용 - 모든 리뷰 관련 로직을 담당
@@ -284,7 +307,10 @@ export default function ProductAllSection({
// TODO: 장바구니 추가 로직 구현
}, []);
const { revwGrd, orderPhnNo } = useMemo(() => extractProductMeta(productInfo), [productInfo]);
const { revwGrd, orderPhnNo } = useMemo(
() => extractProductMeta(productInfo),
[productInfo]
);
const [favoriteOverride, setFavoriteOverride] = useState(null);
const favoriteFlag = useMemo(
@@ -293,7 +319,8 @@ export default function ProductAllSection({
);
const [mobileSendPopupOpen, setMobileSendPopupOpen] = useState(false);
const [isShowUserReviewsFocused, setIsShowUserReviewsFocused] = useState(false);
const [isShowUserReviewsFocused, setIsShowUserReviewsFocused] =
useState(false);
const reviewTotalCount = stats.totalReviews;
const { getScrollTo, scrollTop } = useScrollTo();
@@ -393,7 +420,11 @@ export default function ProductAllSection({
}
// 이미지들 추가
if (productData && productData.imgUrls600 && productData.imgUrls600.length > 0) {
if (
productData &&
productData.imgUrls600 &&
productData.imgUrls600.length > 0
) {
productData.imgUrls600.forEach((image, imgIndex) => {
items.push({
type: 'image',
@@ -413,7 +444,9 @@ export default function ProductAllSection({
// renderItems에 Video가 존재하는지 확인하는 boolean 상태
const hasVideo = useMemo(() => {
return (
renderItems && renderItems.length > 0 && renderItems.some((item) => item.type === 'video')
renderItems &&
renderItems.length > 0 &&
renderItems.some((item) => item.type === 'video')
);
}, [renderItems]);
@@ -456,7 +489,10 @@ export default function ProductAllSection({
pipe(
() => setOpenThemeItemOverlay(true),
tap(() => {
const timerId = setTimeout(() => Spotlight.focus('theme-close-button'), 0);
const timerId = setTimeout(
() => Spotlight.focus('theme-close-button'),
0
);
timersRef.current.push(timerId);
})
),
@@ -484,7 +520,8 @@ export default function ProductAllSection({
});
// documentHeight를 활용하여 반복 계산 제거
const totalHeight = documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
const totalHeight =
documentHeight + (youMayAlsoLikelRef.current?.scrollHeight || 0);
const isAtBottom = scrollPositionRef.current + 1100 >= totalHeight;
if (isAtBottom) {
@@ -565,7 +602,6 @@ export default function ProductAllSection({
// };
// }, []);
// 컴포넌트 unmount 시 모든 timer cleanup
useEffect(() => {
return () => {
@@ -623,7 +659,11 @@ export default function ProductAllSection({
>
<div className={css.qrWrapper}>
{isShowQRCode ? (
<QRCode productInfo={productData} productType={productType} kind={'detail'} />
<QRCode
productInfo={productData}
productType={productType}
kind={'detail'}
/>
) : (
<div className={css.qrRollingWrap}>
<div className={css.innerText}>
@@ -672,7 +712,9 @@ export default function ProductAllSection({
onClick={handleShopByMobileOpen}
onSpotlightUp={handleSpotlightUpToBackButton}
>
<div className={css.shopByMobileText}>{$L('SHOP BY MOBILE')}</div>
<div className={css.shopByMobileText}>
{$L('SHOP BY MOBILE')}
</div>
</TButton>
{panelInfo && (
<div className={css.favoriteBtnWrapper}>
@@ -691,7 +733,9 @@ export default function ProductAllSection({
<div className={css.callToOrderSection}>
{orderPhnNo && (
<>
<div className={css.callToOrderText}>{$L('Call to Order')}</div>
<div className={css.callToOrderText}>
{$L('Call to Order')}
</div>
<div className={css.phoneSection}>
<div className={css.phoneIconContainer}>
<div className={css.phoneIcon} />
@@ -750,15 +794,17 @@ export default function ProductAllSection({
})()} */}
</Container>
{panelInfo && panelInfo.type === 'theme' && !openThemeItemOverlay && (
<TButton
className={css.themeButton}
onClick={handleThemeItemButtonClick}
spotlightId="theme-open-button"
>
{$L('THEME ITEM')}
</TButton>
)}
{panelInfo &&
panelInfo.type === 'theme' &&
!openThemeItemOverlay && (
<TButton
className={css.themeButton}
onClick={handleThemeItemButtonClick}
spotlightId="theme-open-button"
>
{$L('THEME ITEM')}
</TButton>
)}
<DetailMobileSendPopUp
ismobileSendPopupOpen={mobileSendPopupOpen}
@@ -790,7 +836,10 @@ export default function ProductAllSection({
onScroll={handleScroll}
>
<div className={css.productDetail}>
<div id="scroll-marker-product-details" className={css.scrollMarker}></div>
<div
id="scroll-marker-product-details"
className={css.scrollMarker}
></div>
{/* <LayoutSample onClick={handleLayoutSampleClick} /> */}
<div
id="product-details-section"
@@ -799,31 +848,36 @@ export default function ProductAllSection({
onBlur={handleButtonBlur}
>
{/* 비디오가 있으면 먼저 렌더링 (productVideoVersion이 3이 아닐 때만) */}
{hasVideo && renderItems[0].type === 'video' && productVideoVersion !== 3 && (
<>
{productVideoVersion === 1 ? (
<ProductVideo
key="product-video-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
autoPlay={true}
continuousPlay={true}
onScrollToImages={handleScrollToImagesV1}
/>
) : (
<ProductVideoV2
key="product-video-v2-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
autoPlay={true}
onScrollToImages={handleScrollToImagesV2}
/>
)}
<div id="scroll-marker-after-video" className={css.scrollMarker}></div>
</>
)}
{hasVideo &&
renderItems[0].type === 'video' &&
productVideoVersion !== 3 && (
<>
{productVideoVersion === 1 ? (
<ProductVideo
key="product-video-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
autoPlay={true}
continuousPlay={true}
onScrollToImages={handleScrollToImagesV1}
/>
) : (
<ProductVideoV2
key="product-video-v2-0"
productInfo={productData}
videoUrl={renderItems[0].url}
thumbnailUrl={renderItems[0].thumbnail}
autoPlay={true}
onScrollToImages={handleScrollToImagesV2}
/>
)}
<div
id="scroll-marker-after-video"
className={css.scrollMarker}
></div>
</>
)}
{/* 이미지들만 렌더링 (비디오 제외) */}
{renderItems.length > 0
@@ -851,7 +905,10 @@ export default function ProductAllSection({
<ProductDescription productInfo={productData} />
</div>
{/* 리뷰가 있을 때만 UserReviews 섹션 표시 */}
<div id="scroll-marker-user-reviews" className={css.scrollMarker}></div>
<div
id="scroll-marker-user-reviews"
className={css.scrollMarker}
></div>
{hasReviews && (
<div
id="user-reviews-section"
@@ -879,7 +936,10 @@ export default function ProductAllSection({
)}
</div>
<div ref={youMayAlsoLikelRef}>
<div id="scroll-marker-you-may-also-like" className={css.scrollMarker}></div>
<div
id="scroll-marker-you-may-also-like"
className={css.scrollMarker}
></div>
{hasYouMayAlsoLike && (
<div id="you-may-also-like-section">
{/* {(() => {

View File

@@ -27,7 +27,12 @@ import {
sendLogPaymentEntry,
sendLogTotalRecommend,
} from '../../../actions/logActions';
import { pushPanel } from '../../../actions/panelActions';
import { finishMediaPreview } from '../../../actions/mediaActions';
import {
popPanel,
pushPanel,
} from '../../../actions/panelActions';
import { finishVideoPreview } from '../../../actions/playActions';
import {
getProductOption,
getProductOptionId,
@@ -261,7 +266,9 @@ const BuyOption = ({
} else {
const { mbrId, prdtId, prodSno } = response.data.productList[0];
const cartTpSno = `${mbrId}_${prdtId}_${prodSno}`;
dispatch(popPanel(Config.panel_names.DETAIL_PANEL));
dispatch(finishVideoPreview());
dispatch(finishMediaPreview());
dispatch(
pushPanel({
name: Config.panel_names.CHECKOUT_PANEL,
@@ -274,6 +281,26 @@ const BuyOption = ({
} else if (response.retCode === 1001) {
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
dispatch(changeAppStatus({ isLoading: false }));
} else if (response.retCode === 1016) {
//현재 1016에 대한 소스 부분 체크가 원활하지 않아 우선 넘어가도록 기존 상품 연결. 추후 에러 코드에 대한 처리 필요
dispatch(
getMyInfoCheckoutInfo(
{
mbrNo: userNumber,
dirPurcSelYn: 'Y',
cartList: [
{
patnrId: '1',
prdtId: 'T141360',
prodOptCdCval: 'T141360000000',
prodQty: '1',
prodOptTpCdCval: 'ORD04001',
},
],
},
checkOutValidate
)
);
} else {
dispatch(
showError(
@@ -315,6 +342,21 @@ const BuyOption = ({
// return dispatch(setShowPopup(Config.ACTIVE_POPUP.loginPopup));
// }
//옵션 선택 안하면 구매 안돼도록.
if (productOptionInfos.length > 1) {
if (selectedBtnOptIdx === 0) {
return;
}
}
if (
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl &&
productOptionInfos[selectedBtnOptIdx]?.prdtOptDtl.length > 1
) {
if (selectedOptionItemIndex === 0) {
return;
}
}
if (userNumber && selectedPatnrId && selectedPrdtId && quantity) {
const { prodOptCval, priceInfo } = selectedOptions || {};
const { patncNm, brndNm, catNm, prdtNm, prdtId } = productInfo;