[251210] feat: FeaturedBrandsPanel , See More Products - 1

🕐 커밋 시간: 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

🔧 주요 변경 내용:
  • 타입 시스템 안정성 강화
  • 핵심 비즈니스 로직 개선
  • 중간 규모 기능 개선
This commit is contained in:
2025-12-10 18:29:18 +09:00
parent cbdf1b89f8
commit 8057021d1c
5 changed files with 133 additions and 0 deletions

View File

@@ -137,6 +137,9 @@ export const types = {
RESET_BRAND_STATE: 'RESET_BRAND_STATE', RESET_BRAND_STATE: 'RESET_BRAND_STATE',
RESET_BRAND_STATE_EXCEPT_BRAND_INFO: 'RESET_BRAND_STATE_EXCEPT_BRAND_INFO', RESET_BRAND_STATE_EXCEPT_BRAND_INFO: 'RESET_BRAND_STATE_EXCEPT_BRAND_INFO',
RESET_BRAND_LAYOUT_INFO: 'RESET_BRAND_LAYOUT_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 // main actions
GET_SUB_CATEGORY: 'GET_SUB_CATEGORY', GET_SUB_CATEGORY: 'GET_SUB_CATEGORY',

View File

@@ -410,6 +410,8 @@ export const getBrandShopByShow = (props) => (dispatch, getState) => {
type: types.GET_BRAND_SHOP_BY_SHOW, type: types.GET_BRAND_SHOP_BY_SHOW,
payload: { payload: {
data: response.data.data, data: response.data.data,
patnrId,
contsId,
}, },
}); });

View File

