[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 YouMayAlsoLike
|
||||
from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
|
||||
import SeeMoreProducts from '../ProductContentSection/SeeMoreProducts/SeeMoreProducts';
|
||||
import QRCode from '../ProductInfoSection/QRCode/QRCode';
|
||||
import ProductOverview from '../ProductOverview/ProductOverview';
|
||||
// CSS imports
|
||||
@@ -1005,6 +1006,7 @@ export default function ProductAllSection({
|
||||
const [isShowUserReviewsFocused, setIsShowUserReviewsFocused] = useState(false);
|
||||
// 🆕 [251210] patnrId=21 SEE MORE PRODUCTS 버튼 표시 여부
|
||||
const [hasSeeMoreProducts, setHasSeeMoreProducts] = useState(false);
|
||||
const [seeMoreProductsData, setSeeMoreProductsData] = useState([]);
|
||||
|
||||
const reviewTotalCount = stats.totalReviews;
|
||||
|
||||
@@ -1346,8 +1348,25 @@ export default function ProductAllSection({
|
||||
console.log(` - Price: ${product.priceInfo}`);
|
||||
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 {
|
||||
console.log('[SEE MORE PRODUCTS] ❌ No other products in group - hiding button');
|
||||
setSeeMoreProductsData([]);
|
||||
}
|
||||
} else {
|
||||
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 id="scroll-marker-see-more-products" className={css.scrollMarker}></div>
|
||||
<div id="see-more-products-section" data-spotlight-id="see-more-products-area">
|
||||
{/* TODO: 나중에 그룹 상품 표시 컴포넌트 추가 */}
|
||||
<div style={{ padding: '20px', textAlign: 'center', color: '#666' }}>
|
||||
<h3>SEE MORE PRODUCTS</h3>
|
||||
<p>그룹 상품이 여기에 표시될 예정입니다</p>
|
||||
</div>
|
||||
<SeeMoreProducts
|
||||
groupProducts={seeMoreProductsData}
|
||||
panelInfo={panelInfo}
|
||||
onFocus={() => handleButtonFocus('seemoreproducts')}
|
||||
onBlur={handleButtonBlur}
|
||||
/>
|
||||
</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