[저스트포유] 포유

- 카테고리,베스트,파풀러쇼에서 임의 상품에 포유 마크 붙이도록처리.
 - 포유가 붙어 있는 상품을 store에 저장하여 포유페이지에서 노출되도록 변경.
This commit is contained in:
junghoon86.park
2025-09-24 09:43:40 +09:00
parent 5a1209631c
commit adb32b4536
8 changed files with 303 additions and 96 deletions

View File

@@ -256,14 +256,19 @@ export const types = {
* 첫 번째 배너 상시 재생과 두 번째 배너 포커스 재생을 관리합니다.
*/
SET_BANNER_STATE: "SET_BANNER_STATE",
SET_BANNER_FOCUS: "SET_BANNER_FOCUS",
SET_BANNER_FOCUS: "SET_BANNER_FOCUS",
SET_BANNER_AVAILABILITY: "SET_BANNER_AVAILABILITY",
SET_BANNER_TRANSITION: "SET_BANNER_TRANSITION",
PAUSE_PLAYER_CONTROL: "PAUSE_PLAYER_CONTROL",
RESUME_PLAYER_CONTROL: "RESUME_PLAYER_CONTROL",
// 🔽 [추가] HomeBanner 동영상 포커스 정책 관리
SET_CURRENT_FOCUS_BANNER: "SET_CURRENT_FOCUS_BANNER",
UPDATE_VIDEO_POLICY: "UPDATE_VIDEO_POLICY",
UPDATE_VIDEO_POLICY: "UPDATE_VIDEO_POLICY",
SET_MODAL_BORDER: "SET_MODAL_BORDER",
SET_BANNER_VISIBILITY: "SET_BANNER_VISIBILITY",
// 🔽 [추가] JustForYou 상품 관리 부분
GET_RECENTLY_SAW_ITEM: "GET_RECENTLY_SAW_ITEM",
GET_LIKE_BRAND_PRODUCT: "GET_LIKE_BRAND_PRODUCT",
GET_MORE_TO_CONCIDER_AT_THIS_PRICE: "GET_MORE_TO_CONCIDER_AT_THIS_PRICE",
};

View File

@@ -0,0 +1,19 @@
import { types } from './actionTypes';
// 최근 본 아이템 관련 액션
export const getRecentlySawItem = (data) => ({
type: types.GET_RECENTLY_SAW_ITEM,
payload: data,
});
// 좋아하는 브랜드 제품 관련 액션
export const getLikeBrandProduct = (data) => ({
type: types.GET_LIKE_BRAND_PRODUCT,
payload: data,
});
// 이 가격대에서 고려할 만한 더 많은 제품 관련 액션
export const getMoreToConciderAtThisPrice = (data) => ({
type: types.GET_MORE_TO_CONCIDER_AT_THIS_PRICE,
payload: data,
});

View File

@@ -0,0 +1,38 @@
import { types } from '../actions/actionTypes';
// 초기 상태 정의
const initialState = {
recentlySawItems: [],
likeBrandProducts: [],
moreToConciderAtThisPrice: [],
};
const foryouReducer = (state = initialState, action) => {
switch (action.type) {
// 최근 본 아이템
case types.GET_RECENTLY_SAW_ITEM:
return {
...state,
recentlySawItems: action.payload,
};
// 좋아하는 브랜드 제품
case types.GET_LIKE_BRAND_PRODUCT:
return {
...state,
likeBrandProducts: action.payload,
};
// 이 가격대에서 고려할 만한 더 많은 제품
case types.GET_MORE_TO_CONCIDER_AT_THIS_PRICE:
return {
...state,
moreToConciderAtThisPrice: action.payload,
};
default:
return state;
}
};
export default foryouReducer;

View File

