From a85710421ce23791e22d4f93cd268533f27f0f6f Mon Sep 17 00:00:00 2001 From: optrader Date: Thu, 11 Dec 2025 12:29:51 +0900 Subject: [PATCH] [251211] feat: FeaturedBrandsPanel , See More Products - 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 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 ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ์†Œ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  --- .../ProductAllSection/ProductAllSection.jsx | 30 ++- .../SeeMoreProducts/SeeMoreProducts.jsx | 197 ++++++++++++++++++ .../SeeMoreProducts.module.less | 106 ++++++++++ 3 files changed, 328 insertions(+), 5 deletions(-) create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.jsx create mode 100644 com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.module.less diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx index 87eee2fd..c99f28ef 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -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({
- {/* TODO: ๋‚˜์ค‘์— ๊ทธ๋ฃน ์ƒํ’ˆ ํ‘œ์‹œ ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ */} -
-

SEE MORE PRODUCTS

-

๊ทธ๋ฃน ์ƒํ’ˆ์ด ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค

-
+ handleButtonFocus('seemoreproducts')} + onBlur={handleButtonBlur} + />
)} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.jsx new file mode 100644 index 00000000..ed5c7357 --- /dev/null +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.jsx @@ -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 ( +
+ {newGroupProductData && newGroupProductData.length > 0 && ( + + + +
+ {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 ( + + ); + })} +
+
+
+ )} +
+ ); +} \ No newline at end of file diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.module.less new file mode 100644 index 00000000..fea0513d --- /dev/null +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/SeeMoreProducts/SeeMoreProducts.module.less @@ -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; + } + } + } +}