diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js index a62e7509..f0d86f2f 100644 --- a/com.twin.app.shoptime/src/actions/actionTypes.js +++ b/com.twin.app.shoptime/src/actions/actionTypes.js @@ -130,6 +130,7 @@ export const types = { GET_BRAND_CREATORS_INFO: 'GET_BRAND_CREATORS_INFO', GET_BRAND_SHOWROOM: 'GET_BRAND_SHOWROOM', GET_BRAND_RECENTLY_AIRED: 'GET_BRAND_RECENTLY_AIRED', + GET_BRAND_SHOP_BY_SHOW: 'GET_BRAND_SHOP_BY_SHOW', SET_BRAND_LIVE_CHANNEL_UPCOMING: 'SET_BRAND_LIVE_CHANNEL_UPCOMING', SET_BRAND_CHAN_INFO: 'SET_BRAND_CHAN_INFO', RESET_BRAND_STATE: 'RESET_BRAND_STATE', diff --git a/com.twin.app.shoptime/src/actions/brandActions.js b/com.twin.app.shoptime/src/actions/brandActions.js index fb1051e9..bd1c7f02 100644 --- a/com.twin.app.shoptime/src/actions/brandActions.js +++ b/com.twin.app.shoptime/src/actions/brandActions.js @@ -37,10 +37,12 @@ export const getBrandList = () => (dispatch, getState) => { export const getBrandLayoutInfo = (props) => (dispatch, getState) => { const { patnrId } = props; + console.log("[getBrandLayoutInfo] Called - patnrId:", patnrId); + dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } })); const onSuccess = (response) => { - // dlog("getBrandLayoutInfo onSuccess ", response.data); + console.log("[getBrandLayoutInfo] onSuccess - patnrId:", patnrId, "data:", response.data.data); dispatch({ type: types.GET_BRAND_LAYOUT_INFO, @@ -53,6 +55,7 @@ export const getBrandLayoutInfo = (props) => (dispatch, getState) => { }; const onFail = (error) => { + console.log("[getBrandLayoutInfo] onFail - patnrId:", patnrId, "error:", error); derror('getBrandLayoutInfo onFail ', error); dispatch(changeAppStatus({ showLoadingPanel: { show: false } })); }; @@ -336,10 +339,12 @@ export const getBrandCategoryProductInfo = (props) => (dispatch, getState) => { export const getBrandBestSeller = (props) => (dispatch, getState) => { const { patnrId } = props; + console.log("[getBrandBestSeller] Called - patnrId:", patnrId); + dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } })); const onSuccess = (response) => { - // dlog("getBrandBestSeller onSuccess ", response.data); + console.log("[getBrandBestSeller] onSuccess - patnrId:", patnrId, "data:", response.data.data); dispatch({ type: types.GET_BRAND_BEST_SELLER, @@ -352,6 +357,7 @@ export const getBrandBestSeller = (props) => (dispatch, getState) => { }; const onFail = (error) => { + console.log("[getBrandBestSeller] onFail - patnrId:", patnrId, "error:", error); derror('getBrandBestSeller onFail ', error); dispatch(changeAppStatus({ showLoadingPanel: { show: false } })); }; @@ -386,6 +392,39 @@ export const getBrandShowroom = (props) => (dispatch, getState) => { TAxios(dispatch, getState, 'get', URLS.GET_BRAND_SHOWROOM, { patnrId }, {}, onSuccess, onFail); }; +// Featured Brands SHOP BY SHOW 정보 조회 IF-LGSP-376 +export const getBrandShopByShow = (props) => (dispatch, getState) => { + const { patnrId, contsId } = props; + + console.log("[getBrandShopByShow] Called - patnrId:", patnrId, "contsId:", contsId); + + dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } })); + + const onSuccess = (response) => { + console.log("[getBrandShopByShow] onSuccess - patnrId:", patnrId, "data:", response.data.data); + + dispatch({ + type: types.GET_BRAND_SHOP_BY_SHOW, + payload: { + data: response.data.data, + }, + }); + + dispatch(changeAppStatus({ showLoadingPanel: { show: false } })); + }; + + const onFail = (error) => { + console.log("[getBrandShopByShow] onFail - patnrId:", patnrId, "error:", error); + derror('getBrandShopByShow onFail ', error); + dispatch(changeAppStatus({ showLoadingPanel: { show: false } })); + }; + + // patnrId: 필수, contsId: 선택 + const params = contsId ? { patnrId, contsId } : { patnrId }; + + TAxios(dispatch, getState, 'get', URLS.GET_BRAND_SHOP_BY_SHOW, params, {}, onSuccess, onFail); +}; + // Featured Brands Recently Aired 조회 IF-LGSP-373 export const getBrandRecentlyAired = (props) => (dispatch, getState) => { const { patnrId } = props; diff --git a/com.twin.app.shoptime/src/api/apiConfig.js b/com.twin.app.shoptime/src/api/apiConfig.js index 71fbec14..4937b57e 100644 --- a/com.twin.app.shoptime/src/api/apiConfig.js +++ b/com.twin.app.shoptime/src/api/apiConfig.js @@ -55,6 +55,7 @@ export const URLS = { GET_BRAND_CREATORS_INFO: "/lgsp/v1/brand/creators.lge", GET_BRAND_SHOWROOM: "/lgsp/v1/brand/showroom.lge", GET_BRAND_RECENTLY_AIRED: "/lgsp/v1/brand/recently/aired.lge", + GET_BRAND_SHOP_BY_SHOW: "/lgsp/v1/brand/shopByShow.lge", //on-sale controller GET_ON_SALE_INFO: "/lgsp/v1/onsale/onsale.lge", diff --git a/com.twin.app.shoptime/src/reducers/brandReducer.js b/com.twin.app.shoptime/src/reducers/brandReducer.js index 90ef5b3f..0bd6a236 100644 --- a/com.twin.app.shoptime/src/reducers/brandReducer.js +++ b/com.twin.app.shoptime/src/reducers/brandReducer.js @@ -44,6 +44,10 @@ const initialState = { brandRecentlyAiredData: { data: {}, }, + + brandShopByShowData: { + data: {}, + }, }; export const brandReducer = (state = initialState, action) => { @@ -155,6 +159,12 @@ export const brandReducer = (state = initialState, action) => { brandRecentlyAiredData: action.payload, }; + case types.GET_BRAND_SHOP_BY_SHOW: + return { + ...state, + brandShopByShowData: action.payload, + }; + case types.SET_BRAND_LIVE_CHANNEL_UPCOMING: return { ...state, diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBestSeller/FeaturedBestSellerList/FeaturedBestSellerList.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBestSeller/FeaturedBestSellerList/FeaturedBestSellerList.jsx index e6cd90a1..3779e532 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBestSeller/FeaturedBestSellerList/FeaturedBestSellerList.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBestSeller/FeaturedBestSellerList/FeaturedBestSellerList.jsx @@ -60,6 +60,10 @@ export default function FeaturedBestSellerList({ const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo); + console.log("[FeaturedBestSellerList] Mounted - selectedPatnrId:", selectedPatnrId); + console.log("[FeaturedBestSellerList] brandBestSellerInfo:", brandBestSellerInfo); + console.log("[FeaturedBestSellerList] Data count:", brandBestSellerInfo?.length || 0); + const cursorVisible = useSelector( (state) => state.common.appStatus.cursorVisible ); @@ -155,6 +159,9 @@ export default function FeaturedBestSellerList({ lgCatNm, euEnrgLblInfos, } = brandBestSellerInfo[index]; + + console.log("[FeaturedBestSellerList] renderItem - index:", index, "patnrId:", patnrId, "rankOrd:", rankOrd, "prdtNm:", prdtNm); + const rankText = rankOrd === 1 ? rankOrd + "st," diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx index 72320b97..49e397f9 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx @@ -23,6 +23,7 @@ import { getBrandLiveChannelInfo, getBrandRecommendedShowInfo, getBrandSeriesInfo, + getBrandShopByShow, getBrandShowroom, getBrandTSVInfo, } from "../../actions/brandActions"; @@ -63,6 +64,7 @@ import LiveChannels from "./LiveChannels/LiveChannels"; import QuickMenu from "./QuickMenu/QuickMenu"; import RecommendedShows from "./RecommendedShows/RecommendedShows"; import Series from "./Series/Series"; +import ShopByShow from "./ShopByShow/ShopByShow"; import Showroom from "./Showroom/Showroom"; import TodaysDeals from "./TodaysDeals/TodaysDeals"; import UpComing from "./UpComing/UpComing"; @@ -90,6 +92,7 @@ const TEMPLATE_CODE_CONF = { SERIES: "BRD00107", CATEGORY: "BRD00108", SHOWROOM: "BRD00109", + NBCU: "BRD00110", }; const DISPATCH_MAP = Object.freeze({ @@ -101,6 +104,7 @@ const DISPATCH_MAP = Object.freeze({ [TEMPLATE_CODE_CONF.SERIES]: getBrandSeriesInfo, [TEMPLATE_CODE_CONF.CATEGORY]: getBrandCategoryInfo, [TEMPLATE_CODE_CONF.SHOWROOM]: getBrandShowroom, + [TEMPLATE_CODE_CONF.NBCU]: getBrandShopByShow, }); const TOP_MARGIN = 36; @@ -263,6 +267,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { const brandShowroomInfo = useSelector( (state) => state.brand.brandShowroomData.data.brandShowroomInfo ); + const brandShopByShowContsList = useSelector( + (state) => state.brand.brandShopByShowData.data.brandShopByShowContsList + ); + const brandShopByShowContsInfo = useSelector( + (state) => state.brand.brandShopByShowData.data.brandShopByShowContsInfo + ); const [displayTopButton, setDisplayTopButton] = useState(false); const [focusedContainerId, setFocusedContainerId] = useState(null); @@ -412,9 +422,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { ); const renderPageItem = useCallback(() => { + console.log("[FeaturedBrandsPanel] renderPageItem - sortedBrandLayoutInfo length:", sortedBrandLayoutInfo.length); + console.log("[FeaturedBrandsPanel] renderPageItem - sortedBrandLayoutInfo items:", sortedBrandLayoutInfo.map(el => el.shptmBrndOptTpCd)); return ( <> {sortedBrandLayoutInfo.map((el, idx) => { + console.log("[FeaturedBrandsPanel] Processing template code:", el.shptmBrndOptTpCd); switch (el.shptmBrndOptTpCd) { case TEMPLATE_CODE_CONF.LIVE_CHANNELS: { return ( @@ -485,6 +498,10 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { } case TEMPLATE_CODE_CONF.BEST_SELLER: { + console.log("[FeaturedBrandsPanel] BEST_SELLER - patnrId:", selectedPatnrId); + console.log("[FeaturedBrandsPanel] BEST_SELLER - hasTemplateCode:", hasTemplateCodeWithValue(sortedBrandLayoutInfo, TEMPLATE_CODE_CONF.BEST_SELLER)); + console.log("[FeaturedBrandsPanel] BEST_SELLER - shouldRender:", shouldRenderComponent(brandBestSellerInfo)); + console.log("[FeaturedBrandsPanel] BEST_SELLER - data:", brandBestSellerInfo); return ( {hasTemplateCodeWithValue( @@ -492,15 +509,18 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { TEMPLATE_CODE_CONF.BEST_SELLER ) && shouldRenderComponent(brandBestSellerInfo) && ( - + <> + {console.log("[FeaturedBrandsPanel] Rendering FeaturedBestSeller for patnrId:", selectedPatnrId)} + + )} ); @@ -650,6 +670,36 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { ); } + + case TEMPLATE_CODE_CONF.NBCU: { + console.log("[FeaturedBrandsPanel] NBCU - patnrId:", selectedPatnrId); + console.log("[FeaturedBrandsPanel] NBCU - hasTemplateCode:", hasTemplateCodeWithValue(sortedBrandLayoutInfo, TEMPLATE_CODE_CONF.NBCU)); + console.log("[FeaturedBrandsPanel] NBCU - shouldRender:", shouldRenderComponent(brandShopByShowContsList)); + console.log("[FeaturedBrandsPanel] NBCU - data:", brandShopByShowContsList); + return ( + + {hasTemplateCodeWithValue( + sortedBrandLayoutInfo, + TEMPLATE_CODE_CONF.NBCU + ) && + shouldRenderComponent(brandShopByShowContsList) && ( + <> + {console.log("[FeaturedBrandsPanel] Rendering ShopByShow for patnrId:", selectedPatnrId)} + + + )} + + ); + } } })} @@ -668,6 +718,8 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { brandSeriesGroupInfo, brandSeriesInfo, brandShowroomInfo, + brandShopByShowContsList, + brandShopByShowContsInfo, brandTsvInfo, fromGNB, fromQuickMenu, @@ -711,6 +763,7 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { // effect: layout information fetching due to partner id change useEffect(() => { if (!fromDetail) { + console.log("[FeaturedBrandsPanel] Layout Info Effect - patnrId:", panelInfo?.patnrId); dispatch({ type: types.RESET_BRAND_LAYOUT_INFO }); dispatch(getBrandLayoutInfo({ patnrId: panelInfo?.patnrId })); setIsInitialFocusOccurred(false); @@ -733,9 +786,12 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { // effect: data fetching based on brandLayoutInfo and selectedPatnrId useEffect(() => { if (sortedBrandLayoutInfo && selectedPatnrId) { + console.log("[FeaturedBrandsPanel] Data Fetching Effect - selectedPatnrId:", selectedPatnrId); + console.log("[FeaturedBrandsPanel] sortedBrandLayoutInfo:", sortedBrandLayoutInfo); Object.entries(DISPATCH_MAP) // .forEach(([templateCode, action]) => { if (hasTemplateCodeWithValue(sortedBrandLayoutInfo, templateCode)) { + console.log("[FeaturedBrandsPanel] Fetching data for template:", templateCode, "patnrId:", selectedPatnrId); dispatch(action({ patnrId: selectedPatnrId })); } }); diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenuItem/QuickMenuItem.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenuItem/QuickMenuItem.jsx index 5f44f03a..76647df5 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenuItem/QuickMenuItem.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/QuickMenu/QuickMenuItem/QuickMenuItem.jsx @@ -49,10 +49,13 @@ const QuickMenuItem = ({ }, [handleStopScrolling, itemIndex]); const handleClick = useCallback(() => { + console.log("[QuickMenuItem] Click - patnrId:", patnrId, "currentPatnrId:", selectedPatnrId ?? panelInfo?.patnrId); if (patnrId === (selectedPatnrId ?? panelInfo?.patnrId)) { + console.log("[QuickMenuItem] Already selected, returning"); return; } + console.log("[QuickMenuItem] Switching to patnrId:", patnrId); const from = "menu"; const name = panel_names.FEATURED_BRANDS_PANEL; diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.jsx new file mode 100644 index 00000000..cf5a7585 --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.jsx @@ -0,0 +1,86 @@ +import React, { memo, useCallback, useState } from "react"; + +import Spotlight from "@enact/spotlight"; +import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; + +import SectionTitle from "../../../components/SectionTitle/SectionTitle"; +import { $L } from "../../../utils/helperMethods"; +import css from "./ShopByShow.module.less"; +import ShopByShowList from "./ShopByShowList/ShopByShowList"; + +const STRING_CONF = { + SHOP_BY_SHOW: "SHOP BY SHOW", +}; + +const Container = SpotlightContainerDecorator( + { leaveFor: { right: "" }, enterTo: "last-focused" }, + "div" +); + +const ShopByShow = ({ + brandShopByShowContsList, + brandShopByShowContsInfo, + handleItemFocus, + order, + shelfOrder, + spotlightId, + selectedPatnrId, + shelfTitle, +}) => { + const [firstChk, setFirstChk] = useState(0); + + const _handleItemFocus = useCallback(() => { + if (handleItemFocus) handleItemFocus(spotlightId, shelfOrder); + + const c = Spotlight.getCurrent(); + if (firstChk === 0) { + if (c) { + let cAriaLabel = c.getAttribute("aria-label"); + if (cAriaLabel) { + cAriaLabel = "shop-by-show, Heading1," + cAriaLabel; + c.setAttribute("aria-label", cAriaLabel); + } + } + setFirstChk(1); + } else if (firstChk === 1) { + if (c) { + let cAriaLabel = c.getAttribute("aria-label"); + if (cAriaLabel) { + const newcAriaLabel = cAriaLabel.replace( + "shop-by-show, Heading1,", + "" + ); + c.setAttribute("aria-label", newcAriaLabel); + } + } + } else { + return; + } + }, [handleItemFocus, firstChk]); + + return ( + + + + + ); +}; + +export default memo(ShopByShow); diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.module.less new file mode 100644 index 00000000..37ace4d3 --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShow.module.less @@ -0,0 +1,12 @@ +@import "../../../style/CommonStyle.module.less"; +@import "../../../style/utils.module.less"; + +.container { + width: 100%; + margin-bottom: 58px; + + > h2 { + margin-bottom: 24px; + padding-left: 60px; + } +} diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.jsx new file mode 100644 index 00000000..4de18f32 --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.jsx @@ -0,0 +1,144 @@ +import React, { memo, useCallback } from "react"; + +import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; + +import TItemCardNew, { removeDotAndColon } from "../../../../../components/TItemCard/TItemCard.new"; +import TVirtualGridList from "../../../../../components/TVirtualGridList/TVirtualGridList"; +import { + LOG_CONTEXT_NAME, + LOG_MESSAGE_ID, + panel_names, +} from "../../../../../utils/Config"; +import { getTranslate3dValueByDirection } from "../../../../../utils/helperMethods"; +import Spotlight from "@enact/spotlight"; +import { pushPanel, updatePanel } from "../../../../../actions/panelActions"; +import { useDispatch } from "react-redux"; +import css from "./ShopByShowContents.module.less"; + +const Container = SpotlightContainerDecorator( + { leaveFor: { right: "" }, enterTo: null }, + "div" +); + +const ShopByShowContents = memo(({ + clctId, + clctNm, + brandProductInfos, + contentsIndex, + handleItemFocus, + selectedPatnrId, + spotlightId, + shelfOrder, + shelfTitle, + getScrollTo, +}) => { + const dispatch = useDispatch(); + + const handleClick = useCallback( + (patnrId, prdtId) => (e) => { + const tItemCard = e.currentTarget; + const lastFocusedTarget = Spotlight.getCurrent(); + const lastFocusedTargetId = lastFocusedTarget?.getAttribute("data-spotlight-id"); + const xContainer = tItemCard?.parentNode?.parentNode; + + if (lastFocusedTargetId && xContainer) { + const section = "shop-by-show"; + const x = getTranslate3dValueByDirection(xContainer); + + dispatch( + updatePanel({ + name: panel_names.FEATURED_BRANDS_PANEL, + panelInfo: { + lastFocusedTargetId, + patnrId, + section, + x, + }, + }) + ); + } + + dispatch( + pushPanel({ + name: panel_names.DETAIL_PANEL, + panelInfo: { patnrId, prdtId }, + }) + ); + }, + [dispatch] + ); + + const handleFocus = useCallback(() => { + if (handleItemFocus) { + handleItemFocus(); + } + }, [handleItemFocus]); + + return ( + +

{clctNm}

+ {brandProductInfos && brandProductInfos.length > 0 && ( + { + if (!brandProductInfos || !brandProductInfos[index]) { + return null; + } + + const product = brandProductInfos[index]; + const { + prdtImgUrl, + prdtOfferId, + patnrId = "21", + prdtNm, + prdtId, + prdtPrice, + patncNm, + brndNm, + lgCatNm, + euEnrgLblInfos, + } = product; + + return ( + + ); + }} + /> + )} +
+ ); +}); + +export default ShopByShowContents; diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.module.less new file mode 100644 index 00000000..e0fbf7b0 --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowContents/ShopByShowContents.module.less @@ -0,0 +1,32 @@ +@import "../../../../../style/CommonStyle.module.less"; +@import "../../../../../style/utils.module.less"; + +.container { + padding-left: 60px; + margin-bottom: 12px; + + > h3 { + position: relative; + .font(@fontFamily: @arialFontBold, @fontSize: 36px); + color: @COLOR_GRAY08; + margin-bottom: 22px; + } + + > div:nth-child(2) { + .flex(@justifyCenter: flex-start); + .size(@w: 100%, @h: auto); + } +} + +.container:last-child { + margin-bottom: 0; +} + +.tVirtualGridList { + padding-right: 18px; + .size(@h: 438px); + + > div:nth-child(3) { + right: -18px; + } +} diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.jsx new file mode 100644 index 00000000..13c1deff --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.jsx @@ -0,0 +1,134 @@ +import React, { + useCallback, + useEffect, + useRef, + useState, +} from 'react'; + +import { + useDispatch, + useSelector, +} from 'react-redux'; + +import { Job } from '@enact/core/util'; +import SpotlightContainerDecorator + from '@enact/spotlight/SpotlightContainerDecorator'; +import { + getContainerId, + setContainerLastFocusedElement, +} from '@enact/spotlight/src/container'; + +import { getBrandShopByShow } from '../../../../actions/brandActions'; +import useScrollTo from '../../../../hooks/useScrollTo'; +import css from './ShopByShowList.module.less'; +import ShopByShowNav from './ShopByShowNav/ShopByShowNav'; +import ShopByShowContents from './ShopByShowContents/ShopByShowContents'; + +const Container = SpotlightContainerDecorator( + { leaveFor: { right: "" }, enterTo: "last-focused" }, + "div" +); + +export default function ShopByShowList({ + brandShopByShowContsList, + brandShopByShowContsInfo, + handleItemFocus, + selectedPatnrId, + spotlightId, + shelfTitle, + shelfOrder, +}) { + const { getScrollTo, scrollLeft } = useScrollTo(); + const dispatch = useDispatch(); + const panelInfo = useSelector((state) => state.panels.panels[0]?.panelInfo); + const scrollLeftJob = useRef(new Job((func) => func(), 0)); + const [selectedClctId, setSelectedClctId] = useState(null); + + const brandShopByShowClctInfos = brandShopByShowContsInfo?.brandShopByShowClctInfos || []; + + const filteredCollections = selectedClctId + ? brandShopByShowClctInfos.filter(({ clctId }) => clctId === selectedClctId) + : brandShopByShowClctInfos; + + useEffect(() => { + if (panelInfo?.section !== "shop-by-show" || !panelInfo?.x) { + return; + } + + const scrollLeftJobValue = scrollLeftJob.current; + const { x } = panelInfo; + + scrollLeftJobValue.start(() => scrollLeft({ x })); + + return () => scrollLeftJobValue.stop(); + }, [panelInfo, scrollLeft]); + + useEffect(() => { + const containerId = "shop-by-show-list-id"; + const container = document.getElementById(containerId); + + if (container) { + const childrenId = getContainerId(container?.children[0].children[0]); + + if (childrenId) { + setContainerLastFocusedElement(null, [containerId, childrenId]); + } + } + + scrollLeft(); + }, [scrollLeft, selectedPatnrId]); + + const handleTabClick = useCallback((contsId) => { + dispatch(getBrandShopByShow({ patnrId: selectedPatnrId, contsId })); + }, [selectedPatnrId, dispatch]); + + const _handleItemFocus = useCallback(() => { + if (handleItemFocus) { + handleItemFocus(); + } + }, [handleItemFocus]); + + return ( + +
+ {brandShopByShowContsList && brandShopByShowContsList.map((item) => ( + + ))} +
+ + + + {filteredCollections.map((collection, collIdx) => ( + + ))} +
+ ); +} diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.module.less new file mode 100644 index 00000000..f59b43ed --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowList.module.less @@ -0,0 +1,59 @@ +@import "../../../../style/CommonStyle.module.less"; +@import "../../../../style/utils.module.less"; + +.container { + display: flex; + flex-direction: column; + position: relative; + width: 100%; +} + +.tabsContainer { + display: flex; + gap: 12px; + padding: 0 60px 24px 60px; + overflow-x: auto; + overflow-y: hidden; + margin-bottom: 12px; + + &::-webkit-scrollbar { + height: 4px; + } + + &::-webkit-scrollbar-track { + background: transparent; + } + + &::-webkit-scrollbar-thumb { + background: #ccc; + border-radius: 2px; + } +} + +.tabButton { + flex-shrink: 0; + padding: 8px 16px; + background-color: transparent; + border: 1px solid #ccc; + border-radius: 4px; + color: #666; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; + + &:hover { + border-color: #000; + color: #000; + } + + &.active { + background-color: #000; + border-color: #000; + color: #fff; + } + + &:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.2); + } +} diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.jsx new file mode 100644 index 00000000..22bef593 --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.jsx @@ -0,0 +1,82 @@ +import React, { memo, useCallback } from "react"; + +import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; + +import TButton, { TYPES } from "../../../../../components/TButton/TButton"; +import TScroller from "../../../../../components/TScroller/TScroller"; +import useScrollTo from "../../../../../hooks/useScrollTo"; +import css from "./ShopByShowNav.module.less"; + +const Container = SpotlightContainerDecorator( + { leaveFor: { right: "" }, enterTo: "last-focused" }, + "nav" +); + +const STRING_CONF = { + ALL: "ALL", +}; + +export default memo(function ShopByShowNav({ + brandShopByShowClctInfos, + handleItemFocus, + selectedClctId, + setSelectedClctId, +}) { + const { getScrollTo, scrollLeft } = useScrollTo(); + + const handleClick = useCallback( + (clctId, clctNm) => () => { + setSelectedClctId(clctId); + }, + [setSelectedClctId] + ); + + const handleFocus = useCallback(() => { + if (handleItemFocus) { + handleItemFocus(); + } + }, [handleItemFocus]); + + const selectedText = !selectedClctId ? "Selected " : ""; + const allLabelText = selectedText + "ALL 1 of " + (brandShopByShowClctInfos?.length + 1 || 1); + + return ( + + + + + + ); +}); diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.module.less new file mode 100644 index 00000000..dd1be18e --- /dev/null +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/ShopByShow/ShopByShowList/ShopByShowNav/ShopByShowNav.module.less @@ -0,0 +1,55 @@ +@import "../../../../../style/CommonStyle.module.less"; +@import "../../../../../style/utils.module.less"; + +.nav { + position: relative; + .size(@w: 100%, @h: 162px); + margin-bottom: 30px; + padding-right: 1px; + z-index: 2; + + > div:nth-child(1) { + .size(@w: inherit, @h: inherit); + } + + &::before { + position: absolute; + top: 0; + left: 0; + .size(@w: 100%, @h: 144px); + background-color: #ddd; + content: ""; + } + + ul { + display: flex; + align-items: center; + height: inherit; + padding-left: 60px; + border-bottom: 18px solid transparent; + + li { + flex: none; + margin-right: 12px; + + > div { + position: relative; + + &.selected { + &::before { + position: absolute; + bottom: -62px; + left: 50%; + transform: translateX(-50%); + .size(@w: 0, @h: 0); + border-top: 18px solid #ddd; + border-right: 18px solid transparent; + border-bottom: 18px solid transparent; + border-left: 18px solid transparent; + content: ""; + } + } + } + } + } +}