@@ -1,29 +1,35 @@
import { applyMiddleware, combineReducers, createStore } from "redux";
import thunk from "redux-thunk";
import {
applyMiddleware,
combineReducers,
createStore,
} from 'redux';
import thunk from 'redux-thunk';
import { appDataReducer } from "../reducers/appDataReducer";
import { billingReducer } from "../reducers/billingReducer";
import { brandReducer } from "../reducers/brandReducer";
import { cancelReducer } from "../reducers/cancelReducer";
import { cardReducer } from "../reducers/cardReducer";
import { checkoutReducer } from "../reducers/checkoutReducer";
import { commonReducer } from "../reducers/commonReducer";
import { couponReducer } from "../reducers/couponReducer";
import { deviceReducer } from "../reducers/deviceReducer";
import { empReducer } from "../reducers/empReducer";
import { eventReducer } from "../reducers/eventReducer";
import { homeReducer } from "../reducers/homeReducer";
import { localSettingsReducer } from "../reducers/localSettingsReducer";
import { mainReducer } from "../reducers/mainReducer";
import { myPageReducer } from "../reducers/myPageReducer";
import { onSaleReducer } from "../reducers/onSaleReducer";
import { orderReducer } from "../reducers/orderReducer";
import { panelsReducer } from "../reducers/panelReducer";
import { pinCodeReducer } from "../reducers/pinCodeReducer";
import { playReducer } from "../reducers/playReducer";
import { productReducer } from "../reducers/productReducer";
import { searchReducer } from "../reducers/searchReducer";
import { shippingReducer } from "../reducers/shippingReducer";
import { appDataReducer } from '../reducers/appDataReducer';
import { billingReducer } from '../reducers/billingReducer';
import { brandReducer } from '../reducers/brandReducer';
import { cancelReducer } from '../reducers/cancelReducer';
import { cardReducer } from '../reducers/cardReducer';
import { checkoutReducer } from '../reducers/checkoutReducer';
import { commonReducer } from '../reducers/commonReducer';
import { couponReducer } from '../reducers/couponReducer';
import { deviceReducer } from '../reducers/deviceReducer';
import { empReducer } from '../reducers/empReducer';
import { eventReducer } from '../reducers/eventReducer';
//foryou
import foryouReducer from '../reducers/forYouReducer';
import { homeReducer } from '../reducers/homeReducer';
import { localSettingsReducer } from '../reducers/localSettingsReducer';
import { mainReducer } from '../reducers/mainReducer';
import { myPageReducer } from '../reducers/myPageReducer';
import { onSaleReducer } from '../reducers/onSaleReducer';
import { orderReducer } from '../reducers/orderReducer';
import { panelsReducer } from '../reducers/panelReducer';
import { pinCodeReducer } from '../reducers/pinCodeReducer';
import { playReducer } from '../reducers/playReducer';
import { productReducer } from '../reducers/productReducer';
import { searchReducer } from '../reducers/searchReducer';
import { shippingReducer } from '../reducers/shippingReducer';
const rootReducer = combineReducers({
panels: panelsReducer,
@@ -49,6 +55,7 @@ const rootReducer = combineReducers({
cancel: cancelReducer,
pinCode: pinCodeReducer,
emp: empReducer,
foryou: foryouReducer,
});
export const store = createStore(rootReducer, applyMiddleware(thunk));

View File

@@ -1,25 +1,43 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from "@enact/spotlight";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import Spotlight from '@enact/spotlight';
import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { pushPanel, updatePanel } from "../../../actions/panelActions";
import SectionTitle from "../../../components/SectionTitle/SectionTitle";
import TItemCard from "../../../components/TItemCard/TItemCard";
import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo";
import {
pushPanel,
updatePanel,
} from '../../../actions/panelActions';
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
import Tag from '../../../components/TItemCard/Tag';
import TItemCard from '../../../components/TItemCard/TItemCard';
import TitemCardNew from '../../../components/TItemCard/TitemCard.new';
import TScroller from '../../../components/TScroller/TScroller';
import useScrollReset from '../../../hooks/useScrollReset';
import useScrollTo from '../../../hooks/useScrollTo';
import {
LOG_CONTEXT_NAME,
LOG_MESSAGE_ID,
panel_names,
} from "../../../utils/Config";
import { $L, scaleW } from "../../../utils/helperMethods";
import { SpotlightIds } from "../../../utils/SpotlightIds";
import css from "./BestSeller.module.less";
} from '../../../utils/Config';
import {
$L,
scaleW,
} from '../../../utils/helperMethods';
import { SpotlightIds } from '../../../utils/SpotlightIds';
import css from './BestSeller.module.less';
const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator(
@@ -156,6 +174,26 @@ const BestSeller = ({
}
}, [handleShelfFocus]);
const [bestSellerNewData, setBestSellerNewData] = useState([]);
const _randomProduct = useCallback(() => {
const randomChk = Math.round(Math.random()) === 0 ? false : true;
return randomChk;
}, []);
useEffect(() => {
setBestSellerNewData(
bestSellerDatas?.map((item) => ({
...item,
foryou: _randomProduct(),
}))
);
}, [bestSellerDatas]);
useEffect(() => {
console.log("###bestSellerNewData", bestSellerNewData);
}, [bestSellerNewData]);
return (
<Container
className={css.bestSellerWrap}
@@ -176,8 +214,8 @@ const BestSeller = ({
cbScrollTo={getScrollTo}
noScrollByWheel
>
{bestSellerDatas &&
bestSellerDatas.map(
{bestSellerNewData &&
bestSellerNewData.map(
(
{
prdtId,
@@ -190,6 +228,7 @@ const BestSeller = ({
brndNm,
patncNm,
catNm,
foryou,
},
itemIndex
) => {
@@ -197,12 +236,12 @@ const BestSeller = ({
rankOrd === 1
? rankOrd + "st"
: rankOrd === 2
? rankOrd + "nd"
: rankOrd === 3
? rankOrd + "rd"
: rankOrd + "th";
? rankOrd + "nd"
: rankOrd === 3
? rankOrd + "rd"
: rankOrd + "th";
return (
<TItemCard
<TitemCardNew
key={"subItem" + itemIndex}
contextName={LOG_CONTEXT_NAME.HOME}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
@@ -226,9 +265,11 @@ const BestSeller = ({
offerInfo={offerInfo}
spotlightId={"bestsellerItem" + itemIndex}
firstLabel={rankText}
label={itemIndex * 1 + 1 + " of " + bestSellerDatas.length}
label={itemIndex * 1 + 1 + " of " + bestSellerNewData.length}
lastLabel=" go to detail, button"
/>
>
{foryou === true && <Tag text={"For You"} />}
</TitemCardNew>
);
}
)}

View File

@@ -1,32 +1,47 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from "@enact/spotlight";
import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { getContainerId } from "@enact/spotlight/src/container";
import Spotlight from '@enact/spotlight';
import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { getContainerId } from '@enact/spotlight/src/container';
import { updateHomeInfo } from "../../../actions/homeActions";
import { pushPanel } from "../../../actions/panelActions";
import { startVideoPlayer } from "../../../actions/playActions";
import SectionTitle from "../../../components/SectionTitle/SectionTitle";
import { updateHomeInfo } from '../../../actions/homeActions';
import { pushPanel } from '../../../actions/panelActions';
import { startVideoPlayer } from '../../../actions/playActions';
import SectionTitle from '../../../components/SectionTitle/SectionTitle';
import Tag from '../../../components/TItemCard/Tag';
import TItemCard, {
IMAGETYPES,
TYPES,
} from "../../../components/TItemCard/TItemCard";
import TScroller from "../../../components/TScroller/TScroller";
import useScrollReset from "../../../hooks/useScrollReset";
import useScrollTo from "../../../hooks/useScrollTo";
} from '../../../components/TItemCard/TItemCard';
import TitemCardNew from '../../../components/TItemCard/TitemCard.new';
import TScroller from '../../../components/TScroller/TScroller';
import useScrollReset from '../../../hooks/useScrollReset';
import useScrollTo from '../../../hooks/useScrollTo';
import {
LOG_CONTEXT_NAME,
LOG_MESSAGE_ID,
panel_names,
} from "../../../utils/Config";
import { $L, scaleW } from "../../../utils/helperMethods";
import { SpotlightIds } from "../../../utils/SpotlightIds";
import { TEMPLATE_CODE_CONF } from "../HomePanel";
import css from "../PopularShow/PopularShow.module.less";
} from '../../../utils/Config';
import {
$L,
scaleW,
} from '../../../utils/helperMethods';
import { SpotlightIds } from '../../../utils/SpotlightIds';
import { TEMPLATE_CODE_CONF } from '../HomePanel';
import css from '../PopularShow/PopularShow.module.less';
const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator(
@@ -174,6 +189,22 @@ const PopularShow = ({
}
}, [handleShelfFocus]);
const [topInfosNewData, setTopInfosNewData] = useState([]);
const _randomProduct = useCallback(() => {
const randomChk = Math.round(Math.random()) === 0 ? false : true;
return randomChk;
}, []);
useEffect(() => {
setTopInfosNewData(
topInfos?.map((item) => ({
...item,
foryou: _randomProduct(),
}))
);
}, [topInfos]);
return (
<Container
className={css.popularShow}
@@ -194,8 +225,8 @@ const PopularShow = ({
cbScrollTo={getScrollTo}
noScrollByWheel
>
{topInfos &&
topInfos.map(
{topInfosNewData &&
topInfosNewData.map(
(
{
showId,
@@ -209,11 +240,12 @@ const PopularShow = ({
patncNm,
catCd,
showUrl,
foryou,
},
itemIndex
) => {
return (
<TItemCard
<TitemCardNew
key={showId}
contextName={LOG_CONTEXT_NAME.HOME}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
@@ -247,7 +279,9 @@ const PopularShow = ({
firstLabel={patncNm + " "}
label={itemIndex * 1 + 1 + " of " + topInfos.length}
lastLabel=" go to detail, button"
/>
>
{foryou === true && <Tag text={"For You"} />}
</TitemCardNew>
);
}
)}

View File

@@ -16,6 +16,7 @@ import {
import Spottable from '@enact/spotlight/Spottable';
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import { getRecentlySawItem } from '../../../actions/forYouActions';
import { sendLogCuration } from '../../../actions/logActions';
import { getSubCategory } from '../../../actions/mainActions';
import { pushPanel } from '../../../actions/panelActions';
@@ -77,6 +78,8 @@ export default memo(function SubCategory({
const [drawChk, setDrawChk] = useState(false);
const [firstChk, SetFirstChk] = useState(false);
const [categoryItemNewData, setCategoryItemNewData] = useState([]);
const nowMenuRef = usePrevious(nowMenu);
useEffect(() => {
@@ -219,6 +222,28 @@ export default memo(function SubCategory({
}
}, [handleShelfFocus]);
const _randomProduct = useCallback(() => {
const randomChk = Math.round(Math.random()) === 0 ? false : true;
return randomChk;
}, []);
useEffect(() => {
setCategoryItemNewData(
categoryItemInfos?.subCatItemList?.map((item) => ({
...item,
foryou: _randomProduct(),
}))
);
}, [categoryItemInfos?.subCatItemList]);
useEffect(() => {
dispatch(
getRecentlySawItem(
categoryItemNewData.filter((item) => item.foryou === true)
)
);
}, [categoryItemNewData, dispatch]);
return (
<Container
spotlightId={spotlightId}
@@ -242,7 +267,7 @@ export default memo(function SubCategory({
cbScrollTo={getScrollTo}
noScrollByWheel
>
{categoryItemInfos &&
{/* {categoryItemInfos &&
categoryItemInfos?.subCatItemList.map(
(
{
@@ -288,14 +313,65 @@ export default memo(function SubCategory({
}
lastLabel=" go to detail, button"
>
{/* 조건수식 필요함. */}
<Tag text={"For You"} />
{/* //조건수식 필요함. */}
</TItemCardNew>
);
}
)} 원본 보관*/}
{categoryItemNewData &&
categoryItemNewData.map(
(
{
prdtId,
imgUrl,
priceInfo,
prdtNm,
patnrId,
offerInfo,
brndNm,
patncNm,
foryou,
},
itemIndex
) => {
return (
<TItemCardNew
key={"subItem" + itemIndex}
contextName={LOG_CONTEXT_NAME.HOME}
messageId={LOG_MESSAGE_ID.SHELF_CLICK}
catNm={categoryItemInfos.catNm}
order={itemIndex + 1}
shelfId={spotlightId}
shelfLocation={shelfLocation}
shelfTitle={shelfTitle}
brandName={brndNm}
patnerName={patncNm}
imageAlt={prdtId}
imageSource={imgUrl}
priceInfo={priceInfo}
productName={prdtNm}
productId={prdtId}
onFocus={handleFocus(itemIndex)}
onBlur={handleBlur(itemIndex)}
onClick={handleCardClick(patnrId, prdtId)}
offerInfo={offerInfo}
data-catcd-num={currentLgCatCd}
data-catcd-nm={currentLgCatNm}
label={
itemIndex * 1 +
1 +
" of " +
categoryItemInfos?.subCatItemList.length
}
lastLabel=" go to detail, button"
>
{foryou === true && <Tag text={"For You"} />}
</TItemCardNew>
);
}
)}
{drawChk && (
<div className={css.addItem} onFocus={handleScrollRight}>
<SpottableComponent

View File

@@ -17,10 +17,6 @@ import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import background
from '../../../assets/images/JustForYouPanel/background_new.png';
import {
sendLogGNB,
sendLogTotalRecommend,
} from '../../actions/logActions';
import {
popPanel,
pushPanel,
@@ -38,13 +34,7 @@ import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator';
import TVirtualGridList
from '../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../hooks/useScrollTo';
import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from '../../utils/Config';
import { panel_names } from '../../utils/Config';
import { $L } from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds';
import THeaderCustom from '../DetailPanel/components/THeaderCustom';
@@ -65,10 +55,7 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
const dispatch = useDispatch();
const [showButton, setShowButton] = useState("showButton", true);
const cbChangePageRef = useRef(null);
const currentSentMenuRef = useRef(null);
const focusedContainerIdRef = useRef(panelInfo?.focusedContainerId);
const { getScrollTo } = useScrollTo();
const recentlyItem = useSelector((state) => state.foryou.recentlySawItems);
const STRING_CONF = {
LIKE_ITEMS: "Like items you recently saw",
@@ -166,7 +153,7 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
/>
<CustomImage src={background} animationSpeed="none" />
</div>
{recentItems && recentItems?.length > 0 && (
{recentlyItem && recentlyItem?.length > 0 && (
<ListContainer
className={classNames(
css.itemsContainer,
@@ -176,13 +163,13 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
data-wheel-point
>
<SectionTitle
title={`${$L(STRING_CONF.LIKE_ITEMS)} (${recentItems.length})`}
title={`${$L(STRING_CONF.LIKE_ITEMS)} (${recentlyItem.length})`}
data-title-index="JFYTitle"
/>
<div className={css.itemList}>
<TVirtualGridList
dataSize={recentItems.length}
dataSize={recentlyItem.length}
itemWidth={324}
itemHeight={438}
spacing={18}