@@ -52,6 +52,9 @@ const initialState = {
brandTopBannerData: { brandTopBannerData: {
data: {}, data: {},
}, },
// 🆕 [251210] patnrId=21 카테고리 그룹 데이터 저장소
brandShopByShowCategoryGroups: {},
}; };
export const brandReducer = (state = initialState, action) => { export const brandReducer = (state = initialState, action) => {
@@ -174,12 +177,31 @@ export const brandReducer = (state = initialState, action) => {
? nextData ? nextData
: { ...prevData, ...nextData, brandShopByShowContsList: prevData.brandShopByShowContsList }; : { ...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 { return {
...state, ...state,
brandShopByShowData: { brandShopByShowData: {
...action.payload, ...action.payload,
data: mergedData, 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: default:
return state; return state;
} }

View File

@@ -1108,6 +1108,7 @@ export default function ProductAllSection({
const descriptionRef = useRef(null); const descriptionRef = useRef(null);
const reviewRef = useRef(null); const reviewRef = useRef(null);
const youMayAlsoLikelRef = useRef(null); const youMayAlsoLikelRef = useRef(null);
const seeMoreProductsRef = useRef(null);
const prevMediaPanelModalStateRef = useRef(null); // MediaPanel의 이전 modal 상태 추적 const prevMediaPanelModalStateRef = useRef(null); // MediaPanel의 이전 modal 상태 추적
// 동영상과 이미지를 통합한 렌더링 아이템 리스트 생성 (Indicator.jsx 로직 기반) // 동영상과 이미지를 통합한 렌더링 아이템 리스트 생성 (Indicator.jsx 로직 기반)
@@ -1264,6 +1265,15 @@ export default function ProductAllSection({
Spotlight.focus("detail_youMayAlsoLike_area") Spotlight.focus("detail_youMayAlsoLike_area")
},100); },100);
}, [scrollToSection, dispatch]); }, [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 아이콘에서 아래로 내려올 때 첫 번째 버튼을 바라보도록 설정 // 헤더 Back 아이콘에서 아래로 내려올 때 첫 번째 버튼을 바라보도록 설정
useEffect(() => { useEffect(() => {
const firstId = stackOrder[0]; const firstId = stackOrder[0];
@@ -1751,10 +1761,27 @@ export default function ProductAllSection({
activeButton === 'youmaylike' ? css.active : '' activeButton === 'youmaylike' ? css.active : ''
)} )}
onClick={handleYouMayAlsoLikeClick} onClick={handleYouMayAlsoLikeClick}
onFocus={() => handleButtonFocus('youmaylike')}
onBlur={handleButtonBlur}
> >
{$L('YOU MAY ALSO LIKE')} {$L('YOU MAY ALSO LIKE')}
</TButton> </TButton>
)} )}
{/* 🆕 [251210] patnrId=21인 경우 SEE MORE PRODUCTS 버튼 */}
{(panelInfo?.patnrId === 21 || panelInfo?.patnrId === "21") && (
<TButton
className={classNames(
css.youMayLikeButton,
activeButton === 'seemoreproducts' ? css.active : ''
)}
onClick={handleSeeMoreProductsClick}
onFocus={() => handleButtonFocus('seemoreproducts')}
onBlur={handleButtonBlur}
spotlightId="see-more-products-button"
>
{$L('SEE MORE PRODUCTS')}
</TButton>
)}
{/* YouMayLike 버튼 렌더링 상태 로그 */} {/* YouMayLike 버튼 렌더링 상태 로그 */}
{/* {(() => { {/* {(() => {
console.log('[YouMayLike] 버튼 렌더링 체크:', { console.log('[YouMayLike] 버튼 렌더링 체크:', {
@@ -1938,6 +1965,20 @@ export default function ProductAllSection({
</div> </div>
)} )}
</div> </div>
{/* 🆕 [251210] patnrId=21인 경우 SEE MORE PRODUCTS 섹션 */}
{(panelInfo?.patnrId === 21 || panelInfo?.patnrId === "21") && (
<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>
</div>
</div>
)}
<div className={css.topButtonBox}> <div className={css.topButtonBox}>
<TButton <TButton

View File

@@ -280,6 +280,10 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
const brandTopBannerInfo = useSelector( const brandTopBannerInfo = useSelector(
(state) => state.brand.brandTopBannerData.data.brandTopBannerInfo (state) => state.brand.brandTopBannerData.data.brandTopBannerInfo
); );
// 🆕 [251210] patnrId=21 카테고리 그룹 데이터
const brandShopByShowCategoryGroups = useSelector(
(state) => state.brand.brandShopByShowCategoryGroups
);
const [displayTopButton, setDisplayTopButton] = useState(false); const [displayTopButton, setDisplayTopButton] = useState(false);
const [focusedContainerId, setFocusedContainerId] = useState(null); const [focusedContainerId, setFocusedContainerId] = useState(null);
@@ -310,6 +314,9 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
}); });
const renderedShelfCountRef = useRef(0); const renderedShelfCountRef = useRef(0);
// 🆕 [251210] patnrId=21 카테고리 그룹 조회 상태 추적
const fetchedCategoryGroupsRef = useRef(new Set());
const fromDetail = panelInfo?.from && panelInfo.from === "detail"; const fromDetail = panelInfo?.from && panelInfo.from === "detail";
const fromGNB = panelInfo?.from && panelInfo.from === "gnb"; const fromGNB = panelInfo?.from && panelInfo.from === "gnb";
const fromUpcoming = panelInfo?.from && panelInfo.from === "upcoming"; 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] sortedBrandLayoutInfo:", sortedBrandLayoutInfo);
console.log("[FB-PANEL-DATA-FETCH] selectedPatnrId:", selectedPatnrId); 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) { if (sortedBrandLayoutInfo && selectedPatnrId) {
console.log("[FB-PANEL-DATA-FETCH] Fetching data - patnrId:", selectedPatnrId); console.log("[FB-PANEL-DATA-FETCH] Fetching data - patnrId:", selectedPatnrId);
Object.entries(DISPATCH_MAP) // Object.entries(DISPATCH_MAP) //
@@ -835,6 +854,33 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
} }
}, [sortedBrandLayoutInfo, selectedPatnrId]); }, [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(() => { useEffect(() => {
if (selectedCatCd) { if (selectedCatCd) {
dispatch( dispatch(