[251123] fix: CategoryPanel webOS용 재시도 및 가드로직 추가

🕐 커밋 시간: 2025. 11. 23. 22:14:06

📊 변경 통계:
  • 총 파일: 3개
  • 추가: +44줄
  • 삭제: -17줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/mainActions.js
  ~ com.twin.app.shoptime/src/components/TabLayout/TabLayout.jsx
  ~ com.twin.app.shoptime/src/views/CategoryPanel/CategoryPanel.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/mainActions.js (javascript):
    🔄 Modified: getMainCategoryShowDetail(), getTop20Show()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • UI 컴포넌트 아키텍처 개선
This commit is contained in:
2025-11-23 22:14:06 +09:00
parent 549f5caee7
commit 3b95810946
3 changed files with 188 additions and 292 deletions

View File

@@ -152,8 +152,10 @@ export const getMainCategoryShowDetail = (props) => (dispatch, getState) => {
// 서브카테고리 조회 IF-LGSP-051
let getSubCategoryKey = null;
let lastSubCategoryParams = {};
const SUB_CATEGORY_RETRY_LIMIT = 3;
const SUB_CATEGORY_RETRY_DELAY_MS = 400;
export const getSubCategory =
(params, pageNo = 1, key = null, clear = false) =>
(params, pageNo = 1, key = null, clear = false, retryCount = 0) =>
(dispatch, getState) => {
const { lgCatCd, patnrIdList, tabType, filterType, recommendIncFlag } = params;
let pageSize = params.pageSize || CATEGORY_DATA_MAX_RESULTS_LIMIT;
@@ -214,6 +216,22 @@ export const getSubCategory =
};
const onFail = (error) => {
const nextRetryCount = retryCount + 1;
const canRetry = nextRetryCount < SUB_CATEGORY_RETRY_LIMIT;
if (canRetry) {
console.warn('getSubCategory retry', {
lgCatCd,
pageNo,
retryCount: nextRetryCount,
});
setTimeout(() => {
dispatch(getSubCategory(params, pageNo, currentKey, clear, nextRetryCount));
}, SUB_CATEGORY_RETRY_DELAY_MS * nextRetryCount);
return;
}
console.error('getSubCategory onFail', error);
if (pageNo === 1) {
lastSubCategoryParams = {};
@@ -234,23 +252,23 @@ export const getSubCategory =
export const continueGetSubCategory = (key, pageNo) => (dispatch, getState) => {
if (!lastSubCategoryParams) {
// <<<<<<< HEAD
// <<<<<<< HEAD
console.warn('No previous category parameters found');
// =======
// console.warn("No previous category parameters found");
// >>>>>>> gitlab/develop
// =======
// console.warn("No previous category parameters found");
// >>>>>>> gitlab/develop
return;
}
const subCategoryData = getState().main.subCategoryData;
const targetData =
// <<<<<<< HEAD
// <<<<<<< HEAD
subCategoryData[key]?.subCatItemList || subCategoryData[key]?.subCatShowList || [];
// =======
// subCategoryData[key]?.subCatItemList ||
// subCategoryData[key]?.subCatShowList ||
// [];
// >>>>>>> gitlab/develop
// =======
// subCategoryData[key]?.subCatItemList ||
// subCategoryData[key]?.subCatShowList ||
// [];
// >>>>>>> gitlab/develop
const totalCount = subCategoryData[key]?.total ?? 0;
const startIndex = CATEGORY_DATA_MAX_RESULTS_LIMIT * (pageNo - 1);
if (
@@ -261,13 +279,13 @@ export const continueGetSubCategory = (key, pageNo) => (dispatch, getState) => {
//ignore query
return;
}
// <<<<<<< HEAD
// <<<<<<< HEAD
dispatch(getSubCategory({ ...lastSubCategoryParams }, pageNo, getSubCategoryKey));
// =======
// dispatch(
// getSubCategory({ ...lastSubCategoryParams }, pageNo, getSubCategoryKey)
// );
// >>>>>>> gitlab/develop
// =======
// dispatch(
// getSubCategory({ ...lastSubCategoryParams }, pageNo, getSubCategoryKey)
// );
// >>>>>>> gitlab/develop
};
const clearSubCategory = () => ({
@@ -341,11 +359,11 @@ export const getMainYouMayLike =
getState,
'get',
URLS.GET_YOUMAYLIKE,
// <<<<<<< HEAD
// <<<<<<< HEAD
{ lgCatCd, exclCurationId, exclPatnrId, exclPrdtId, catDpTh3, catDpTh4 },
// =======
// { lgCatCd, catDpTh3, catDpTh4, exclCurationId, exclPatnrId, exclPrdtId },
// >>>>>>> gitlab/develop
// =======
// { lgCatCd, catDpTh3, catDpTh4, exclCurationId, exclPatnrId, exclPrdtId },
// >>>>>>> gitlab/develop
{},
onSuccess,
onFail

View File

@@ -1,42 +1,23 @@
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from 'classnames';
import {
useDispatch,
useSelector,
} from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
//아이콘
import { Job } from '@enact/core/util';
//enact
import Skinnable from '@enact/sandstone/Skinnable';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import { Cancelable } from '@enact/ui/Cancelable';
import shoptimeFullIconRuc
from '../../../assets/images/icons/ic-lnb-logo-shoptime-ruc-white.png';
import shoptimeFullIconRuc from '../../../assets/images/icons/ic-lnb-logo-shoptime-ruc-white.png';
//이미지
import shoptimeFullIcon
from '../../../assets/images/icons/ic-lnb-logo-shoptime@3x.png';
import shoptimeFullIcon from '../../../assets/images/icons/ic-lnb-logo-shoptime@3x.png';
import { gnbOpened } from '../../actions/commonActions';
import {
checkEnterThroughGNB,
resetHomeInfo,
} from '../../actions/homeActions';
import { checkEnterThroughGNB, resetHomeInfo } from '../../actions/homeActions';
import { resetPanels } from '../../actions/panelActions';
import {
clearShopperHouseData,
resetSearch,
resetVoiceSearch,
} from '../../actions/searchActions';
import { clearShopperHouseData, resetSearch, resetVoiceSearch } from '../../actions/searchActions';
import usePrevious from '../../hooks/usePrevious';
import useScrollTo from '../../hooks/useScrollTo';
import { panel_names } from '../../utils/Config';
@@ -55,32 +36,14 @@ import TabItem from './TabItem';
import TabItemSub from './TabItemSub';
import css from './TabLayout.module.less';
const Container = SpotlightContainerDecorator(
{ enterTo: "default-element" },
"div"
);
const Container = SpotlightContainerDecorator({ enterTo: 'default-element' }, 'div');
const MainContainer = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const MainContainer = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
const CancelableDiv = Cancelable(
{ modal: true, onCancel: "handleCancel" },
Skinnable(Container)
);
const CancelableDiv = Cancelable({ modal: true, onCancel: 'handleCancel' }, Skinnable(Container));
class TabMenuItem {
constructor(
icons = "",
title = "",
spotlightId,
path,
patncNm,
target,
id,
children = []
) {
constructor(icons = '', title = '', spotlightId, path, patncNm, target, id, children = []) {
this.icons = icons;
this.title = title;
this.spotlightId = spotlightId;
@@ -147,30 +110,26 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
const [mainSelectedIndex, setMainSelectedIndex] = useState(-1);
const [secondDepthReduce, setSecondDepthReduce] = useState(false);
const [lastFocusId, setLastFocusId] = useState(null);
const [selectedTitle, setSelectedTitle] = useState("");
const [selectedTitle, setSelectedTitle] = useState('');
const [selectedSubItemId, setSelectedSubItemId] = useState(null);
const [selectedSubIndex, setSelectedSubIndex] = useState(-1);
const [subTabLastFocusId, setSubTabLastFocusId] = useState(null);
const [tabs, setTabs] = useState([]);
const [tabFocused, setTabFocused] = useState([false, false, false]); //COLLABSED_MAIN, ACTIVATED_MAIN, ACTIVATED_SUB
const panelSwitching = useRef(null);
const cursorVisible = useSelector(
(state) => state.common.appStatus.cursorVisible
);
const cursorVisible = useSelector((state) => state.common.appStatus.cursorVisible);
const cursorVisibleRef = usePrevious(cursorVisible);
const data = useSelector((state) => state.home.menuData?.data);
const panels = useSelector((state) => state.panels.panels);
const { loginUserData } = useSelector((state) => state.common.appStatus);
const menuItems = useSelector((state) => state.home.menuItems);
const webOSVersion = useSelector(
(state) => state.common.appStatus.webOSVersion
);
const webOSVersion = useSelector((state) => state.common.appStatus.webOSVersion);
const httpHeader = useSelector((state) => state.common.httpHeader);
const broadcast = useSelector(
(state) => state.common.broadcast,
(newState) => newState?.type !== "deActivateTab" // 'deActivateTab'일 때만 리렌더링 허용
(newState) => newState?.type !== 'deActivateTab' // 'deActivateTab'일 때만 리렌더링 허용
);
const deviceCountryCode = httpHeader["X-Device-Country"];
const deviceCountryCode = httpHeader['X-Device-Country'];
const mouseNavOpen = useRef(new Job((func) => func(), 1000));
const mouseMainEntered = useRef(false);
const scrollTopJobRef = useRef(new Job((func) => func(), 0));
@@ -179,23 +138,21 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
let result = [];
switch (type) {
case "GNB":
case 'GNB':
result =
data?.gnb &&
data.gnb.map((item) => ({
data?.gnb?.map((item) => ({
title: item.menuNm,
}));
})) || [];
break;
//카테고리
case 10500:
result =
data?.homeCategory &&
data.homeCategory.map((item) => ({
data?.homeCategory?.map((item) => ({
icons: CategoryIcon,
id: item.lgCatCd,
title: item.lgCatNm,
spotlightId: "spotlight_category",
spotlightId: 'spotlight_category',
target: [
{
name: panel_names.CATEGORY_PANEL,
@@ -211,34 +168,33 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
},
},
],
}));
})) || [];
break;
//브랜드
case 10300:
result =
data?.shortFeaturedBrands &&
data.shortFeaturedBrands.map((item) => ({
data?.shortFeaturedBrands?.map((item) => ({
icons: FeaturedBrandIcon,
id: item.patnrId,
path: item.patncLogoPath,
patncNm: item.patncNm,
spotlightId: "spotlight_featuredbrand",
spotlightId: 'spotlight_featuredbrand',
target: [
{
name: panel_names.FEATURED_BRANDS_PANEL,
panelInfo: { from: "gnb", patnrId: item.patnrId },
panelInfo: { from: 'gnb', patnrId: item.patnrId },
},
],
}));
})) || [];
break;
//
case 10600:
result = data.mypage
.map((item) => ({
result = (
data?.mypage?.map((item) => ({
icons: MyPageIcon,
id: item.menuId,
title: item.menuNm,
spotlightId: "spotlight_mypage",
spotlightId: 'spotlight_mypage',
target: [
{
name: panel_names.MY_PAGE_PANEL,
@@ -249,26 +205,23 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
},
},
],
}))
.filter((item) => {
if (!loginUserData.userNumber && item.title === "My Orders") {
return false;
}
if (
webOSVersion < "6.0" &&
(item.title === "My Orders" || item.title === "My Info")
) {
return false;
}
return true;
});
})) || []
).filter((item) => {
if (!loginUserData.userNumber && item.title === 'My Orders') {
return false;
}
if (webOSVersion < '6.0' && (item.title === 'My Orders' || item.title === 'My Info')) {
return false;
}
return true;
});
break;
case 10700:
result = [
{
icons: SearchIcon,
spotlightId: "spotlight_search",
spotlightId: 'spotlight_search',
target: [{ name: panel_names.SEARCH_PANEL }],
},
];
@@ -278,7 +231,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
result = [
{
icons: HomeIcon,
spotlightId: "spotlight_home",
spotlightId: 'spotlight_home',
target: [{ name: panel_names.HOME_PANEL }],
},
];
@@ -288,7 +241,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
result = [
{
icons: OnSaleIcon,
spotlightId: "spotlight_onsale",
spotlightId: 'spotlight_onsale',
target: [{ name: panel_names.ON_SALE_PANEL }],
},
];
@@ -298,7 +251,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
result = [
{
icons: TrendingNowIcon,
spotlightId: "spotlight_trendingnow",
spotlightId: 'spotlight_trendingnow',
target: [{ name: panel_names.TRENDING_NOW_PANEL }],
},
];
@@ -308,16 +261,16 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
result = [
{
icons: HotPicksIcon,
spotlightId: "spotlight_hotpicks",
spotlightId: 'spotlight_hotpicks',
target: [{ name: panel_names.HOT_PICKS_PANEL }],
},
];
break;
case 10800:
case 10800:
result = [
{
icons: CartIcon,
spotlightId: "spotlight_cart",
spotlightId: 'spotlight_cart',
target: [{ name: panel_names.CART_PANEL }],
},
];
@@ -331,7 +284,12 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
if (data) {
for (let i = 0; i < menuItems.length; i++) {
const currentKey = menuItems[i].menuId;
const menuInfo = getMenuData(currentKey || "GNB");
const menuInfo = getMenuData(currentKey || 'GNB') || [];
if (!Array.isArray(menuInfo) || menuInfo.length === 0) {
menuItems[i].children = [];
continue;
}
for (let j = 0; j < menuInfo.length; j++) {
if (![10600, 10500, 10300].includes(currentKey)) {
@@ -595,7 +553,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
}, [mainExpanded, mainSelectedIndex]);
const logoImg = useMemo(() => {
if (deviceCountryCode === "RU") {
if (deviceCountryCode === 'RU') {
return shoptimeFullIconRuc;
} else return shoptimeFullIcon;
}, [deviceCountryCode]);
@@ -608,11 +566,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
}, [topPanelName]);
const showSubTab = useMemo(() => {
if (
tabActivated &&
tabs[mainSelectedIndex] &&
tabs[mainSelectedIndex].hasChildren()
) {
if (tabActivated && tabs[mainSelectedIndex] && tabs[mainSelectedIndex].hasChildren()) {
return true; // 서브 탭이 있는 경우
}
return false; // 서브 탭이 없는 경우
@@ -640,7 +594,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
dispatch(gnbOpened(true));
if (panels.length === 0) {
Spotlight.focus("spotlight_home");
Spotlight.focus('spotlight_home');
return;
}
@@ -650,7 +604,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
//
else {
if (!subTabLastFocusId) {
Spotlight.focus("spotlight_home");
Spotlight.focus('spotlight_home');
}
}
}
@@ -664,7 +618,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
useEffect(() => {
if (!panelInfo) {
setMainSelectedIndex(-1);
setLastFocusId("spotlight_home");
setLastFocusId('spotlight_home');
setSubTabLastFocusId(null);
return;
}
@@ -681,17 +635,11 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
subTarget = panelInfo.lgCatCd;
}
// case: Featured Brands 2depth
else if (
topPanelName === panel_names.FEATURED_BRANDS_PANEL &&
panelInfo?.patnrId
) {
else if (topPanelName === panel_names.FEATURED_BRANDS_PANEL && panelInfo?.patnrId) {
subTarget = panelInfo.patnrId;
}
// case: My Info 2depth
else if (
topPanelName === panel_names.MY_PAGE_PANEL &&
panelInfo?.menuId
) {
else if (topPanelName === panel_names.MY_PAGE_PANEL && panelInfo?.menuId) {
subTarget = panelInfo.menuId;
}
}
@@ -716,8 +664,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
}, [tabActivated, subTabLastFocusId, mainSelectedIndex]);
useEffect(() => {
const hasFeaturedBrands =
tabs[mainSelectedIndex]?.children[0]?.path !== undefined;
const hasFeaturedBrands = tabs[mainSelectedIndex]?.children[0]?.path !== undefined;
const SCROLL_OFFSET_INDEX = hasFeaturedBrands ? 8 : 9;
@@ -735,9 +682,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
tabs[mainSelectedIndex]?.children.length - 1 >= selectedSubIndex
) {
const targetScrollIndex = selectedSubIndex - SCROLL_OFFSET_INDEX;
scrollTopJobRef.current.start(() =>
scrollTop({ y: y * targetScrollIndex })
);
scrollTopJobRef.current.start(() => scrollTop({ y: y * targetScrollIndex }));
return () => scrollTopJobRef.current.stop();
}
@@ -770,8 +715,8 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
}, [cursorVisible]);
useEffect(() => {
if (broadcast?.type === "deActivateTab") {
console.log("TabLayout deactivateTab by broadcast");
if (broadcast?.type === 'deActivateTab') {
console.log('TabLayout deactivateTab by broadcast');
deActivateTab();
}
}, [broadcast]);
@@ -803,7 +748,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
const moveFocusToMainTab = useCallback(
(e) => {
if (e.key === "ArrowLeft" && showSubTab && lastFocusId) {
if (e.key === 'ArrowLeft' && showSubTab && lastFocusId) {
Spotlight.focus(lastFocusId);
}
},
@@ -846,16 +791,14 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
<img
src={logoImg}
alt=""
className={classNames(
deviceCountryCode === "RU" && css.rucLogo
)}
className={classNames(deviceCountryCode === 'RU' && css.rucLogo)}
/>
</h1>
{tabs.map((item, index) => (
<TabItem
{...item}
key={"tabitemExpanded" + index}
key={'tabitemExpanded' + index}
onFocus={onFocus}
spotlightId={item.spotlightId}
setLastFocusId={setLastFocusId}
@@ -865,11 +808,10 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
icons={item.icons}
expanded={mainExpanded}
mainSelected={
(panels.length === 0 &&
item.spotlightId === "spotlight_home") ||
(panels.length === 0 && item.spotlightId === 'spotlight_home') ||
(panels[0]?.name === panel_names.PLAYER_PANEL &&
panels.length === 1 &&
item.spotlightId === "spotlight_home") ||
item.spotlightId === 'spotlight_home') ||
(Array.isArray(item.target) &&
item.target[0]?.name &&
panels[0]?.name === item.target[0]?.name)
@@ -906,19 +848,14 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
onMouseLeave={onTabBlur(ACTIVATED_SUB)}
onKeyDown={moveFocusToMainTab}
>
<TScroller
cbScrollTo={getScrollTo}
className={css.scrollWrap}
>
<TScroller cbScrollTo={getScrollTo} className={css.scrollWrap}>
{showSubTab &&
tabs[mainSelectedIndex]?.children.map((item, index) => {
return (
<TabItemSub
{...item}
mainMenuTitle={
tabs && tabs[mainSelectedIndex]?.title
}
key={"tabitemSubmenu" + index}
mainMenuTitle={tabs && tabs[mainSelectedIndex]?.title}
key={'tabitemSubmenu' + index}
spotlightId={item.spotlightId}
setLastFocusId={setSubTabLastFocusId}
onClick={onClickSubItem}
@@ -926,7 +863,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
index={index}
isSubItem={true}
deActivateTab={deActivateTab}
title={item.title + "-sub"}
title={item.title + '-sub'}
itemId={item.id}
path={item.path}
patncNm={item.patncNm}
@@ -940,10 +877,7 @@ export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {
panels[0]?.panelInfo === item.target[0]?.panelInfo
}
label={
index * 1 +
1 +
" of " +
tabs[mainSelectedIndex]?.children.length
index * 1 + 1 + ' of ' + tabs[mainSelectedIndex]?.children.length
}
/>
);

View File

@@ -1,38 +1,25 @@
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
import { Job } from '@enact/core/util';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import {
sendLogCuration,
sendLogGNB,
sendLogTotalRecommend,
} from "../../actions/logActions";
import {
continueGetSubCategory,
getSubCategory,
} from "../../actions/mainActions";
import { updatePanel } from "../../actions/panelActions";
import SectionTitle from "../../components/SectionTitle/SectionTitle";
import TBody from "../../components/TBody/TBody";
import TButton, { TYPES } from "../../components/TButton/TButton";
import TButtonTab, { LIST_TYPE } from "../../components/TButtonTab/TButtonTab";
import TDropDown from "../../components/TDropDown/TDropDown";
import THeader from "../../components/THeader/THeader";
import TPanel from "../../components/TPanel/TPanel";
import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
import usePrevious from "../../hooks/usePrevious";
import { sendLogCuration, sendLogGNB, sendLogTotalRecommend } from '../../actions/logActions';
import { continueGetSubCategory, getSubCategory } from '../../actions/mainActions';
import { updatePanel } from '../../actions/panelActions';
import SectionTitle from '../../components/SectionTitle/SectionTitle';
import TBody from '../../components/TBody/TBody';
import TButton, { TYPES } from '../../components/TButton/TButton';
import TButtonTab, { LIST_TYPE } from '../../components/TButtonTab/TButtonTab';
import TDropDown from '../../components/TDropDown/TDropDown';
import THeader from '../../components/THeader/THeader';
import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator from '../../components/TVerticalPagenator/TVerticalPagenator';
import usePrevious from '../../hooks/usePrevious';
import {
CATEGORY_DATA_MAX_RESULTS_LIMIT,
LOG_CONTEXT_NAME,
@@ -40,28 +27,21 @@ import {
LOG_MESSAGE_ID,
LOG_TP_NO,
panel_names,
} from "../../utils/Config";
import {
$L,
getSpottableDescendants,
isElementInContainer,
} from "../../utils/helperMethods";
import { SpotlightIds } from "../../utils/SpotlightIds";
import ItemContents from "./CategoryContents/ItemContents/ItemContents";
import ShowContents from "./CategoryContents/ShowContents/ShowContents";
import css from "./CategoryPanel.module.less";
} from '../../utils/Config';
import { $L, getSpottableDescendants, isElementInContainer } from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds';
import ItemContents from './CategoryContents/ItemContents/ItemContents';
import ShowContents from './CategoryContents/ShowContents/ShowContents';
import css from './CategoryPanel.module.less';
const Container = SpotlightContainerDecorator({ enterTo: null }, "div");
const Container = SpotlightContainerDecorator({ enterTo: null }, 'div');
const TabContainer = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const TabContainer = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
const INDEX_ITEM = 0;
const INDEX_SHOWS = 1;
const getButtonTabList = () => {
return [$L("ITEM"), $L("SHOWS")];
return [$L('ITEM'), $L('SHOWS')];
};
let buttonTabList = null;
@@ -93,9 +73,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
const [tab, setTab] = useState(panelInfo.tab ? panelInfo.tab : INDEX_ITEM);
const tabRef = usePrevious(tab);
const categoryDatasRef = usePrevious(categoryDatas);
const [dropDownTab, setDropDownTab] = useState(
panelInfo.dropDownTab ? panelInfo.dropDownTab : 0
);
const [dropDownTab, setDropDownTab] = useState(panelInfo.dropDownTab ? panelInfo.dropDownTab : 0);
const dropDownTabRef = usePrevious(dropDownTab);
const [filterMethods, setFilterMethods] = useState([]);
const [styleChange, setStyleChange] = useState(false);
@@ -143,19 +121,19 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
}
logParamsRef.current = {
cnttTpNm: "",
cnttTpNm: '',
expsOrd: `${panelInfo?.expsOrd}`,
lgCatCd,
lgCatNm,
linkTpCd: panelInfo?.linkTpCd,
logTpNo: LOG_TP_NO.CURATION.CATEGORY,
sortTpNm: "",
sortTpNm: '',
};
}, [categoryItemInfos, categoryShowInfos, panelInfo?.expsOrd, tab]);
useEffect(() => {
const timer = setTimeout(() => {
const cnttTpNm = tab === INDEX_SHOWS ? "Show" : "Item";
const cnttTpNm = tab === INDEX_SHOWS ? 'Show' : 'Item';
dispatch(sendLogCuration({ ...logParamsRef.current, cnttTpNm }));
}, 1000);
@@ -164,7 +142,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
useEffect(() => {
const timer = setTimeout(() => {
const sortTpNm = dropDownTab === 0 ? "New" : "Popular";
const sortTpNm = dropDownTab === 0 ? 'New' : 'Popular';
dispatch(sendLogCuration({ ...logParamsRef.current, sortTpNm }));
}, 1000);
@@ -172,15 +150,20 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
}, [dropDownTab, panelInfo?.expsOrd]);
const reload = useCallback(() => {
const tabType = tabRef.current === INDEX_SHOWS ? "CAT00101" : "CAT00102";
const filterType = dropDownTabRef.current === 0 ? "CAT00202" : "CAT00201";
const pageSize = "20";
const tabType = tabRef.current === INDEX_SHOWS ? 'CAT00101' : 'CAT00102';
const filterType = dropDownTabRef.current === 0 ? 'CAT00202' : 'CAT00201';
const pageSize = '20';
const hasCategoryData =
tabRef.current === INDEX_SHOWS
? !!categoryDatas?.categoryShowInfos
: !!categoryDatas?.categoryItemInfos;
if (
categoryParams?.lgCatCd !== lgCatCd ||
categoryParams?.tabType !== tabType ||
categoryParams?.filterType !== filterType ||
categoryParams?.pageSize !== pageSize
categoryParams?.pageSize !== pageSize ||
!hasCategoryData
) {
dispatch(
getSubCategory(
@@ -196,7 +179,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
)
);
}
}, [categoryParams, lgCatCd, tab, dropDownTab]);
}, [categoryDatas, categoryParams, lgCatCd, tab, dropDownTab]);
//panelInfo changed
useEffect(() => {
@@ -207,9 +190,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
if (panelInfo.tab !== tab) {
setContainerLastFocusedElement(null, [SpotlightIds.SHOW_PRODUCTS_BOX]);
setContainerLastFocusedElement(null, [SpotlightIds.SHOW_CONTENTS_BOX]);
setContainerLastFocusedElement(null, [
SpotlightIds.CATEGORY_CONTENTS_BOX,
]);
setContainerLastFocusedElement(null, [SpotlightIds.CATEGORY_CONTENTS_BOX]);
setTab(panelInfo.tab ? panelInfo.tab : INDEX_ITEM);
} else if (panelInfo.currentSpot) {
Spotlight.focus(panelInfo.currentSpot);
@@ -217,7 +198,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
setTimeout(() => {
if (tab === INDEX_ITEM) {
Spotlight.focus(`[data-spotlight-id="${"tab-" + tab}"]`);
Spotlight.focus(`[data-spotlight-id="${'tab-' + tab}"]`);
}
Spotlight.focus(SpotlightIds.TBODY);
if (panelInfo.currentSpot) {
@@ -230,13 +211,9 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
useEffect(() => {
if (categoryDatas) {
const tabNode = document.querySelector(
`[data-spotlight-id="${"tab-" + tab}"]`
);
const tabNode = document.querySelector(`[data-spotlight-id="${'tab-' + tab}"]`);
if (tabNode) {
setContainerLastFocusedElement(tabNode, [
SpotlightIds.CATEGORY_TAB_CONTAINER,
]);
setContainerLastFocusedElement(tabNode, [SpotlightIds.CATEGORY_TAB_CONTAINER]);
}
}
}, [tab, categoryDatas, panelInfo]);
@@ -247,9 +224,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
setContainerLastFocusedElement(null, [SpotlightIds.CATEGORY_CONTENTS_BOX]);
reload();
if (categoryFilterCd) {
const detailCdNmValues = categoryFilterCd
.map((item) => $L(item.detailCdNm))
.reverse();
const detailCdNmValues = categoryFilterCd.map((item) => $L(item.detailCdNm)).reverse();
setFilterMethods(detailCdNmValues);
}
@@ -274,7 +249,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
const target = ev.currentTarget;
let currentSpotId = null;
if (target) {
currentSpotId = target.getAttribute("data-spotlight-id");
currentSpotId = target.getAttribute('data-spotlight-id');
}
dispatch(
updatePanel({
@@ -293,43 +268,28 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
let clickedTopShow = null;
let clickedTopShowPrd = null;
if (
categoryItemInfos?.subCatItemList &&
currentSpotId.includes("categoryItemContents")
) {
const clickedNumber = Number(
currentSpotId.replace("categoryItemContents", "")
);
if (categoryItemInfos?.subCatItemList && currentSpotId.includes('categoryItemContents')) {
const clickedNumber = Number(currentSpotId.replace('categoryItemContents', ''));
clickedItem = categoryItemInfos.subCatItemList[clickedNumber];
}
if (
categoryShowInfos?.subCatShowList &&
currentSpotId.includes("categoryShowContents")
) {
const clickedNumber = Number(
currentSpotId.replace("categoryShowContents", "")
);
if (categoryShowInfos?.subCatShowList && currentSpotId.includes('categoryShowContents')) {
const clickedNumber = Number(currentSpotId.replace('categoryShowContents', ''));
clickedShow = categoryShowInfos.subCatShowList[clickedNumber];
}
if (topShowInfo?.productInfos) {
if (currentSpotId.includes("showCategory-spotlightId")) {
const clickedId = currentSpotId.replace(
"showCategory-spotlightId-",
""
);
clickedTopShowPrd = topShowInfo.productInfos.find(
(item) => clickedId === item.prdtId
);
} else if (currentSpotId.includes("category-topshow")) {
if (currentSpotId.includes('showCategory-spotlightId')) {
const clickedId = currentSpotId.replace('showCategory-spotlightId-', '');
clickedTopShowPrd = topShowInfo.productInfos.find((item) => clickedId === item.prdtId);
} else if (currentSpotId.includes('category-topshow')) {
clickedTopShow = topShowInfo;
}
}
const params = {
tabTitle: tabRef.current === 0 ? "ITEM" : "SHOWS",
sortType: dropDownTabRef.current === 0 ? "NEW" : "MOST POPULAR",
tabTitle: tabRef.current === 0 ? 'ITEM' : 'SHOWS',
sortType: dropDownTabRef.current === 0 ? 'NEW' : 'MOST POPULAR',
contextName: LOG_CONTEXT_NAME.CATEGORY,
messageId: LOG_MESSAGE_ID.CONTENTCLICK,
};
@@ -359,8 +319,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
// 탑쇼 상품 로그
if (clickedTopShowPrd) {
const { prdtId, prdtNm, priceInfo } = clickedTopShowPrd;
const [regularPrice, discountPrice, , , discountRate] =
priceInfo?.split("|") || [];
const [regularPrice, discountPrice, , , discountRate] = priceInfo?.split('|') || [];
sendLog({
...params,
category: categoryShowInfos?.catNm,
@@ -387,8 +346,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
// 아이템 로그
if (clickedItem) {
const { prdtNm, priceInfo, brndNm, prdtId, patnrId } = clickedItem;
const [regularPrice, discountPrice, , , discountRate] =
priceInfo?.split("|") || [];
const [regularPrice, discountPrice, , , discountRate] = priceInfo?.split('|') || [];
sendLog({
...params,
category: categoryItemInfos?.catNm,
@@ -428,7 +386,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
cbChangePageRef.current(0, true, false);
}
setContainerLastFocusedElement(null, [SpotlightIds.CATEGORY_CONTENTS_BOX]);
Spotlight.focus("tab-" + tab);
Spotlight.focus('tab-' + tab);
}, [tab]);
//onScroll* event can't use Callback
@@ -437,14 +395,9 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
tabRef.current === INDEX_SHOWS
? SpotlightIds.SHOW_CONTENTS_BOX
: SpotlightIds.CATEGORY_CONTENTS_BOX;
const targetKey =
tabRef.current === INDEX_SHOWS
? "categoryShowInfos"
: "categoryItemInfos";
const targetKey = tabRef.current === INDEX_SHOWS ? 'categoryShowInfos' : 'categoryItemInfos';
const targetData = categoryDatasRef.current[targetKey];
const subKey = targetData?.subCatItemList
? "subCatItemList"
: "subCatShowList";
const subKey = targetData?.subCatItemList ? 'subCatItemList' : 'subCatShowList';
const spottableDescendants = getSpottableDescendants(targetContentsBoxId);
const visibleIndexes = [];
@@ -473,40 +426,33 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
}, []);
const showGotoTopButton = useMemo(() => {
const targetData =
tab === INDEX_SHOWS ? categoryShowInfos : categoryItemInfos;
const subKey = targetData?.subCatItemList
? "subCatItemList"
: "subCatShowList";
const targetData = tab === INDEX_SHOWS ? categoryShowInfos : categoryItemInfos;
const subKey = targetData?.subCatItemList ? 'subCatItemList' : 'subCatShowList';
if (!targetData || !targetData[subKey] || targetData.total < 5) {
return false;
}
return subKey === "subCatShowList"
return subKey === 'subCatShowList'
? targetData[subKey].length + 1 >= targetData.total
: targetData[subKey].length >= targetData.total;
}, [tab, categoryShowInfos, categoryItemInfos]);
const itemCountNumbers = useMemo(() => {
if (categoryItemInfos && tab === INDEX_ITEM) {
return categoryItemInfos.subCatItemList.length > 0
? categoryItemInfos.total
: "0";
return categoryItemInfos.subCatItemList.length > 0 ? categoryItemInfos.total : '0';
} else if (categoryShowInfos && tab === INDEX_SHOWS) {
return categoryShowInfos.subCatShowList.length > 0
? categoryShowInfos.total
: "0";
return categoryShowInfos.subCatShowList.length > 0 ? categoryShowInfos.total : '0';
} else {
return;
}
}, [categoryItemInfos, categoryShowInfos, tab]);
useEffect(() => {
const c = document.getElementById("floatLayer");
const c = document.getElementById('floatLayer');
c.classList.add("category_dropdown");
c.classList.add('category_dropdown');
return () => {
c.classList.remove("category_dropdown");
c.classList.remove('category_dropdown');
};
}, []);
@@ -521,7 +467,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
<TBody className={css.tBody} scrollable={false}>
<TVerticalPagenator
className={css.tVerticalPagenator}
spotlightId={"category_verticalPagenator"}
spotlightId={'category_verticalPagenator'}
defaultContainerId={panelInfo?.focusedContainerId}
onScrollStop={onScrollStop}
onFocusedContainerId={onFocusedContainerId}
@@ -530,7 +476,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
deleteFocus={true}
>
<Container
spotlightId={"categorypanel_top_point_area"}
spotlightId={'categorypanel_top_point_area'}
data-wheel-point={true}
className={css.cateContainer}
>
@@ -570,9 +516,7 @@ const CategoryPanel = ({ panelInfo, isOnTop, spotlightId }) => {
</TabContainer>
)}
{tab === INDEX_ITEM && <ItemContents onClick={handleItemClick} />}
{tab === INDEX_SHOWS && (
<ShowContents onClick={handleItemClick} />
)}
{tab === INDEX_SHOWS && <ShowContents onClick={handleItemClick} />}
</Container>
{showGotoTopButton && (
<TButton