[foryou] foryou패널 노출관련 처리 및 에너지라벨 노출 처리#1

- homebanner에서 justforyou dispatch처리.
 - 포유부분 노출 처리
 - TITEMCARD videoshow 부분 에너지라벨 미노출 변경.
 - foryou패널에서 10000자리 넘어가는부분에 대한 스타일 수정 및 금액에따른 스타일변경 처리 변경.
 - 롤링 배너쪽 미작업
This commit is contained in:
junghoon86.park
2025-10-20 13:27:49 +09:00
parent f70e2b1a21
commit 6c0830f3f8
5 changed files with 354 additions and 216 deletions

View File

@@ -202,10 +202,10 @@
&.labelBox { &.labelBox {
width: calc(100% - 60px); width: calc(100% - 60px);
> p { > p {
font-size: 27px; font-size: 25px;
&.priceInfo { &.priceInfo {
> span { > span {
font-size: 16px; font-size: 15px;
} }
} }
} }

View File

@@ -1,69 +1,92 @@
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'; import React, {
memo,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux'; import {
useDispatch,
useSelector,
} from 'react-redux';
import Spotlight from '@enact/spotlight'; import Spotlight from '@enact/spotlight';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
import defaultLogoImg from '../../../assets/images/ic-tab-partners-default@3x.png';
import defaultimgHorizontal from '../../../assets/images/img-thumb-empty-hor@3x.png';
import defaultImageItem from '../../../assets/images/img-thumb-empty-product@3x.png';
import defaultimgVertical from '../../../assets/images/img-thumb-empty-ver@3x.png';
import IcLiveShow from '../../../assets/images/tag/tag-liveshow.png';
// 🧪 테스트용 에너지 라벨 (실제 PDF 변환 테스트) // 🧪 테스트용 에너지 라벨 (실제 PDF 변환 테스트)
import testEnergyIconA from '../../../assets/images/energyLabel/labelgradeA.png'; import testEnergyIconA
import testEnergyIconB from '../../../assets/images/energyLabel/labelgradeB.png'; from '../../../assets/images/energyLabel/labelgradeA.png';
import testEnergyIconC from '../../../assets/images/energyLabel/labelgradeC.png'; import testEnergyIconB
from '../../../assets/images/energyLabel/labelgradeB.png';
import testEnergyIconC
from '../../../assets/images/energyLabel/labelgradeC.png';
import defaultLogoImg
from '../../../assets/images/ic-tab-partners-default@3x.png';
import defaultimgHorizontal
from '../../../assets/images/img-thumb-empty-hor@3x.png';
import defaultImageItem
from '../../../assets/images/img-thumb-empty-product@3x.png';
import defaultimgVertical
from '../../../assets/images/img-thumb-empty-ver@3x.png';
import IcLiveShow from '../../../assets/images/tag/tag-liveshow.png';
import testEnergyPdf from '../../../assets/mock/EnergyLabelSample.pdf'; import testEnergyPdf from '../../../assets/mock/EnergyLabelSample.pdf';
import { setHidePopup, setShowPopup } from '../../actions/commonActions'; import {
setHidePopup,
setShowPopup,
} from '../../actions/commonActions';
import { import {
clearConvertedImage, clearConvertedImage,
convertPdfToImage,
convertMultiplePdfs, convertMultiplePdfs,
convertPdfToImage,
} from '../../actions/convertActions'; } from '../../actions/convertActions';
import { sendLogTotalRecommend } from '../../actions/logActions'; import { sendLogTotalRecommend } from '../../actions/logActions';
import usePriceInfo from '../../hooks/usePriceInfo'; import usePriceInfo from '../../hooks/usePriceInfo';
import * as Config from '../../utils/Config'; import * as Config from '../../utils/Config';
import { $L, getQRCodeUrl, removeSpecificTags } from '../../utils/helperMethods'; import {
$L,
getQRCodeUrl,
removeSpecificTags,
} from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds'; import { SpotlightIds } from '../../utils/SpotlightIds';
import CustomImage from '../CustomImage/CustomImage'; import CustomImage from '../CustomImage/CustomImage';
import TPopUp from '../TPopUp/TPopUp'; import TPopUp from '../TPopUp/TPopUp';
import css from './TItemCard.module.less'; import css from './TItemCard.module.less';
const SpottableComponent = Spottable('div'); const SpottableComponent = Spottable("div");
const SpottableTemp = Spottable('div'); const SpottableTemp = Spottable("div");
const TYPES = { const TYPES = {
vertical: 'vertical', vertical: "vertical",
horizontal: 'horizontal', horizontal: "horizontal",
videoShow: 'videoShow', videoShow: "videoShow",
}; };
const IMAGETYPES = { const IMAGETYPES = {
imgHorizontal: 'imgHorizontal', imgHorizontal: "imgHorizontal",
imgVertical: 'imgVertical', imgVertical: "imgVertical",
}; };
const STRING_CONF = { const STRING_CONF = {
SOLD_OUT: 'SOLD OUT', SOLD_OUT: "SOLD OUT",
ENERGY_LOADING: 'Loading energy label...', ENERGY_LOADING: "Loading energy label...",
ENERGY_ERROR: 'Failed to load energy label', ENERGY_ERROR: "Failed to load energy label",
}; };
const ENERGY_LABEL_MODE = { const ENERGY_LABEL_MODE = {
API_ONLY: 'API_ONLY', API_ONLY: "API_ONLY",
WITH_MOCK: 'WITH_MOCK', WITH_MOCK: "WITH_MOCK",
}; };
const CURRENT_ENERGY_LABEL_MODE = ENERGY_LABEL_MODE.WITH_MOCK; const CURRENT_ENERGY_LABEL_MODE = ENERGY_LABEL_MODE.WITH_MOCK;
export const removeDotAndColon = (string) => { export const removeDotAndColon = (string) => {
return /[.:]/.test(string) ? string.replace(/[.:]/g, '') : string; return /[.:]/.test(string) ? string.replace(/[.:]/g, "") : string;
}; };
const parsePrice = (price) => { const parsePrice = (price) => {
return parseFloat(price?.replace(/[^0-9.-]+/g, '') || '0'); return parseFloat(price?.replace(/[^0-9.-]+/g, "") || "0");
}; };
const generateMockEnergyLabels = (productId) => { const generateMockEnergyLabels = (productId) => {
@@ -77,11 +100,13 @@ const generateMockEnergyLabels = (productId) => {
return Math.abs(hash); return Math.abs(hash);
}; };
const seed = productId ? hashCode(productId) : Math.floor(Math.random() * 1000); const seed = productId
? hashCode(productId)
: Math.floor(Math.random() * 1000);
const randomCount = (seed % 3) + 1; const randomCount = (seed % 3) + 1;
const testIcons = [testEnergyIconA, testEnergyIconB, testEnergyIconC]; const testIcons = [testEnergyIconA, testEnergyIconB, testEnergyIconC];
const testGrades = ['A (TEST)', 'B (TEST)', 'C (TEST)']; const testGrades = ["A (TEST)", "B (TEST)", "C (TEST)"];
return Array.from({ length: randomCount }, (_, index) => ({ return Array.from({ length: randomCount }, (_, index) => ({
enrgLblUrl: testEnergyPdf, enrgLblUrl: testEnergyPdf,
@@ -144,8 +169,12 @@ export default memo(function TItemCardNew({
const [currentPdfUrl, setCurrentPdfUrl] = useState(null); const [currentPdfUrl, setCurrentPdfUrl] = useState(null);
const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd); const countryCode = useSelector((state) => state.common.httpHeader.cntry_cd);
const cursorVisible = useSelector((state) => state.common.appStatus.cursorVisible); const cursorVisible = useSelector(
const { activePopup, popupVisible } = useSelector((state) => state.common.popup); (state) => state.common.appStatus.cursorVisible
);
const { activePopup, popupVisible } = useSelector(
(state) => state.common.popup
);
const convert = useSelector((state) => state.convert); const convert = useSelector((state) => state.convert);
@@ -156,7 +185,7 @@ export default memo(function TItemCardNew({
const currentBlobUrl = convert?.convertedImage; const currentBlobUrl = convert?.convertedImage;
return () => { return () => {
if (currentBlobUrl && currentBlobUrl.startsWith('blob:')) { if (currentBlobUrl && currentBlobUrl.startsWith("blob:")) {
URL.revokeObjectURL(currentBlobUrl); URL.revokeObjectURL(currentBlobUrl);
} }
}; };
@@ -164,9 +193,11 @@ export default memo(function TItemCardNew({
useEffect(() => { useEffect(() => {
if (!imageSource) { if (!imageSource) {
if (type === 'videoShow') { if (type === "videoShow") {
setDefaultImage( setDefaultImage(
imgType === IMAGETYPES.imgHorizontal ? defaultimgHorizontal : defaultimgVertical imgType === IMAGETYPES.imgHorizontal
? defaultimgHorizontal
: defaultimgVertical
); );
} else { } else {
setDefaultImage(defaultImageItem); setDefaultImage(defaultImageItem);
@@ -194,7 +225,8 @@ export default memo(function TItemCardNew({
// } // }
// }, [euEnrgLblInfos, productId, dispatch]); // }, [euEnrgLblInfos, productId, dispatch]);
const { originalPrice, discountedPrice, discountRate } = usePriceInfo(priceInfo) || {}; const { originalPrice, discountedPrice, discountRate } =
usePriceInfo(priceInfo) || {};
const _onBlur = useCallback(() => { const _onBlur = useCallback(() => {
if (onBlur) { if (onBlur) {
@@ -282,23 +314,25 @@ export default memo(function TItemCardNew({
); );
const ariaLabel = useMemo(() => { const ariaLabel = useMemo(() => {
const soldOutText = soldoutFlag === 'Y' ? 'Sold Out ' : ''; const soldOutText = soldoutFlag === "Y" ? "Sold Out " : "";
const firstLabelText = firstLabel ? `${firstLabel} ` : ''; const firstLabelText = firstLabel ? `${firstLabel} ` : "";
const discountLabel = discountRate ? `${discountRate} discount, ` : ''; const discountLabel = discountRate ? `${discountRate} discount, ` : "";
const discountPriceLabel = discountRate ? `Sale price ${discountedPrice}, ` : ''; const discountPriceLabel = discountRate
? `Sale price ${discountedPrice}, `
: "";
const parsedPrice = parsePrice(originalPrice); const parsedPrice = parsePrice(originalPrice);
const priceLabel = const priceLabel =
parsedPrice === 0 parsedPrice === 0
? offerInfo ? offerInfo
? ` ${offerInfo}` ? ` ${offerInfo}`
: '' : ""
: originalPrice : originalPrice
? ` Original price ${originalPrice}, ` ? ` Original price ${originalPrice}, `
: ''; : "";
const productLabel = label || ''; const productLabel = label || "";
const lastLabelText = lastLabel || ''; const lastLabelText = lastLabel || "";
return `${soldOutText}${firstLabelText}${discountLabel}${productName}${discountPriceLabel}${priceLabel}${productLabel}${lastLabelText}`; return `${soldOutText}${firstLabelText}${discountLabel}${productName}${discountPriceLabel}${priceLabel}${productLabel}${lastLabelText}`;
}, [ }, [
@@ -319,7 +353,7 @@ export default memo(function TItemCardNew({
}, [productName]); }, [productName]);
const handleClosePopup = useCallback(() => { const handleClosePopup = useCallback(() => {
if (convert?.convertedImage && convert.convertedImage.startsWith('blob:')) { if (convert?.convertedImage && convert.convertedImage.startsWith("blob:")) {
URL.revokeObjectURL(convert.convertedImage); URL.revokeObjectURL(convert.convertedImage);
} }
@@ -338,10 +372,10 @@ export default memo(function TItemCardNew({
setCurrentPdfUrl(pdfUrl); setCurrentPdfUrl(pdfUrl);
// PNG 이미지는 직접 표시 // PNG 이미지는 직접 표시
if (pdfUrl.endsWith('.png')) { if (pdfUrl.endsWith(".png")) {
// console.log(`📸 [EnergyLabel] Displaying PNG directly:`, pdfUrl); // console.log(`📸 [EnergyLabel] Displaying PNG directly:`, pdfUrl);
dispatch({ dispatch({
type: 'CONVERT_PDF_TO_IMAGE_SUCCESS', type: "CONVERT_PDF_TO_IMAGE_SUCCESS",
payload: { pdfUrl, imageUrl: pdfUrl }, payload: { pdfUrl, imageUrl: pdfUrl },
}); });
dispatch(setShowPopup(Config.ACTIVE_POPUP.energyPopup)); dispatch(setShowPopup(Config.ACTIVE_POPUP.energyPopup));
@@ -358,7 +392,10 @@ export default memo(function TItemCardNew({
pdfUrl, pdfUrl,
(error, imageUrl) => { (error, imageUrl) => {
if (error) { if (error) {
console.error('[EnergyLabel] 최종 변환 실패:', error.message || error); console.error(
"[EnergyLabel] 최종 변환 실패:",
error.message || error
);
// 실패해도 팝업은 열어서 에러 메시지 표시 // 실패해도 팝업은 열어서 에러 메시지 표시
dispatch(setShowPopup(Config.ACTIVE_POPUP.energyPopup)); dispatch(setShowPopup(Config.ACTIVE_POPUP.energyPopup));
setTimeout(() => { setTimeout(() => {
@@ -386,13 +423,15 @@ export default memo(function TItemCardNew({
className={classNames( className={classNames(
css[type], css[type],
nonPosition && css.nonPosition, nonPosition && css.nonPosition,
type === 'videoShow' && css[imgType], type === "videoShow" && css[imgType],
className && className className && className
)} )}
onBlur={_onBlur} onBlur={_onBlur}
onClick={_onClick} onClick={_onClick}
onFocus={_onFocus} onFocus={_onFocus}
spotlightId={spotlightId ?? 'spotlightId-' + removeDotAndColon(productId)} spotlightId={
spotlightId ?? "spotlightId-" + removeDotAndColon(productId)
}
aria-label={ariaLabel} aria-label={ariaLabel}
role="button" role="button"
{...rest} {...rest}
@@ -403,7 +442,7 @@ export default memo(function TItemCardNew({
delay={0} delay={0}
src={imageSource} src={imageSource}
fallbackSrc={ fallbackSrc={
type === 'videoShow' type === "videoShow"
? imgType === IMAGETYPES.imgHorizontal ? imgType === IMAGETYPES.imgHorizontal
? defaultimgHorizontal ? defaultimgHorizontal
: defaultimgVertical : defaultimgVertical
@@ -411,24 +450,32 @@ export default memo(function TItemCardNew({
} }
onError={addDefaultImg} onError={addDefaultImg}
/> />
{priceInfo && discountRate && Number(discountRate.replace('%', '')) > 4 && ( {priceInfo &&
<span className={css.discount}>{discountRate}</span> discountRate &&
)} Number(discountRate.replace("%", "")) > 4 && (
{soldoutFlag && soldoutFlag === 'Y' && ( <span className={css.discount}>{discountRate}</span>
<div className={classNames(css.soldout, countryCode === 'DE' && css.de)}> )}
{soldoutFlag && soldoutFlag === "Y" && (
<div
className={classNames(
css.soldout,
countryCode === "DE" && css.de
)}
>
{$L(STRING_CONF.SOLD_OUT)} {$L(STRING_CONF.SOLD_OUT)}
</div> </div>
)} )}
{isLive && <img className={css.liveTag} src={IcLiveShow} alt="Live Show" />} {isLive && (
<img className={css.liveTag} src={IcLiveShow} alt="Live Show" />
)}
</div> </div>
<div className={css.flexBox}> <div className={css.flexBox}>
<div <div
className={classNames( className={classNames(
css.descWrap, css.descWrap,
catNm && css.hstNmWrap, catNm && css.hstNmWrap,
euEnrgLblInfos && parsePrice(originPrice) >= 1000 &&
euEnrgLblInfos.length > 0 && parsePrice(dcPrice) >= 1000 &&
euEnrgLblInfos[0]?.enrgLblIcnUrl !== null &&
css.labelBox css.labelBox
)} )}
> >
@@ -447,14 +494,17 @@ export default memo(function TItemCardNew({
{children} {children}
{priceInfo ? ( {priceInfo ? (
<p className={css.priceInfo}> <p className={css.priceInfo}>
{parseFloat(originalPrice?.replace(/[^0-9.-]+/g, '') || '0') === 0 ? ( {parseFloat(originalPrice?.replace(/[^0-9.-]+/g, "") || "0") ===
0 ? (
<strong>{offerInfo}</strong> <strong>{offerInfo}</strong>
) : discountRate ? ( ) : discountRate ? (
discountedPrice discountedPrice
) : ( ) : (
originalPrice originalPrice
)} )}
{discountRate && <span className={css.originalPrice}>{originalPrice}</span>} {discountRate && (
<span className={css.originalPrice}>{originalPrice}</span>
)}
</p> </p>
) : ( ) : (
<p className={css.offerInfo}>{offerInfo}</p> <p className={css.offerInfo}>{offerInfo}</p>
@@ -463,7 +513,9 @@ export default memo(function TItemCardNew({
{originPrice && ( {originPrice && (
<p className={css.priceInfo}> <p className={css.priceInfo}>
{dcPrice ? dcPrice : originPrice} {dcPrice ? dcPrice : originPrice}
{dcPrice && <span className={css.originalPrice}>{originPrice}</span>} {dcPrice && (
<span className={css.originalPrice}>{originPrice}</span>
)}
</p> </p>
)} )}
</div> </div>
@@ -480,7 +532,9 @@ export default memo(function TItemCardNew({
return null; return null;
} }
energyLabels = euEnrgLblInfos; energyLabels = euEnrgLblInfos;
} else if (CURRENT_ENERGY_LABEL_MODE === ENERGY_LABEL_MODE.WITH_MOCK) { } else if (
CURRENT_ENERGY_LABEL_MODE === ENERGY_LABEL_MODE.WITH_MOCK
) {
if (hasValidApiData) { if (hasValidApiData) {
energyLabels = euEnrgLblInfos; energyLabels = euEnrgLblInfos;
} else { } else {
@@ -492,22 +546,23 @@ export default memo(function TItemCardNew({
return ( return (
<div className={css.labelImgBox}> <div className={css.labelImgBox}>
{energyLabels {type !== "videoShow" &&
.filter((info, index) => index < 3) energyLabels
.map((info, index) => ( .filter((info, index) => index < 3)
<SpottableTemp .map((info, index) => (
key={index} <SpottableTemp
spotlightDisabled={Boolean(!cursorVisible)} key={index}
onClick={(e) => onEnergyClick(e, info.enrgLblUrl)} spotlightDisabled={Boolean(!cursorVisible)}
aria-label={`Energy Efficiency ${info.enrgGrade || ''}`} onClick={(e) => onEnergyClick(e, info.enrgLblUrl)}
> aria-label={`Energy Efficiency ${info.enrgGrade || ""}`}
<CustomImage >
alt={`Energy Label ${info.enrgGrade || index + 1}`} <CustomImage
delay={0} alt={`Energy Label ${info.enrgGrade || index + 1}`}
src={info.enrgLblIcnUrl} delay={0}
/> src={info.enrgLblIcnUrl}
</SpottableTemp> />
))} </SpottableTemp>
))}
</div> </div>
); );
})()} })()}
@@ -546,11 +601,11 @@ export default memo(function TItemCardNew({
return ( return (
<TPopUp <TPopUp
kind="energyPopup" kind="energyPopup"
title={$L('Energy Efficiency')} title={$L("Energy Efficiency")}
hasText hasText
open={popupVisible} open={popupVisible}
hasButton hasButton
button1Text={$L('CLOSE')} button1Text={$L("CLOSE")}
onClose={handleClosePopup} onClose={handleClosePopup}
> >
<div className={css.energyPopupContent}> <div className={css.energyPopupContent}>
@@ -566,21 +621,33 @@ export default memo(function TItemCardNew({
) : convert.error ? ( ) : convert.error ? (
<div> <div>
<p>{$L(STRING_CONF.ENERGY_ERROR)}</p> <p>{$L(STRING_CONF.ENERGY_ERROR)}</p>
<p style={{ fontSize: '0.8em', marginTop: '10px' }}> <p style={{ fontSize: "0.8em", marginTop: "10px" }}>
{convert.error?.message || String(convert.error)} {convert.error?.message || String(convert.error)}
</p> </p>
</div> </div>
) : convert.isConverting ? ( ) : convert.isConverting ? (
<div> <div>
<p>{$L(STRING_CONF.ENERGY_LOADING)}</p> <p>{$L(STRING_CONF.ENERGY_LOADING)}</p>
<p style={{ fontSize: '0.8em', marginTop: '10px', color: '#999' }}> <p
style={{
fontSize: "0.8em",
marginTop: "10px",
color: "#999",
}}
>
Converting PDF to image... (attempt in progress) Converting PDF to image... (attempt in progress)
</p> </p>
</div> </div>
) : ( ) : (
<div> <div>
<p>{$L(STRING_CONF.ENERGY_ERROR)}</p> <p>{$L(STRING_CONF.ENERGY_ERROR)}</p>
<p style={{ fontSize: '0.8em', marginTop: '10px', color: '#999' }}> <p
style={{
fontSize: "0.8em",
marginTop: "10px",
color: "#999",
}}
>
Unknown state - no image or error Unknown state - no image or error
</p> </p>
</div> </div>
@@ -590,7 +657,13 @@ export default memo(function TItemCardNew({
) : ( ) : (
<div> <div>
<p>{$L(STRING_CONF.ENERGY_ERROR)}</p> <p>{$L(STRING_CONF.ENERGY_ERROR)}</p>
<p style={{ fontSize: '0.8em', marginTop: '10px', color: '#999' }}> <p
style={{
fontSize: "0.8em",
marginTop: "10px",
color: "#999",
}}
>
Convert reducer state not found Convert reducer state not found
</p> </p>
</div> </div>

View File

@@ -13,7 +13,6 @@ import {
// }; // };
const initialState = { const initialState = {
justForYouInfo: {}, justForYouInfo: {},
shelfInfos: {},
}; };
// const foryouReducer = (state = initialState, action) => { // const foryouReducer = (state = initialState, action) => {
@@ -48,13 +47,8 @@ const justForYouInfo = curry((state, action) =>
set("justForYouInfo", get("payload", action), state) set("justForYouInfo", get("payload", action), state)
); );
const shelfInfos = curry((state, action) =>
set("shelfInfos", get("payload", action), state)
);
const handlers = { const handlers = {
[types.GET_JUSTFORYOU_INFO]: justForYouInfo, [types.GET_JUSTFORYOU_INFO]: justForYouInfo,
[types.GET_SHELFINFOS]: shelfInfos,
}; };
export const foryouReducer = (state = initialState, action = {}) => { export const foryouReducer = (state = initialState, action = {}) => {

View File

@@ -1,11 +1,21 @@
// src/views/HomePanel/HomeBanner/HomeBanner.jsx // src/views/HomePanel/HomeBanner/HomeBanner.jsx
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 Spotlight from '@enact/spotlight';
import { SpotlightContainerDecorator } from '@enact/spotlight/SpotlightContainerDecorator'; import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable'; import Spottable from '@enact/spotlight/Spottable';
import { types } from '../../../actions/actionTypes'; import { types } from '../../../actions/actionTypes';
@@ -17,9 +27,16 @@ import {
setOptionalTermsUserDecision, setOptionalTermsUserDecision,
updateOptionalTermsAgreement, updateOptionalTermsAgreement,
} from '../../../actions/commonActions'; } from '../../../actions/commonActions';
import { fetchCurrentUserHomeTerms, setDefaultFocus } from '../../../actions/homeActions'; import { getJustForYouInfo } from '../../../actions/forYouActions';
import {
fetchCurrentUserHomeTerms,
setDefaultFocus,
} from '../../../actions/homeActions';
import { setMyPageTermsAgree } from '../../../actions/myPageActions'; import { setMyPageTermsAgree } from '../../../actions/myPageActions';
import { popPanel, pushPanel } from '../../../actions/panelActions'; import {
popPanel,
pushPanel,
} from '../../../actions/panelActions';
import { import {
releasePlayControl, releasePlayControl,
requestPlayControl, requestPlayControl,
@@ -29,6 +46,11 @@ import CustomImage from '../../../components/CustomImage/CustomImage';
// import TButtonScroller from "../../../components/TButtonScroller/TButtonScroller"; // import TButtonScroller from "../../../components/TButtonScroller/TButtonScroller";
import OptionalConfirm from '../../../components/Optional/OptionalConfirm'; import OptionalConfirm from '../../../components/Optional/OptionalConfirm';
import TNewPopUp from '../../../components/TPopUp/TNewPopUp'; import TNewPopUp from '../../../components/TPopUp/TNewPopUp';
import {
useFocusHistory,
} from '../../../hooks/useFocusHistory/useFocusHistory';
import { useVideoPlay } from '../../../hooks/useVideoPlay/useVideoPlay';
import { useVideoMove } from '../../../hooks/useVideoTransition/useVideoMove';
import { panel_names } from '../../../utils/Config'; import { panel_names } from '../../../utils/Config';
import { $L } from '../../../utils/helperMethods'; import { $L } from '../../../utils/helperMethods';
import css from './HomeBanner.module.less'; import css from './HomeBanner.module.less';
@@ -40,38 +62,53 @@ import Random from './RandomUnit';
import RandomUnitNew from './RandomUnit'; import RandomUnitNew from './RandomUnit';
import Rolling from './RollingUnit'; import Rolling from './RollingUnit';
import SimpleVideoContainer from './SimpleVideoContainer'; import SimpleVideoContainer from './SimpleVideoContainer';
import { useFocusHistory } from '../../../hooks/useFocusHistory/useFocusHistory';
import { useVideoPlay } from '../../../hooks/useVideoPlay/useVideoPlay';
import { useVideoMove } from '../../../hooks/useVideoTransition/useVideoMove';
const SpottableComponent = Spottable('div'); const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); const Container = SpotlightContainerDecorator(
const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); { enterTo: "last-focused" },
"div"
);
const ContainerBasic = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, handleShelfFocus }) { export default function HomeBanner({
firstSpot,
spotlightId,
handleItemFocus,
handleShelfFocus,
}) {
const dispatch = useDispatch(); const dispatch = useDispatch();
const homeTopDisplayInfo = useSelector((state) => state.home.homeTopDisplayInfo); useEffect(() => {
dispatch(getJustForYouInfo());
}, [dispatch]);
const homeTopDisplayInfo = useSelector(
(state) => state.home.homeTopDisplayInfo
);
const bannerDataList = useSelector((state) => state.home.bannerData?.bannerInfos); const bannerDataList = useSelector(
(state) => state.home.bannerData?.bannerInfos
);
const popupVisible = useSelector((state) => state.common.popup.popupVisible); const popupVisible = useSelector((state) => state.common.popup.popupVisible);
// 🔽 useFocusHistory - 경량화된 범용 포커스 히스토리 // 🔽 useFocusHistory - 경량화된 범용 포커스 히스토리
const focusHistory = useFocusHistory({ const focusHistory = useFocusHistory({
enableLogging: true, enableLogging: true,
useGlobalState: true, useGlobalState: true,
logPrefix: '[HomeBanner-Focus]', logPrefix: "[HomeBanner-Focus]",
}); });
// 🔽 useVideoPlay - 동영상 재생 제어 // 🔽 useVideoPlay - 동영상 재생 제어
const videoPlay = useVideoPlay({ const videoPlay = useVideoPlay({
enableLogging: true, enableLogging: true,
logPrefix: '[HomeBanner-VideoPlay]', logPrefix: "[HomeBanner-VideoPlay]",
}); });
// 🔽 useVideoMove - 포커스 전환 기반 동영상 제어 // 🔽 useVideoMove - 포커스 전환 기반 동영상 제어
const { playByTransition, cleanup } = useVideoMove({ const { playByTransition, cleanup } = useVideoMove({
enableLogging: true, enableLogging: true,
logPrefix: '[HomeBanner-VideoMove]', logPrefix: "[HomeBanner-VideoMove]",
}); });
const selectTemplate = useMemo(() => { const selectTemplate = useMemo(() => {
@@ -86,11 +123,19 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
const termsData = useSelector((state) => state.home.termsData); const termsData = useSelector((state) => state.home.termsData);
const termsIdMap = useSelector((state) => state.home.termsIdMap); const termsIdMap = useSelector((state) => state.home.termsIdMap);
const optionalTermsAvailable = useSelector((state) => state.home.optionalTermsAvailable); const optionalTermsAvailable = useSelector(
(state) => state.home.optionalTermsAvailable
);
const optionalTermsData = useSelector((state) => { const optionalTermsData = useSelector((state) => {
// Chromium68 호환성을 위해 Optional Chaining 제거 // Chromium68 호환성을 위해 Optional Chaining 제거
if (state.home.termsData && state.home.termsData.data && state.home.termsData.data.terms) { if (
return state.home.termsData.data.terms.find((term) => term.trmsTpCd === 'MST00405'); state.home.termsData &&
state.home.termsData.data &&
state.home.termsData.data.terms
) {
return state.home.termsData.data.terms.find(
(term) => term.trmsTpCd === "MST00405"
);
} }
return null; return null;
}); });
@@ -98,22 +143,27 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
const isGnbOpened = useSelector((state) => state.common.isGnbOpened); const isGnbOpened = useSelector((state) => state.common.isGnbOpened);
const currentTermsFlag = useSelector((state) => state.common.termsFlag); const currentTermsFlag = useSelector((state) => state.common.termsFlag);
// 선택약관 동의여부 // 선택약관 동의여부
const introTermsAgree = useSelector((state) => state.common.termsFlag.optionalTerms); const introTermsAgree = useSelector(
(state) => state.common.termsFlag.optionalTerms
);
// 새로운 Redux 상태: 선택약관 팝업 플로우 관리 // 새로운 Redux 상태: 선택약관 팝업 플로우 관리
const optionalTermsPopupFlow = useSelector((state) => state.common.optionalTermsPopupFlow); const optionalTermsPopupFlow = useSelector(
(state) => state.common.optionalTermsPopupFlow
);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// 팝업표시 상태 // 팝업표시 상태
const [isOptionalConfirmVisible, setIsOptionalConfirmVisible] = useState(false); const [isOptionalConfirmVisible, setIsOptionalConfirmVisible] =
useState(false);
const [isOptionalTermsVisible, setIsOptionalTermsVisible] = useState(false); const [isOptionalTermsVisible, setIsOptionalTermsVisible] = useState(false);
const [optionalTermsAgreed, setOptionalTermsAgreed] = useState(false); const [optionalTermsAgreed, setOptionalTermsAgreed] = useState(false);
// 선택약관 팝업 표시 여부 =================================================== // 선택약관 팝업 표시 여부 ===================================================
const shouldShowOptionalTermsPopup = useMemo(() => { const shouldShowOptionalTermsPopup = useMemo(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Step 1: 상태 확인', { console.log("[HomeBanner] Step 1: 상태 확인", {
termsLoading, termsLoading,
isGnbOpened, isGnbOpened,
optionalTermsAvailable, optionalTermsAvailable,
@@ -123,8 +173,8 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
// 1. 기본 조건 확인 // 1. 기본 조건 확인
if (termsLoading || isGnbOpened || !optionalTermsAvailable) { if (termsLoading || isGnbOpened || !optionalTermsAvailable) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Early return: 기본 조건 불만족'); console.log("[HomeBanner] Early return: 기본 조건 불만족");
} }
return false; return false;
} }
@@ -135,35 +185,38 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
optionalTermsPopupFlow.userDecision || optionalTermsPopupFlow.userDecision ||
optionalTermsPopupFlow.agreedInSession optionalTermsPopupFlow.agreedInSession
) { ) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Early return: 이미 처리됨', optionalTermsPopupFlow); console.log(
"[HomeBanner] Early return: 이미 처리됨",
optionalTermsPopupFlow
);
} }
return false; return false;
} }
// 3. 서버 데이터 확인 // 3. 서버 데이터 확인
const terms = termsData && termsData.data && termsData.data.terms; const terms = termsData && termsData.data && termsData.data.terms;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Step 2: termsData 확인', terms); console.log("[HomeBanner] Step 2: termsData 확인", terms);
} }
if (!terms) { if (!terms) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Early return: terms가 존재하지 않음'); console.log("[HomeBanner] Early return: terms가 존재하지 않음");
} }
return false; return false;
} }
const optionalTerm = terms.find((term) => term.trmsTpCd === 'MST00405'); const optionalTerm = terms.find((term) => term.trmsTpCd === "MST00405");
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Step 3: optionalTerm 검색 결과', optionalTerm); console.log("[HomeBanner] Step 3: optionalTerm 검색 결과", optionalTerm);
} }
const result = optionalTerm const result = optionalTerm
? optionalTerm.trmsPopFlag === 'Y' && optionalTerm.trmsAgrFlag === 'N' ? optionalTerm.trmsPopFlag === "Y" && optionalTerm.trmsAgrFlag === "N"
: false; : false;
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] Step 4: 최종 결과', result); console.log("[HomeBanner] Step 4: 최종 결과", result);
} }
return result; return result;
}, [ }, [
@@ -177,67 +230,67 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
// 선택약관 팝업 표시 여부 =================================================== // 선택약관 팝업 표시 여부 ===================================================
const handleOptionalAgree = useCallback(() => { const handleOptionalAgree = useCallback(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] handleAgree Click'); console.log("[HomeBanner] handleAgree Click");
} }
if (!termsIdMap || Object.keys(termsIdMap).length === 0) { if (!termsIdMap || Object.keys(termsIdMap).length === 0) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.error('[HomeBanner] termsIdMap이 없습니다:', termsIdMap); console.error("[HomeBanner] termsIdMap이 없습니다:", termsIdMap);
} }
return; return;
} }
const requiredTermTypes = ['MST00401', 'MST00402', 'MST00405']; const requiredTermTypes = ["MST00401", "MST00402", "MST00405"];
const missingTerms = requiredTermTypes.filter((type) => !termsIdMap[type]); const missingTerms = requiredTermTypes.filter((type) => !termsIdMap[type]);
if (missingTerms.length > 0) { if (missingTerms.length > 0) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.error('[HomeBanner] 누락된 약관 타입:', missingTerms); console.error("[HomeBanner] 누락된 약관 타입:", missingTerms);
} }
return; return;
} }
const termsList = []; const termsList = [];
if (termsIdMap['MST00401']) { if (termsIdMap["MST00401"]) {
termsList.push(termsIdMap['MST00401']); // 개인정보처리방침 termsList.push(termsIdMap["MST00401"]); // 개인정보처리방침
} }
if (termsIdMap['MST00402']) { if (termsIdMap["MST00402"]) {
termsList.push(termsIdMap['MST00402']); // 이용약관 termsList.push(termsIdMap["MST00402"]); // 이용약관
} }
if (termsIdMap['MST00405']) { if (termsIdMap["MST00405"]) {
termsList.push(termsIdMap['MST00405']); // 선택약관 termsList.push(termsIdMap["MST00405"]); // 선택약관
} }
const notTermsList = []; const notTermsList = [];
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] 현재 termsIdMap:', termsIdMap); console.log("[HomeBanner] 현재 termsIdMap:", termsIdMap);
console.log('[HomeBanner] 약관 동의 API 호출 파라미터:', { console.log("[HomeBanner] 약관 동의 API 호출 파라미터:", {
termsList, termsList,
notTermsList, notTermsList,
}); });
} }
const callback = (response) => { const callback = (response) => {
if (response.retCode === '000' || response.retCode === 0) { if (response.retCode === "000" || response.retCode === 0) {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] 약관 동의 성공:', response); console.log("[HomeBanner] 약관 동의 성공:", response);
} }
// ✅ IntroPanel과 동일한 방식으로 Redux 상태 직접 업데이트 (API 호출 없이) // ✅ IntroPanel과 동일한 방식으로 Redux 상태 직접 업데이트 (API 호출 없이)
dispatch(updateOptionalTermsAgreement(true)); dispatch(updateOptionalTermsAgreement(true));
// 로컬 상태도 업데이트 (기존 로직 유지) // 로컬 상태도 업데이트 (기존 로직 유지)
setOptionalTermsAgreed(true); setOptionalTermsAgreed(true);
} else { } else {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.error('[HomeBanner] 약관 동의 실패:', response); console.error("[HomeBanner] 약관 동의 실패:", response);
} }
} }
}; };
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] 약관 동의 API 호출 payload:', { console.log("[HomeBanner] 약관 동의 API 호출 payload:", {
termsList, termsList,
notTermsList, notTermsList,
}); });
@@ -247,8 +300,8 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
}, [dispatch, termsIdMap]); }, [dispatch, termsIdMap]);
const handleOptionalTermsClick = useCallback(() => { const handleOptionalTermsClick = useCallback(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] 약관 자세히 보기 클릭'); console.log("[HomeBanner] 약관 자세히 보기 클릭");
} }
setIsOptionalConfirmVisible(false); setIsOptionalConfirmVisible(false);
setIsOptionalTermsVisible(true); setIsOptionalTermsVisible(true);
@@ -262,14 +315,14 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
type: types.GET_TERMS_AGREE_YN_SUCCESS, type: types.GET_TERMS_AGREE_YN_SUCCESS,
payload: { payload: {
...currentTermsFlag, ...currentTermsFlag,
optionalTerms: 'Y', optionalTerms: "Y",
}, },
}); });
}, [handleOptionalAgree]); }, [handleOptionalAgree]);
const handleOptionalDeclineClick = useCallback(() => { const handleOptionalDeclineClick = useCallback(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] 거절/다음에 하기 버튼 클릭'); console.log("[HomeBanner] 거절/다음에 하기 버튼 클릭");
} }
// ✅ 거절 상태 업데이트 // ✅ 거절 상태 업데이트
dispatch(updateOptionalTermsAgreement(false)); dispatch(updateOptionalTermsAgreement(false));
@@ -280,13 +333,13 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
const handleTermsPopupClosed = useCallback(() => { const handleTermsPopupClosed = useCallback(() => {
setIsOptionalTermsVisible(false); setIsOptionalTermsVisible(false);
setIsOptionalConfirmVisible(true); setIsOptionalConfirmVisible(true);
Spotlight.focus('optional-confirm-popup'); Spotlight.focus("optional-confirm-popup");
}, []); }, []);
// 선택약관 팝업 Agree // 선택약관 팝업 Agree
const handleTermsPopupAgree = useCallback(() => { const handleTermsPopupAgree = useCallback(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === "development") {
console.log('[HomeBanner] handleTermsPopupAgree'); console.log("[HomeBanner] handleTermsPopupAgree");
} }
handleOptionalAgree(); handleOptionalAgree();
setIsOptionalTermsVisible(false); setIsOptionalTermsVisible(false);
@@ -306,22 +359,24 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
const data = bannerDataList[i]; const data = bannerDataList[i];
let bannerDetailInfos = data.bannerDetailInfos; let bannerDetailInfos = data.bannerDetailInfos;
if (data.shptmDspyTpNm === 'Random') { if (data.shptmDspyTpNm === "Random") {
if ( if (
bannerDetailInfos[data.randomIndex].shptmBanrTpNm === 'LIVE' || bannerDetailInfos[data.randomIndex].shptmBanrTpNm === "LIVE" ||
bannerDetailInfos[data.randomIndex].shptmBanrTpNm === 'VOD' bannerDetailInfos[data.randomIndex].shptmBanrTpNm === "VOD"
) { ) {
targetIndex = i; targetIndex = i;
break; break;
} }
} else if ( } else if (
bannerDetailInfos.find((el) => el.shptmBanrTpNm === 'LIVE' || el.shptmBanrTpNm === 'VOD') bannerDetailInfos.find(
(el) => el.shptmBanrTpNm === "LIVE" || el.shptmBanrTpNm === "VOD"
)
) { ) {
targetIndex = i; targetIndex = i;
break; break;
} }
} }
return 'banner' + targetIndex; return "banner" + targetIndex;
} }
return null; return null;
@@ -336,8 +391,8 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
// 선택 약관 팝업을 띄워야 하는 경우 // 선택 약관 팝업을 띄워야 하는 경우
if (shouldShowOptionalTermsPopup && !isOptionalConfirmVisible) { if (shouldShowOptionalTermsPopup && !isOptionalConfirmVisible) {
console.log('shouldShowOptionalTermsPopup', shouldShowOptionalTermsPopup); console.log("shouldShowOptionalTermsPopup", shouldShowOptionalTermsPopup);
console.log('App.js optionalTermsConfirm 팝업 표시'); console.log("App.js optionalTermsConfirm 팝업 표시");
const timer = setTimeout(() => { const timer = setTimeout(() => {
setIsOptionalConfirmVisible(true); setIsOptionalConfirmVisible(true);
@@ -348,36 +403,41 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
// 컴포넌트 언마운트 시 타이머 클리어 // 컴포넌트 언마운트 시 타이머 클리어
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
}, [shouldShowOptionalTermsPopup, termsLoading, isOptionalConfirmVisible, dispatch]); }, [
shouldShowOptionalTermsPopup,
termsLoading,
isOptionalConfirmVisible,
dispatch,
]);
const renderItem = useCallback( const renderItem = useCallback(
(index, isHorizontal, videoPlayerable = false) => { (index, isHorizontal, videoPlayerable = false) => {
const data = bannerDataList?.[index] ?? {}; const data = bannerDataList?.[index] ?? {};
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined}> <div className={!isHorizontal ? css.imgBox : undefined}>
{data.shptmDspyTpNm === 'Rolling' ? ( {data.shptmDspyTpNm === "Rolling" ? (
<Rolling <Rolling
bannerData={data} bannerData={data}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
key={'banner' + index} key={"banner" + index}
spotlightId={'banner' + index} spotlightId={"banner" + index}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
videoPlayerable={videoPlayerable} videoPlayerable={videoPlayerable}
/> />
) : data.shptmDspyTpNm === 'Random' ? ( ) : data.shptmDspyTpNm === "Random" ? (
<Random <Random
bannerData={data} bannerData={data}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
key={'banner' + index} key={"banner" + index}
spotlightId={'banner' + index} spotlightId={"banner" + index}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
randomNumber={data.randomIndex} randomNumber={data.randomIndex}
videoPlayerable={videoPlayerable} videoPlayerable={videoPlayerable}
/> />
) : ( ) : (
<SpottableComponent spotlightId={'banner' + index}> <SpottableComponent spotlightId={"banner" + index}>
<CustomImage <CustomImage
delay={0} delay={0}
src={ src={
@@ -386,7 +446,9 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
: homeTopDisplayInfo.vtctpImgPath1 : homeTopDisplayInfo.vtctpImgPath1
} }
aria-label={ aria-label={
isHorizontal ? homeTopDisplayInfo.wdthtpImgNm1 : homeTopDisplayInfo.vtctpImgNm1 isHorizontal
? homeTopDisplayInfo.wdthtpImgNm1
: homeTopDisplayInfo.vtctpImgNm1
} }
/> />
</SpottableComponent> </SpottableComponent>
@@ -403,14 +465,14 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
const data = bannerDataList?.[index] ?? {}; const data = bannerDataList?.[index] ?? {};
// DSP00201 레이아웃의 두 번째 배너는 새로운 RandomUnitNew를 사용 // DSP00201 레이아웃의 두 번째 배너는 새로운 RandomUnitNew를 사용
if (selectTemplate === 'DSP00201' && index === 1) { if (selectTemplate === "DSP00201" && index === 1) {
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined}> <div className={!isHorizontal ? css.imgBox : undefined}>
<RandomUnitNew <RandomUnitNew
bannerData={data} bannerData={data}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
key={'banner' + index} key={"banner" + index}
spotlightId={'banner' + index} spotlightId={"banner" + index}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
randomNumber={data.randomIndex} randomNumber={data.randomIndex}
@@ -420,14 +482,14 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
} }
// 다른 타입의 유닛 렌더링 (예: RollingUnit) // 다른 타입의 유닛 렌더링 (예: RollingUnit)
if (data.shptmDspyTpNm === 'Rolling') { if (data.shptmDspyTpNm === "Rolling") {
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined}> <div className={!isHorizontal ? css.imgBox : undefined}>
<RollingUnit <RollingUnit
bannerData={data} bannerData={data}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
key={'banner' + index} key={"banner" + index}
spotlightId={'banner' + index} spotlightId={"banner" + index}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
/> />
@@ -436,8 +498,11 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
} }
// 기본 이미지만 있는 배너 등 다른 케이스 처리 // 기본 이미지만 있는 배너 등 다른 케이스 처리
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined} key={'banner' + index}> <div
<SpottableComponent spotlightId={'banner' + index}> className={!isHorizontal ? css.imgBox : undefined}
key={"banner" + index}
>
<SpottableComponent spotlightId={"banner" + index}>
{/* ... 정적 이미지 렌더링 로직 ... */} {/* ... 정적 이미지 렌더링 로직 ... */}
</SpottableComponent> </SpottableComponent>
</div> </div>
@@ -452,7 +517,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined}> <div className={!isHorizontal ? css.imgBox : undefined}>
<SimpleVideoContainer <SimpleVideoContainer
spotlightId={'banner' + index} // "banner0" spotlightId={"banner" + index} // "banner0"
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
/> />
@@ -467,7 +532,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
return ( return (
<div className={!isHorizontal ? css.imgBox : undefined}> <div className={!isHorizontal ? css.imgBox : undefined}>
<SimpleVideoContainer <SimpleVideoContainer
spotlightId={'banner' + index} spotlightId={"banner" + index}
isHorizontal={isHorizontal} isHorizontal={isHorizontal}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
/> />
@@ -478,12 +543,12 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
); );
useEffect(() => { useEffect(() => {
console.log('####introTermsAgree', introTermsAgree); console.log("####introTermsAgree", introTermsAgree);
}, [introTermsAgree]); }, [introTermsAgree]);
const renderLayout = useCallback(() => { const renderLayout = useCallback(() => {
switch (selectTemplate) { switch (selectTemplate) {
case 'DSP00201': { case "DSP00201": {
return ( return (
<> <>
<ContainerBasic className={css.smallBox}> <ContainerBasic className={css.smallBox}>
@@ -492,14 +557,14 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
</ContainerBasic> </ContainerBasic>
{renderItem(2, false, false)} {renderItem(2, false, false)}
{/* //NOTE - 약관 동의 여부 & 추후 API 따라 팝업 표시 여부 결정 */} {/* //NOTE - 약관 동의 여부 & 추후 API 따라 팝업 표시 여부 결정 */}
{introTermsAgree === 'Y' ? ( {introTermsAgree === "Y" ? (
<div className={css.imgBox}> <div className={css.imgBox}>
<JustForSwitchBanner <JustForSwitchBanner
renderItem={renderItem} renderItem={renderItem}
handleShelfFocus={_handleShelfFocus} handleShelfFocus={_handleShelfFocus}
handleItemFocus={_handleItemFocus} handleItemFocus={_handleItemFocus}
isHorizontal={false} isHorizontal={false}
spotlightId={'banner3'} spotlightId={"banner3"}
/> />
</div> </div>
) : ( ) : (
@@ -508,7 +573,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
</> </>
); );
} }
case 'DSP00202': { case "DSP00202": {
return ( return (
<> <>
{renderItem(0, false, false)} {renderItem(0, false, false)}
@@ -520,7 +585,7 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
</> </>
); );
} }
case 'DSP00203': { case "DSP00203": {
return ( return (
<> <>
{renderItem(0, false, false)} {renderItem(0, false, false)}
@@ -538,7 +603,11 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
return ( return (
<> <>
<Container className={css.container} spotlightId={spotlightId} data-wheel-point={true}> <Container
className={css.container}
spotlightId={spotlightId}
data-wheel-point={true}
>
<div className={css.homeTemplateBox}>{renderLayout()}</div> <div className={css.homeTemplateBox}>{renderLayout()}</div>
</Container> </Container>
{/* 선택약관 동의 팝업 */} {/* 선택약관 동의 팝업 */}
@@ -551,19 +620,19 @@ export default function HomeBanner({ firstSpot, spotlightId, handleItemFocus, ha
onOptionalDeclineClick={handleOptionalDeclineClick} onOptionalDeclineClick={handleOptionalDeclineClick}
customPosition={true} customPosition={true}
position={{ position={{
position: 'absolute', position: "absolute",
top: '342px', // 가운데를 기준으로 한 좌표 (1080/2) - 198 top: "342px", // 가운데를 기준으로 한 좌표 (1080/2) - 198
left: '0px', left: "0px",
bottom: 'unset', bottom: "unset",
transform: 'none', transform: "none",
}} }}
/> />
{/* 선택약관 자세히 보기 팝업 */} {/* 선택약관 자세히 보기 팝업 */}
<TNewPopUp <TNewPopUp
kind="figmaTermsPopup" kind="figmaTermsPopup"
open={isOptionalTermsVisible} open={isOptionalTermsVisible}
title={$L('Optional Terms')} title={$L("Optional Terms")}
text={optionalTermsData?.trmsCntt || ''} text={optionalTermsData?.trmsCntt || ""}
onClose={handleTermsPopupClosed} onClose={handleTermsPopupClosed}
onAgreeClick={handleTermsPopupAgree} onAgreeClick={handleTermsPopupAgree}
showAgreeButton={true} showAgreeButton={true}

View File

@@ -31,6 +31,7 @@ import TButton, { TYPES } from '../../components/TButton/TButton';
import TItemCard, { import TItemCard, {
removeDotAndColon, removeDotAndColon,
} from '../../components/TItemCard/TItemCard'; } from '../../components/TItemCard/TItemCard';
import TItemCardNew from '../../components/TItemCard/TItemCard.new';
import TPanel from '../../components/TPanel/TPanel'; import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator'; from '../../components/TVerticalPagenator/TVerticalPagenator';
@@ -52,15 +53,9 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
const [showButton, setShowButton] = useState("showButton", true); const [showButton, setShowButton] = useState("showButton", true);
const cbChangePageRef = useRef(null); const cbChangePageRef = useRef(null);
const shelfInfos = useSelector((state) => state.foryou?.shelfInfos); const shelfInfos = useSelector(
(state) => state.foryou?.justForYouInfo?.shelfInfos
useEffect(() => { );
dispatch(getJustForYouInfo());
}, [dispatch]);
useEffect(() => {
console.log("###shelfInfos updated", shelfInfos);
}, [dispatch, shelfInfos]);
const onClick = useCallback(() => { const onClick = useCallback(() => {
dispatch(popPanel(panel_names.JUST_FOR_YOU_TEST_PANEL)); dispatch(popPanel(panel_names.JUST_FOR_YOU_TEST_PANEL));
@@ -109,17 +104,23 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
const handleItemClick = () => { const handleItemClick = () => {
// Extract product ID from contentId if needed // Extract product ID from contentId if needed
const prdtId = contentId.split("_").pop(); const tokens = contentId.split("_");
const patnrId = tokens?.[4] || "";
const prdtId = tokens?.[5] || "";
dispatch( dispatch(
pushPanel({ pushPanel({
name: panel_names.DETAIL_PANEL, name: panel_names.DETAIL_PANEL,
panelInfo: { prdtId: contentId }, // panelInfo: { prdtId: contentId },
panelInfo: {
patnrId: patnrId,
prdtId: prdtId,
},
}) })
); );
}; };
return ( return (
<TItemCard <TItemCardNew
key={contentId} key={contentId}
patnerName={partnerName} patnerName={partnerName}
data-wheel-point={index >= 5} data-wheel-point={index >= 5}
@@ -127,7 +128,8 @@ const JustForYouTestPanel = ({ panelInfo, ...rest }) => {
imageSource={thumbnail} imageSource={thumbnail}
label={`${index + 1} of ${productInfos.length}`} label={`${index + 1} of ${productInfos.length}`}
lastLabel=" go to detail, button" lastLabel=" go to detail, button"
priceInfo={{ price, dcPrice }} dcPrice={dcPrice}
originPrice={price}
productName={title} productName={title}
productId={contentId} productId={contentId}
onClick={handleItemClick} onClick={handleItemClick}