From 8057021d1c8e436b872c8278a2d21efbc818f5c0 Mon Sep 17 00:00:00 2001 From: optrader Date: Wed, 10 Dec 2025 18:29:18 +0900 Subject: [PATCH] [251210] feat: FeaturedBrandsPanel , See More Products - 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 12. 10. 18:29:18 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 5๊ฐœ โ€ข ์ถ”๊ฐ€: +133์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/actions/actionTypes.js ~ com.twin.app.shoptime/src/actions/brandActions.js ~ com.twin.app.shoptime/src/reducers/brandReducer.js ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํƒ€์ž… ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ ๊ฐ•ํ™” โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  --- .../src/actions/actionTypes.js | 3 ++ .../src/actions/brandActions.js | 2 + .../src/reducers/brandReducer.js | 41 +++++++++++++++++ .../ProductAllSection/ProductAllSection.jsx | 41 +++++++++++++++++ .../FeaturedBrandsPanel.jsx | 46 +++++++++++++++++++ 5 files changed, 133 insertions(+) diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index 595b6408..899acac2 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -137,6 +137,9 @@ export const types = { RESET_BRAND_STATE: 'RESET_BRAND_STATE', RESET_BRAND_STATE_EXCEPT_BRAND_INFO: 'RESET_BRAND_STATE_EXCEPT_BRAND_INFO', RESET_BRAND_LAYOUT_INFO: 'RESET_BRAND_LAYOUT_INFO', + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ + SET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS: 'SET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS', + RESET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS: 'RESET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS', // main actions GET_SUB_CATEGORY: 'GET_SUB_CATEGORY', diff --git a/com.twin.app.shoptime/src/actions/brandActions.js b/com.twin.app.shoptime/src/actions/brandActions.js index 713472f0..7b835478 100644 --- a/com.twin.app.shoptime/src/actions/brandActions.js +++ b/com.twin.app.shoptime/src/actions/brandActions.js @@ -410,6 +410,8 @@ export const getBrandShopByShow = (props) => (dispatch, getState) => { type: types.GET_BRAND_SHOP_BY_SHOW, payload: { data: response.data.data, + patnrId, + contsId, }, }); diff --git a/com.twin.app.shoptime/src/reducers/brandReducer.js b/com.twin.app.shoptime/src/reducers/brandReducer.js index 96c76408..7eb92d6d 100644 --- a/com.twin.app.shoptime/src/reducers/brandReducer.js +++ b/com.twin.app.shoptime/src/reducers/brandReducer.js @@ -52,6 +52,9 @@ const initialState = { brandTopBannerData: { data: {}, }, + + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ + brandShopByShowCategoryGroups: {}, }; export const brandReducer = (state = initialState, action) => { @@ -174,12 +177,31 @@ export const brandReducer = (state = initialState, action) => { ? nextData : { ...prevData, ...nextData, brandShopByShowContsList: prevData.brandShopByShowContsList }; + // ๐Ÿ†• [251210] patnrId=21์ธ ๊ฒฝ์šฐ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ๋ณ„๋„ ์ €์žฅ + const updatedCategoryGroups = { ...state.brandShopByShowCategoryGroups }; + + if (action.payload?.patnrId === 21 || action.payload?.patnrId === "21") { + const patnrId = String(action.payload.patnrId); + + // patnrId๋ณ„ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์œผ๋ฉด ์ดˆ๊ธฐํ™” + if (!updatedCategoryGroups[patnrId]) { + updatedCategoryGroups[patnrId] = {}; + } + + // ํ˜„์žฌ contsId์— ๋Œ€ํ•œ ๊ทธ๋ฃน ์ •๋ณด ์ €์žฅ + if (nextData.brandShopByShowContsInfo?.contsId) { + const contsId = nextData.brandShopByShowContsInfo.contsId; + updatedCategoryGroups[patnrId][contsId] = nextData.brandShopByShowContsInfo; + } + } + return { ...state, brandShopByShowData: { ...action.payload, data: mergedData, }, + brandShopByShowCategoryGroups: updatedCategoryGroups, }; } @@ -218,6 +240,25 @@ export const brandReducer = (state = initialState, action) => { }; } + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์„ค์ • + case types.SET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS: { + return { + ...state, + brandShopByShowCategoryGroups: { + ...state.brandShopByShowCategoryGroups, + ...action.payload, + }, + }; + } + + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์ดˆ๊ธฐํ™” + case types.RESET_BRAND_SHOP_BY_SHOW_CATEGORY_GROUPS: { + return { + ...state, + brandShopByShowCategoryGroups: {}, + }; + } + default: return state; } 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 92008749..87c4fa64 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -1108,6 +1108,7 @@ export default function ProductAllSection({ const descriptionRef = useRef(null); const reviewRef = useRef(null); const youMayAlsoLikelRef = useRef(null); + const seeMoreProductsRef = useRef(null); const prevMediaPanelModalStateRef = useRef(null); // MediaPanel์˜ ์ด์ „ modal ์ƒํƒœ ์ถ”์  // ๋™์˜์ƒ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ํ†ตํ•ฉํ•œ ๋ Œ๋”๋ง ์•„์ดํ…œ ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (Indicator.jsx ๋กœ์ง ๊ธฐ๋ฐ˜) @@ -1264,6 +1265,15 @@ export default function ProductAllSection({ Spotlight.focus("detail_youMayAlsoLike_area") },100); }, [scrollToSection, dispatch]); + + const handleSeeMoreProductsClick = useCallback(() => { + console.log('[SEE MORE PRODUCTS] Button clicked - currently no action'); + // TODO: ๋‚˜์ค‘์— ๊ทธ๋ฃน ์ƒํ’ˆ ํ‘œ์‹œ ๋กœ์ง ์ถ”๊ฐ€ + scrollToSection('scroll-marker-see-more-products'); + setTimeout(() => { + Spotlight.focus("see-more-products-area"); + }, 100); + }, [scrollToSection]); // ํ—ค๋” Back ์•„์ด์ฝ˜์—์„œ ์•„๋ž˜๋กœ ๋‚ด๋ ค์˜ฌ ๋•Œ ์ฒซ ๋ฒˆ์งธ ๋ฒ„ํŠผ์„ ๋ฐ”๋ผ๋ณด๋„๋ก ์„ค์ • useEffect(() => { const firstId = stackOrder[0]; @@ -1751,10 +1761,27 @@ export default function ProductAllSection({ activeButton === 'youmaylike' ? css.active : '' )} onClick={handleYouMayAlsoLikeClick} + onFocus={() => handleButtonFocus('youmaylike')} + onBlur={handleButtonBlur} > {$L('YOU MAY ALSO LIKE')} )} + {/* ๐Ÿ†• [251210] patnrId=21์ธ ๊ฒฝ์šฐ SEE MORE PRODUCTS ๋ฒ„ํŠผ */} + {(panelInfo?.patnrId === 21 || panelInfo?.patnrId === "21") && ( + handleButtonFocus('seemoreproducts')} + onBlur={handleButtonBlur} + spotlightId="see-more-products-button" + > + {$L('SEE MORE PRODUCTS')} + + )} {/* YouMayLike ๋ฒ„ํŠผ ๋ Œ๋”๋ง ์ƒํƒœ ๋กœ๊ทธ */} {/* {(() => { console.log('[YouMayLike] ๋ฒ„ํŠผ ๋ Œ๋”๋ง ์ฒดํฌ:', { @@ -1938,6 +1965,20 @@ export default function ProductAllSection({ )} + + {/* ๐Ÿ†• [251210] patnrId=21์ธ ๊ฒฝ์šฐ SEE MORE PRODUCTS ์„น์…˜ */} + {(panelInfo?.patnrId === 21 || panelInfo?.patnrId === "21") && ( +
+
+
+ {/* TODO: ๋‚˜์ค‘์— ๊ทธ๋ฃน ์ƒํ’ˆ ํ‘œ์‹œ ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ */} +
+

SEE MORE PRODUCTS

+

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

+
+
+
+ )}
{ const brandTopBannerInfo = useSelector( (state) => state.brand.brandTopBannerData.data.brandTopBannerInfo ); + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ + const brandShopByShowCategoryGroups = useSelector( + (state) => state.brand.brandShopByShowCategoryGroups + ); const [displayTopButton, setDisplayTopButton] = useState(false); const [focusedContainerId, setFocusedContainerId] = useState(null); @@ -310,6 +314,9 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { }); const renderedShelfCountRef = useRef(0); + // ๐Ÿ†• [251210] patnrId=21 ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ์กฐํšŒ ์ƒํƒœ ์ถ”์  + const fetchedCategoryGroupsRef = useRef(new Set()); + const fromDetail = panelInfo?.from && panelInfo.from === "detail"; const fromGNB = panelInfo?.from && panelInfo.from === "gnb"; const fromUpcoming = panelInfo?.from && panelInfo.from === "upcoming"; @@ -803,6 +810,18 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { console.log("[FB-PANEL-DATA-FETCH] sortedBrandLayoutInfo:", sortedBrandLayoutInfo); console.log("[FB-PANEL-DATA-FETCH] selectedPatnrId:", selectedPatnrId); + // ๐Ÿ†• [251210] patnrId ๋ณ€๊ฒฝ ์‹œ ์กฐํšŒ ์ƒํƒœ ์ดˆ๊ธฐํ™” + if (selectedPatnrId) { + const patnrIdString = String(selectedPatnrId); + // ์ด์ „ patnrId์™€ ๋‹ค๋ฅด๋ฉด ref ์ดˆ๊ธฐํ™” + const currentFetchKeys = Array.from(fetchedCategoryGroupsRef.current).filter(key => key.startsWith(patnrIdString)); + if (currentFetchKeys.length === 0) { + console.log("[FB-PANEL-DATA-FETCH] patnrId changed, clearing category group fetch status"); + // ๋‹ค๋ฅธ patnrId๋กœ ์ „ํ™˜ ์‹œ ref ์ดˆ๊ธฐํ™” + fetchedCategoryGroupsRef.current.clear(); + } + } + if (sortedBrandLayoutInfo && selectedPatnrId) { console.log("[FB-PANEL-DATA-FETCH] Fetching data - patnrId:", selectedPatnrId); Object.entries(DISPATCH_MAP) // @@ -835,6 +854,33 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { } }, [sortedBrandLayoutInfo, selectedPatnrId]); + // ๐Ÿ†• [251210] patnrId=21์ธ ๊ฒฝ์šฐ ๋ชจ๋“  ์นดํ…Œ๊ณ ๋ฆฌ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ์กฐํšŒ + useEffect(() => { + if (selectedPatnrId === 21 || selectedPatnrId === "21") { + console.log("[FB-PANEL-CATEGORY-GROUPS] patnrId=21 detected - fetching all category group data"); + console.log("[FB-PANEL-CATEGORY-GROUPS] brandShopByShowContsList:", brandShopByShowContsList); + + // ๊ฐ ์นดํ…Œ๊ณ ๋ฆฌ(contsId)๋ณ„ ๊ทธ๋ฃน ๋ฐ์ดํ„ฐ ์กฐํšŒ + if (brandShopByShowContsList && brandShopByShowContsList.length > 0) { + brandShopByShowContsList.forEach((conts) => { + const fetchKey = `${selectedPatnrId}-${conts.contsId}`; + + // useRef๋กœ ์ด๋ฏธ ์กฐํšŒ๋œ contsId ์ถ”์  (๋ฌดํ•œ๋ฃจํ”„ ๋ฐฉ์ง€) + if (!fetchedCategoryGroupsRef.current.has(fetchKey)) { + console.log("[FB-PANEL-CATEGORY-GROUPS] Fetching category group for contsId:", conts.contsId); + fetchedCategoryGroupsRef.current.add(fetchKey); // ์กฐํšŒ ์ƒํƒœ ๊ธฐ๋ก + dispatch(getBrandShopByShow({ + patnrId: selectedPatnrId, + contsId: conts.contsId + })); + } else { + console.log("[FB-PANEL-CATEGORY-GROUPS] Category group already fetched for contsId:", conts.contsId); + } + }); + } + } + }, [selectedPatnrId, brandShopByShowContsList, dispatch]); // brandShopByShowCategoryGroups ์ œ๊ฑฐ + useEffect(() => { if (selectedCatCd) { dispatch(