[251211] feat: FeaturedBrandsPanel , See More Products - 3
🕐 커밋 시간: 2025. 12. 11. 12:29:51 📊 변경 통계: • 총 파일: 3개 • 추가: +25줄 • 삭제: -5줄 📁 추가된 파일: + com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.jsx + com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.module.less 📝 수정된 파일: ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx 🔧 주요 변경 내용: • 소규모 기능 개선
This commit is contained in:
@@ -116,6 +116,7 @@ import UserReviews from '../ProductContentSection/UserReviews/UserReviews';
|
|||||||
// import ViewAllReviewsButton from '../ProductContentSection/UserReviews/ViewAllReviewsButton';
|
// import ViewAllReviewsButton from '../ProductContentSection/UserReviews/ViewAllReviewsButton';
|
||||||
import YouMayAlsoLike
|
import YouMayAlsoLike
|
||||||
from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
|
from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
|
||||||
|
import SeeMoreProducts from '../ProductContentSection/SeeMoreProducts/SeeMoreProducts';
|
||||||
import QRCode from '../ProductInfoSection/QRCode/QRCode';
|
import QRCode from '../ProductInfoSection/QRCode/QRCode';
|
||||||
import ProductOverview from '../ProductOverview/ProductOverview';
|
import ProductOverview from '../ProductOverview/ProductOverview';
|
||||||
// CSS imports
|
// CSS imports
|
||||||
@@ -1005,6 +1006,7 @@ export default function ProductAllSection({
|
|||||||
const [isShowUserReviewsFocused, setIsShowUserReviewsFocused] = useState(false);
|
const [isShowUserReviewsFocused, setIsShowUserReviewsFocused] = useState(false);
|
||||||
// 🆕 [251210] patnrId=21 SEE MORE PRODUCTS 버튼 표시 여부
|
// 🆕 [251210] patnrId=21 SEE MORE PRODUCTS 버튼 표시 여부
|
||||||
const [hasSeeMoreProducts, setHasSeeMoreProducts] = useState(false);
|
const [hasSeeMoreProducts, setHasSeeMoreProducts] = useState(false);
|
||||||
|
const [seeMoreProductsData, setSeeMoreProductsData] = useState([]);
|
||||||
|
|
||||||
const reviewTotalCount = stats.totalReviews;
|
const reviewTotalCount = stats.totalReviews;
|
||||||
|
|
||||||
@@ -1346,8 +1348,25 @@ export default function ProductAllSection({
|
|||||||
console.log(` - Price: ${product.priceInfo}`);
|
console.log(` - Price: ${product.priceInfo}`);
|
||||||
console.log(` - Brand: ${product.brndNm || 'N/A'}`);
|
console.log(` - Brand: ${product.brndNm || 'N/A'}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 🆕 SeeMoreProducts 컴포넌트를 위한 데이터 변환
|
||||||
|
const formattedProducts = otherProducts.map(product => ({
|
||||||
|
prdtId: product.prdtId,
|
||||||
|
prdtNm: product.prdtNm,
|
||||||
|
priceInfo: product.priceInfo,
|
||||||
|
patncNm: foundConts.patncNm,
|
||||||
|
patnrId: foundConts.patnrId,
|
||||||
|
brndNm: product.brndNm,
|
||||||
|
imgUrl: product.prdtImgUrl,
|
||||||
|
lgCatCd: product.lgCatCd,
|
||||||
|
offerInfo: product.offerInfo,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// YouMayAlsoLike 데이터 형식으로 맞추기
|
||||||
|
setSeeMoreProductsData(formattedProducts);
|
||||||
} else {
|
} else {
|
||||||
console.log('[SEE MORE PRODUCTS] ❌ No other products in group - hiding button');
|
console.log('[SEE MORE PRODUCTS] ❌ No other products in group - hiding button');
|
||||||
|
setSeeMoreProductsData([]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('[SEE MORE PRODUCTS] ❌ No group found for current product - hiding button');
|
console.log('[SEE MORE PRODUCTS] ❌ No group found for current product - hiding button');
|
||||||
@@ -2060,11 +2079,12 @@ export default function ProductAllSection({
|
|||||||
<div ref={seeMoreProductsRef}>
|
<div ref={seeMoreProductsRef}>
|
||||||
<div id="scroll-marker-see-more-products" className={css.scrollMarker}></div>
|
<div id="scroll-marker-see-more-products" className={css.scrollMarker}></div>
|
||||||
<div id="see-more-products-section" data-spotlight-id="see-more-products-area">
|
<div id="see-more-products-section" data-spotlight-id="see-more-products-area">
|
||||||
{/* TODO: 나중에 그룹 상품 표시 컴포넌트 추가 */}
|
<SeeMoreProducts
|
||||||
<div style={{ padding: '20px', textAlign: 'center', color: '#666' }}>
|
groupProducts={seeMoreProductsData}
|
||||||
<h3>SEE MORE PRODUCTS</h3>
|
panelInfo={panelInfo}
|
||||||
<p>그룹 상품이 여기에 표시될 예정입니다</p>
|
onFocus={() => handleButtonFocus('seemoreproducts')}
|
||||||
</div>
|
onBlur={handleButtonBlur}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { Job } from '@enact/core/util';
|
||||||
|
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
|
|
||||||
|
import { clearThemeDetail } from '../../../../actions/homeActions';
|
||||||
|
import { finishModalMediaForce } from '../../../../actions/mediaActions';
|
||||||
|
import { popPanel, pushPanel, updatePanel } from '../../../../actions/panelActions';
|
||||||
|
import { finishVideoPreview } from '../../../../actions/playActions';
|
||||||
|
import THeader from '../../../../components/THeader/THeader';
|
||||||
|
import TItemCardNew from '../../../../components/TItemCard/TItemCard.new';
|
||||||
|
import TVerticalPagenator from '../../../../components/TVerticalPagenator/TVerticalPagenator';
|
||||||
|
import useScrollTo from '../../../../hooks/useScrollTo';
|
||||||
|
import { LOG_CONTEXT_NAME, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config';
|
||||||
|
import { $L } from '../../../../utils/helperMethods';
|
||||||
|
import css from './SeeMoreProducts.module.less';
|
||||||
|
|
||||||
|
const SpottableComponent = Spottable('div');
|
||||||
|
|
||||||
|
const Container = SpotlightContainerDecorator(
|
||||||
|
{
|
||||||
|
enterTo: 'last-focused',
|
||||||
|
leaveFor: {
|
||||||
|
left: 'spotlight-product-info-section-container',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'div'
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function SeeMoreProducts({
|
||||||
|
groupProducts,
|
||||||
|
panelInfo,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
}) {
|
||||||
|
const { getScrollTo, scrollLeft } = useScrollTo();
|
||||||
|
const [newGroupProductData, setNewGroupProductData] = useState([]);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const focusedContainerIdRef = useRef(null);
|
||||||
|
|
||||||
|
const panels = useSelector((state) => state.panels.panels);
|
||||||
|
const themeProductInfos = useSelector((state) => state.home.themeCurationDetailInfoData);
|
||||||
|
|
||||||
|
const launchedFromPlayer = useMemo(() => {
|
||||||
|
const detailPanelIndex = panels.findIndex(({ name }) => name === 'detailpanel');
|
||||||
|
const playerPanelIndex = panels.findIndex(({ name }) => name === 'playerpanel');
|
||||||
|
|
||||||
|
return detailPanelIndex - 1 === playerPanelIndex;
|
||||||
|
}, [panels]);
|
||||||
|
|
||||||
|
const onFocusedContainerId = useCallback((containerId) => {
|
||||||
|
focusedContainerIdRef.current = containerId;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const _onFocus = useCallback(() => {
|
||||||
|
if (onFocus) {
|
||||||
|
onFocus();
|
||||||
|
}
|
||||||
|
}, [onFocus]);
|
||||||
|
|
||||||
|
const _onBlur = useCallback(() => {
|
||||||
|
if (onBlur) {
|
||||||
|
onBlur();
|
||||||
|
}
|
||||||
|
}, [onBlur]);
|
||||||
|
|
||||||
|
// 그룹 상품 데이터 처리 (YOU MAY ALSO LIKE와 동일 로직)
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('[SeeMoreProducts] 그룹 상품 데이터 처리:', {
|
||||||
|
originalData: groupProducts,
|
||||||
|
originalLength: groupProducts?.length || 0,
|
||||||
|
hasData: !!(groupProducts && groupProducts.length > 0)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (groupProducts && groupProducts.length > 0) {
|
||||||
|
// 최대 9개로 제한 (YOU MAY ALSO LIKE와 동일)
|
||||||
|
const processedData = groupProducts.length > 9
|
||||||
|
? groupProducts.slice(0, groupProducts.length - 1)
|
||||||
|
: groupProducts;
|
||||||
|
|
||||||
|
console.log('[SeeMoreProducts] 처리된 데이터 설정:', {
|
||||||
|
processedLength: processedData.length,
|
||||||
|
processedData
|
||||||
|
});
|
||||||
|
setNewGroupProductData(processedData);
|
||||||
|
} else {
|
||||||
|
console.log('[SeeMoreProducts] 데이터 없음 - 빈 배열 설정');
|
||||||
|
setNewGroupProductData([]);
|
||||||
|
}
|
||||||
|
}, [groupProducts]);
|
||||||
|
|
||||||
|
const cursorOpen = useRef(new Job((func) => func(), 1000));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{newGroupProductData && newGroupProductData.length > 0 && (
|
||||||
|
<TVerticalPagenator
|
||||||
|
spotlightId={'detail_seeMoreProducts_area'}
|
||||||
|
data-wheel-point={true}
|
||||||
|
className={css.tVerticalPagenator}
|
||||||
|
defaultContainerId={panelInfo?.focusedContainerId}
|
||||||
|
onFocusedContainerId={onFocusedContainerId}
|
||||||
|
topMargin={36}
|
||||||
|
>
|
||||||
|
<Container className={css.container} onFocus={_onFocus} onBlur={_onBlur}>
|
||||||
|
<THeader title={$L('SEE MORE PRODUCTS')} className={css.tHeader} />
|
||||||
|
<div className={css.renderCardContainer}>
|
||||||
|
{newGroupProductData?.map((product, index) => {
|
||||||
|
const {
|
||||||
|
imgUrl: prdtImgUrl, // 이미지 URL 변경
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
prdtNm,
|
||||||
|
priceInfo,
|
||||||
|
offerInfo,
|
||||||
|
patncNm,
|
||||||
|
brndNm,
|
||||||
|
lgCatCd,
|
||||||
|
euEnrgLblInfos,
|
||||||
|
} = product;
|
||||||
|
|
||||||
|
const handleItemClick = () => {
|
||||||
|
console.log('[SeeMoreProducts] 상품 클릭:', product);
|
||||||
|
|
||||||
|
// Promise 체이닝으로 순서 보장 (YOU MAY ALSO LIKE와 동일)
|
||||||
|
Promise.resolve()
|
||||||
|
.then(() => {
|
||||||
|
// 1. 기존 비디오 강제 종료
|
||||||
|
dispatch(finishVideoPreview());
|
||||||
|
dispatch(finishModalMediaForce());
|
||||||
|
|
||||||
|
if (themeProductInfos && themeProductInfos.length > 0) {
|
||||||
|
dispatch(clearThemeDetail());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// 2. 비디오 종료 후 새로운 상품으로 업데이트
|
||||||
|
dispatch(
|
||||||
|
updatePanel({
|
||||||
|
name: panel_names.DETAIL_PANEL,
|
||||||
|
panelInfo: {
|
||||||
|
showNm: panelInfo?.showNm,
|
||||||
|
showId: panelInfo?.showId,
|
||||||
|
liveFlag: panelInfo?.liveFlag,
|
||||||
|
thumbnailUrl: panelInfo?.thumbnailUrl,
|
||||||
|
patnrId,
|
||||||
|
prdtId,
|
||||||
|
launchedFromPlayer: launchedFromPlayer,
|
||||||
|
fromPanel: {
|
||||||
|
fromSeeMoreProducts: true, // 🆕 SeeMoreProducts에서 선택된 상품임을 표시
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
cursorOpen.current.stop();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// prdtId가 없는 경우를 대비한 안정적인 key 생성
|
||||||
|
const itemKey = prdtId ? `${patnrId}-${prdtId}` : `see-more-product-${index}`;
|
||||||
|
|
||||||
|
// 🆕 [251210] TItemCardNew에 spotlightId와 spottable 설정 추가
|
||||||
|
return (
|
||||||
|
<TItemCardNew
|
||||||
|
key={itemKey}
|
||||||
|
className={css.itemCardNew}
|
||||||
|
contextName={LOG_CONTEXT_NAME.YOUMAYLIKE}
|
||||||
|
messageId={LOG_MESSAGE_ID.CONTENTCLICK}
|
||||||
|
productId={prdtId}
|
||||||
|
nowProductId={panelInfo?.prdtId}
|
||||||
|
nowProductTitle={panelInfo?.prdtNm}
|
||||||
|
nowCategory={panelInfo?.catNm}
|
||||||
|
catNm={lgCatCd}
|
||||||
|
patnerName={patncNm}
|
||||||
|
brandName={brndNm}
|
||||||
|
imageAlt={prdtId}
|
||||||
|
imageSource={prdtImgUrl}
|
||||||
|
priceInfo={priceInfo}
|
||||||
|
offerInfo={offerInfo}
|
||||||
|
productName={prdtNm}
|
||||||
|
onClick={handleItemClick}
|
||||||
|
label={index * 1 + 1 + ' of ' + newGroupProductData.length}
|
||||||
|
lastLabel=" go to detail, button"
|
||||||
|
euEnrgLblInfos={euEnrgLblInfos}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</TVerticalPagenator>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
@import "../../../../style/CommonStyle.module.less";
|
||||||
|
@import "../../../../style/utils.module.less";
|
||||||
|
|
||||||
|
// .container {
|
||||||
|
// .size(@w: 874px,@h:500px);
|
||||||
|
|
||||||
|
// .itemWrapper {
|
||||||
|
// .size(@w: 874px,@h:500px);
|
||||||
|
// .item {
|
||||||
|
// .size(@w: 300px,@h:300px);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
.tVerticalPagenator {
|
||||||
|
.size(@w: 1114px, @h: auto); // 마진 포함 전체 크기 (1054px + 60px)
|
||||||
|
max-width: 1114px;
|
||||||
|
// padding-left: 30px; // 좌측 30px 마진
|
||||||
|
// padding-right: 30px; // 우측 30px 마진
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
// .sectionTitle {
|
||||||
|
// .font(@fontFamily: @baseFont, @fontSize: 30px);
|
||||||
|
// min-height: 56px;
|
||||||
|
// font-weight: 700;
|
||||||
|
// color: rgba(234, 234, 234, 1);
|
||||||
|
// // margin: 30px 0 20px 0;
|
||||||
|
// }
|
||||||
|
.tHeader {
|
||||||
|
background: transparent;
|
||||||
|
.size(@w: 1144px, @h: 36px); // 마진 제외 콘텐츠 크기
|
||||||
|
max-width: 1144px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
.size(@w:100%,@h:100%);
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.averageOverallRating {
|
||||||
|
.size(@w: 176px,@h:30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 700;
|
||||||
|
height: 36px;
|
||||||
|
color: rgba(234, 234, 234, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
.flex(@direction:column,@alignCenter:flex-start);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 34px;
|
||||||
|
// > div {
|
||||||
|
// margin: 0 15px 15px 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.renderCardContainer {
|
||||||
|
width: 1144px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
// margin-top: 34px;
|
||||||
|
> div.itemCardNew {
|
||||||
|
/* item card */
|
||||||
|
margin: 0 15px 15px 0;
|
||||||
|
.size(@w:360px,@h:494px);
|
||||||
|
background-color: rgba(51, 51, 51, 0.95);
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
> div:nth-child(1) {
|
||||||
|
/* img wrapper*/
|
||||||
|
.size(@w:323px,@h:323px);
|
||||||
|
|
||||||
|
> img {
|
||||||
|
.size(@w:100%,@h:100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> div:nth-child(2) {
|
||||||
|
margin-top: 15px;
|
||||||
|
/* desc wrapper */
|
||||||
|
> div > div > h3 {
|
||||||
|
/* title */
|
||||||
|
color: rgba(234, 234, 234, 1);
|
||||||
|
.size(@w:100%,@h:64px);
|
||||||
|
line-height: 31px;
|
||||||
|
}
|
||||||
|
> p {
|
||||||
|
/* priceInfo */
|
||||||
|
height: 43px;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// width: 100%;
|
||||||
|
// padding-left: 60px;
|
||||||
|
// overflow: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user