Merge feature/si_log: 통합로그 수정사항 적용

This commit is contained in:
opacity@t-win.kr
2025-12-05 17:10:44 +09:00
16 changed files with 324 additions and 178 deletions

View File

@@ -0,0 +1,55 @@
#!/bin/bash
# 브랜치 비교 스크립트
# 사용법: ./compare-branches.sh <파일경로> [브랜치1] [브랜치2]
FILE_PATH=$1
BRANCH1=${2:-develop}
BRANCH2=${3:-feature/si_log}
if [ -z "$FILE_PATH" ]; then
echo "❌ 사용법: ./compare-branches.sh <파일경로> [브랜치1] [브랜치2]"
echo ""
echo "예시:"
echo " ./compare-branches.sh src/App/deepLinkHandler.js"
echo " ./compare-branches.sh src/actions/logActions.js develop develop_si"
exit 1
fi
FILENAME=$(basename "$FILE_PATH")
TEMP_DIR="/tmp/branch_compare"
# 브랜치 이름에서 슬래시를 언더스코어로 변경
BRANCH1_SAFE=$(echo "$BRANCH1" | sed 's/\//_/g')
BRANCH2_SAFE=$(echo "$BRANCH2" | sed 's/\//_/g')
mkdir -p "$TEMP_DIR"
echo "🔍 비교 중..."
echo " 📂 파일: $FILE_PATH"
echo " 🌿 브랜치1: $BRANCH1"
echo " 🌿 브랜치2: $BRANCH2"
echo ""
git show "$BRANCH1:com.twin.app.shoptime/$FILE_PATH" > "$TEMP_DIR/${FILENAME}_${BRANCH1_SAFE}" 2>/dev/null
if [ $? -ne 0 ]; then
echo "❌ 오류: $BRANCH1 브랜치에서 파일을 찾을 수 없습니다."
exit 1
fi
git show "$BRANCH2:com.twin.app.shoptime/$FILE_PATH" > "$TEMP_DIR/${FILENAME}_${BRANCH2_SAFE}" 2>/dev/null
if [ $? -ne 0 ]; then
echo "❌ 오류: $BRANCH2 브랜치에서 파일을 찾을 수 없습니다."
exit 1
fi
echo "✅ 파일 추출 완료!"
echo "📊 라인 수:"
wc -l "$TEMP_DIR/${FILENAME}_${BRANCH1_SAFE}" "$TEMP_DIR/${FILENAME}_${BRANCH2_SAFE}"
echo ""
echo "🎨 Cursor에서 비교 화면 열기..."
code --diff "$TEMP_DIR/${FILENAME}_${BRANCH1_SAFE}" "$TEMP_DIR/${FILENAME}_${BRANCH2_SAFE}"
echo "✨ 완료!"

View File

@@ -1,4 +1,3 @@
import { updateHomeInfo } from "../actions/homeActions"; import { updateHomeInfo } from "../actions/homeActions";
import { pushPanel } from "../actions/panelActions"; import { pushPanel } from "../actions/panelActions";
import { import {
@@ -252,18 +251,22 @@ export const handleDeepLink = (contentTarget) => (dispatch, _getState) => {
// break; // break;
} }
// 251204 [통합로그] webOS 에서 shoptime 진입점 정보 수집
const isFirstLaunch = _getState().common.appStatus?.isFirstLaunch;
dispatch( dispatch(
sendLogTotalRecommend({ sendLogTotalRecommend({
contextName: LOG_CONTEXT_NAME.ENTRY, contextName: LOG_CONTEXT_NAME.ENTRY,
messageId: LOG_MESSAGE_ID.ENTRY_INFO, messageId: LOG_MESSAGE_ID.ENTRY_INFO,
deeplink: deeplinkPanel, entryMenu: linkTpNm,
curationId: curationId ? curationId : showId, deeplink: type,
productId: prdtId, linkTypeCode: linkTpCd,
partnerID: patnrId, curationId: curationId,
showId: showId, showId: showId,
channelId: chanId, channelId: chanId,
productId: prdtId,
category: lgCatNm, category: lgCatNm,
linkTypeCode: linkTpCd, firstYn: isFirstLaunch ? "Y" : "N",
}) })
); );

View File

@@ -4,72 +4,61 @@ import React, {
useMemo, useMemo,
useRef, useRef,
useState, useState,
} from 'react'; } from "react";
import classNames from 'classnames'; import classNames from "classnames";
import { import {
AsYouTypeFormatter, AsYouTypeFormatter,
PhoneNumberFormat, PhoneNumberFormat,
PhoneNumberUtil, PhoneNumberUtil,
} from 'google-libphonenumber'; } from "google-libphonenumber";
import { import { useDispatch, useSelector } from "react-redux";
useDispatch,
useSelector,
} from 'react-redux';
import { import { off, on } from "@enact/core/dispatcher";
off, import spotlight, { Spotlight } from "@enact/spotlight";
on, import { SpotlightContainerDecorator } from "@enact/spotlight/SpotlightContainerDecorator";
} from '@enact/core/dispatcher'; import { Spottable } from "@enact/spotlight/Spottable";
import spotlight, { Spotlight } from '@enact/spotlight';
import {
SpotlightContainerDecorator,
} from '@enact/spotlight/SpotlightContainerDecorator';
import { Spottable } from '@enact/spotlight/Spottable';
import defaultImage from '../../../assets/images/img-thumb-empty-144@3x.png'; import defaultImage from "../../../assets/images/img-thumb-empty-144@3x.png";
import { types } from '../../actions/actionTypes'; import { types } from "../../actions/actionTypes";
import { import { clearSMS, sendSms } from "../../actions/appDataActions";
clearSMS,
sendSms,
} from '../../actions/appDataActions';
import { import {
changeLocalSettings, changeLocalSettings,
setHidePopup, setHidePopup,
setShowPopup, setShowPopup,
} from '../../actions/commonActions'; } from "../../actions/commonActions";
import { import {
clearRegisterDeviceInfo, clearRegisterDeviceInfo,
getDeviceAdditionInfo, getDeviceAdditionInfo,
registerDeviceInfo, registerDeviceInfo,
} from '../../actions/deviceActions'; } from "../../actions/deviceActions";
import { import {
clearCurationCoupon, clearCurationCoupon,
setEventIssueReq, setEventIssueReq,
} from '../../actions/eventActions'; } from "../../actions/eventActions";
import { import {
sendLogShopByMobile, sendLogShopByMobile,
sendLogTotalRecommend, sendLogTotalRecommend,
} from '../../actions/logActions'; } from "../../actions/logActions";
import { import {
ACTIVE_POPUP, ACTIVE_POPUP,
LOG_CONTEXT_NAME, LOG_CONTEXT_NAME,
LOG_MESSAGE_ID, LOG_MESSAGE_ID,
LOG_TP_NO, LOG_TP_NO,
} from '../../utils/Config'; } from "../../utils/Config";
import { import {
$L, $L,
decryptPhoneNumber, decryptPhoneNumber,
encryptPhoneNumber, encryptPhoneNumber,
formatLocalDateTime, formatLocalDateTime,
} from '../../utils/helperMethods'; } from "../../utils/helperMethods";
import CustomImage from '../CustomImage/CustomImage'; import CustomImage from "../CustomImage/CustomImage";
import TButton from '../TButton/TButton'; import TButton from "../TButton/TButton";
import TPopUp from '../TPopUp/TPopUp'; import TPopUp from "../TPopUp/TPopUp";
import HistoryPhoneNumber from './HistoryPhoneNumber/HistoryPhoneNumber'; import HistoryPhoneNumber from "./HistoryPhoneNumber/HistoryPhoneNumber";
import css from './MobileSendPopUp.module.less'; import css from "./MobileSendPopUp.module.less";
import PhoneInputSection from './PhoneInputSection'; import PhoneInputSection from "./PhoneInputSection";
import SMSNumKeyPad from './SMSNumKeyPad'; import SMSNumKeyPad from "./SMSNumKeyPad";
const SECRET_KEY = "fy7BTKuM9eeTQqEC9sF3Iw5qG43Aaip"; const SECRET_KEY = "fy7BTKuM9eeTQqEC9sF3Iw5qG43Aaip";
@@ -463,7 +452,10 @@ export default function MobileSendPopUp({
const logParams = { const logParams = {
status: "send", status: "send",
nowMenu: nowMenu, nowMenu: nowMenu,
partner: patncNm, partner: patncNm ?? shopByMobileLogRef?.current?.patncNm,
productId: prdtId ?? shopByMobileLogRef?.current?.prdtId,
productTitle: title ?? shopByMobileLogRef?.current?.prdtNm,
brand: shopByMobileLogRef?.current?.brndNm,
contextName: LOG_CONTEXT_NAME.SHOPBYMOBILE, contextName: LOG_CONTEXT_NAME.SHOPBYMOBILE,
messageId: LOG_MESSAGE_ID.SMB, messageId: LOG_MESSAGE_ID.SMB,
}; };
@@ -652,7 +644,7 @@ export default function MobileSendPopUp({
)} )}
<span>{mobileNumber}</span> <span>{mobileNumber}</span>
</SpottableComponent> </SpottableComponent>
</InputContainer> </InputContainer>
<Container className={css.flex}> <Container className={css.flex}>
{keyPadOff && recentSentNumber.length > 0 ? ( {keyPadOff && recentSentNumber.length > 0 ? (
<HistoryPhoneNumber <HistoryPhoneNumber

View File

@@ -140,8 +140,9 @@ export default memo(function TItemCard({
shelfTitle: shelfTitle, shelfTitle: shelfTitle,
productId: productId, productId: productId,
productTitle: productName, productTitle: productName,
showId: showId, showId: showId ?? contentId,
showTitle: showTitle, showTitle: showTitle ?? contentTitle,
contentId: contentId,
nowProductId: nowProductId, nowProductId: nowProductId,
nowCategory: nowCategory, nowCategory: nowCategory,
nowProductTitle: nowProductTitle, nowProductTitle: nowProductTitle,
@@ -159,7 +160,7 @@ export default memo(function TItemCard({
} }
} }
}, },
[onClick, disabled, contextName, messageId] [onClick, disabled, contextName, messageId, contentId, contentTitle]
); );
const _onFocus = useCallback(() => { const _onFocus = useCallback(() => {
if (onFocus) { if (onFocus) {

View File

@@ -173,8 +173,9 @@ export default memo(function YouMayLike({
productTitle={prdtNm} productTitle={prdtNm}
nowProductId={productInfo?.prdtId} nowProductId={productInfo?.prdtId}
nowProductTitle={productInfo?.prdtNm} nowProductTitle={productInfo?.prdtNm}
nowCategory={productInfo?.catNm} nowCategory={productInfo?.catNm ?? productInfo?.lgCatNm}
catNm={lgCatNm} catNm={lgCatNm}
category={lgCatNm}
patnerName={patncNm} patnerName={patncNm}
brandName={brndNm} brandName={brndNm}
imageAlt={prdtId} imageAlt={prdtId}

View File

@@ -319,10 +319,11 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => {
return; return;
} }
const templateCode = containerId?.split("-")[0] || containerId;
const foundElement = sortedBrandLayoutInfo.find( const foundElement = sortedBrandLayoutInfo.find(
(el) => el.shptmBrndOptTpCd === containerId (el) => el.shptmBrndOptTpCd === templateCode
); );
const actualShelfOrder = foundElement ? foundElement.expsOrd : null; const actualShelfOrder = foundElement ? foundElement.expsOrd : 0;
const selectedBrand = `${LOG_MENU.FEATURED_BRANDS}/${selectedPatncNm}`; const selectedBrand = `${LOG_MENU.FEATURED_BRANDS}/${selectedPatncNm}`;
const currentShelf = `${getMenuByContainerId(containerId)}`; const currentShelf = `${getMenuByContainerId(containerId)}`;

View File

@@ -25,7 +25,11 @@ import {
finishVideoPreview, finishVideoPreview,
startVideoPlayer, startVideoPlayer,
} from "../../../../actions/playActions"; } from "../../../../actions/playActions";
import { panel_names } from "../../../../utils/Config"; import {
LOG_CONTEXT_NAME,
LOG_MESSAGE_ID,
panel_names,
} from "../../../../utils/Config";
import { $L } from "../../../../utils/helperMethods"; import { $L } from "../../../../utils/helperMethods";
import css from "./LiveChannelsVerticalContents.module.less"; import css from "./LiveChannelsVerticalContents.module.less";
import LiveChannelsVerticalProductList from "./LiveChannelsVerticalProductList/LiveChannelsVerticalProductList"; import LiveChannelsVerticalProductList from "./LiveChannelsVerticalProductList/LiveChannelsVerticalProductList";
@@ -175,10 +179,14 @@ const LiveChannelsVerticalContents = ({
dispatch( dispatch(
sendLogTotalRecommend({ sendLogTotalRecommend({
contextName: LOG_CONTEXT_NAME.FEATURED_BRANDS,
messageId: LOG_MESSAGE_ID.SHELF_CLICK,
partner: chanNm, partner: chanNm,
shelfLocation: shelfOrder, shelfLocation: shelfOrder,
shelfId: spotlightId, shelfId: spotlightId,
shelfTitle: shelfTitle, shelfTitle: shelfTitle,
showId: showId,
showTitle: showNm,
contentId: showId, contentId: showId,
contentTitle: showNm, contentTitle: showNm,
brand: brndNm, brand: brndNm,

View File

@@ -217,6 +217,8 @@ export default function Favorites({ title, panelInfo, isOnTop }) {
(patnrId, prdtId, prdtNm, patncNm, showId, showNm, brndNm) => (ev) => { (patnrId, prdtId, prdtNm, patncNm, showId, showNm, brndNm) => (ev) => {
const params = { const params = {
menu: "Favorite", menu: "Favorite",
contentId: showId ?? prdtId,
contentTitle: showNm ?? prdtNm,
productId: prdtId, productId: prdtId,
productTitle: prdtNm, productTitle: prdtNm,
partner: patncNm, partner: patncNm,
@@ -340,7 +342,10 @@ export default function Favorites({ title, panelInfo, isOnTop }) {
item.patnrId, item.patnrId,
item.prdtId, item.prdtId,
item.prdtNm, item.prdtNm,
item.patncNm item.patncNm,
item.showId,
item.showNm,
item.brndNm
)} )}
onToggle={handleItemToggle(item.prdtId)} onToggle={handleItemToggle(item.prdtId)}
length={favoritesDatas.length} length={favoritesDatas.length}

View File

@@ -145,6 +145,7 @@ export default function RecentlyViewedContents({
lgCatCd, lgCatCd,
thumbnailUrl, thumbnailUrl,
showNm, showNm,
brndNm,
} = item; } = item;
return ( return (
<MyPageItemCard <MyPageItemCard
@@ -161,7 +162,8 @@ export default function RecentlyViewedContents({
lgCatCd, lgCatCd,
prdtId, prdtId,
prdtNm, prdtNm,
patncNm patncNm,
brndNm
)} )}
onToggle={_handleItemToggle(showId, prdtId)} onToggle={_handleItemToggle(showId, prdtId)}
spotlightId={mainContainerId + index} spotlightId={mainContainerId + index}

View File

@@ -271,6 +271,23 @@ export default function Reminders({ title, cbScrollTo }) {
Spotlight.focus("mypage-reminder-delete"); Spotlight.focus("mypage-reminder-delete");
}, [upComingAlertShow]); }, [upComingAlertShow]);
const handleItemClick = useCallback(
(showId, showNm, patncNm, brndNm, patnrId) => () => {
const params = {
menu: "Reminders",
contentId: showId,
contentTitle: showNm,
showId: showId,
showTitle: showNm,
partner: patncNm,
brand: brndNm,
contextName: Config.LOG_CONTEXT_NAME.MYPAGE,
messageId: Config.LOG_MESSAGE_ID.MYPAGE_CLICK,
};
dispatch(sendLogTotalRecommend(params));
},
[dispatch]
);
const renderItem = useCallback( const renderItem = useCallback(
({ index, ...rest }) => { ({ index, ...rest }) => {
const sortedAlertShows = upComingAlertShow.alertShows const sortedAlertShows = upComingAlertShow.alertShows
@@ -298,15 +315,29 @@ export default function Reminders({ title, cbScrollTo }) {
showNm={listItem.showNm} showNm={listItem.showNm}
strtDt={listItem.strtDt} strtDt={listItem.strtDt}
thumbnailUrl={listItem.thumbnailUrl} thumbnailUrl={listItem.thumbnailUrl}
brndNm={listItem.brndNm}
activeDelete={activeDelete} activeDelete={activeDelete}
selected={selectedItems[listItem.showId]} selected={selectedItems[listItem.showId]}
onToggle={handleItemToggle(listItem.showId)} onToggle={handleItemToggle(listItem.showId)}
onClick={handleItemClick(
listItem.showId,
listItem.showNm,
listItem.patncNm,
listItem.brndNm,
listItem.patnrId
)}
index={index} index={index}
length={upComingAlertShow.alertShows.length} length={upComingAlertShow.alertShows.length}
/> />
); );
}, },
[upComingAlertShow, activeDelete, selectedItems, handleItemToggle] [
upComingAlertShow,
activeDelete,
selectedItems,
handleItemToggle,
handleItemClick,
]
); );
return ( return (

View File

@@ -36,7 +36,7 @@ export default memo(function OnSaleContents({
messageId: LOG_MESSAGE_ID.SHELF, messageId: LOG_MESSAGE_ID.SHELF,
category: selectedLgCatNm, category: selectedLgCatNm,
shelfLocation: shelfOrder, shelfLocation: shelfOrder,
shelfId: selectedLgCatCd, shelfId: spotlightId,
shelfTitle: saleNm, shelfTitle: saleNm,
}; };
dispatch(sendLogTotalRecommend(params)); dispatch(sendLogTotalRecommend(params));

View File

@@ -108,8 +108,9 @@ export default function OnSalePanel({ panelInfo, spotlightId }) {
if (categoryInfos) { if (categoryInfos) {
dispatch(copyCategoryInfos(categoryInfos)); dispatch(copyCategoryInfos(categoryInfos));
setCategories(categoryInfos); setCategories(categoryInfos);
setSelectedLgCatCd(panelInfo?.lgCatCd); // GNB 진입 시 panelInfo가 비어있으면 첫 번째 카테고리를 기본값으로 설정
setSelectedLgCatNm(panelInfo?.lgCatNm); setSelectedLgCatCd(panelInfo?.lgCatCd ?? categoryInfos[0]?.lgCatCd);
setSelectedLgCatNm(panelInfo?.lgCatNm ?? categoryInfos[0]?.lgCatNm);
} }
} }
}, [ }, [

View File

@@ -1,38 +1,60 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import classNames from 'classnames'; import classNames from "classnames";
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from "react-redux";
import { Job } from '@enact/core/util'; import { Job } from "@enact/core/util";
import Spotlight from '@enact/spotlight'; import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator'; import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { getContainerNode, setContainerLastFocusedElement } from '@enact/spotlight/src/container'; import {
getContainerNode,
setContainerLastFocusedElement,
} from "@enact/spotlight/src/container";
import { sendLogTotalRecommend } from '../../../../actions/logActions'; import { sendLogTotalRecommend } from "../../../../actions/logActions";
import { navigateToDetail, SOURCE_MENUS, pushPanel } from '../../../../actions/panelActions'; import {
import { hidePlayerOverlays } from '../../../../actions/videoPlayActions'; navigateToDetail,
import TItemCard, { TYPES } from '../../../../components/TItemCard/TItemCard'; SOURCE_MENUS,
import TVirtualGridList from '../../../../components/TVirtualGridList/TVirtualGridList'; pushPanel,
import useScrollTo from '../../../../hooks/useScrollTo'; } from "../../../../actions/panelActions";
import { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names } from '../../../../utils/Config'; import { hidePlayerOverlays } from "../../../../actions/videoPlayActions";
import { scaleH } from '../../../../utils/helperMethods'; import TItemCard, { TYPES } from "../../../../components/TItemCard/TItemCard";
import ListEmptyContents from '../TabContents/ListEmptyContents/ListEmptyContents'; import TVirtualGridList from "../../../../components/TVirtualGridList/TVirtualGridList";
import css1 from './ShopNowContents.module.less'; import useScrollTo from "../../../../hooks/useScrollTo";
import cssV2 from './ShopNowContents.v2.module.less'; import {
LOG_CONTEXT_NAME,
LOG_MENU,
LOG_MESSAGE_ID,
panel_names,
} from "../../../../utils/Config";
import { scaleH } from "../../../../utils/helperMethods";
import ListEmptyContents from "../TabContents/ListEmptyContents/ListEmptyContents";
import css1 from "./ShopNowContents.module.less";
import cssV2 from "./ShopNowContents.v2.module.less";
const extractPriceInfo = (priceInfo) => { const extractPriceInfo = (priceInfo) => {
if (!priceInfo) return { originalPrice: '', discountedPrice: '', discountRate: '' }; if (!priceInfo)
return { originalPrice: "", discountedPrice: "", discountRate: "" };
const parts = priceInfo.split('|').map((part) => part.trim()); const parts = priceInfo.split("|").map((part) => part.trim());
return { return {
originalPrice: parts[0] || '', originalPrice: parts[0] || "",
discountedPrice: parts[1] || '', discountedPrice: parts[1] || "",
discountRate: parts[4] || '', discountRate: parts[4] || "",
}; };
}; };
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
export default function ShopNowContents({ export default function ShopNowContents({
shopNowInfo, shopNowInfo,
videoVerticalVisible, videoVerticalVisible,
@@ -42,7 +64,7 @@ export default function ShopNowContents({
panelInfo, panelInfo,
tabTitle, tabTitle,
version = 1, version = 1,
direction = 'vertical', direction = "vertical",
}) { }) {
const css = version === 2 ? cssV2 : css1; const css = version === 2 ? cssV2 : css1;
const { getScrollTo, scrollTop } = useScrollTo(); const { getScrollTo, scrollTop } = useScrollTo();
@@ -54,12 +76,12 @@ export default function ShopNowContents({
const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]); const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]);
useEffect(() => { useEffect(() => {
console.log('=== [ShopNow] Component Rendered ==='); console.log("=== [ShopNow] Component Rendered ===");
console.log('[ShopNow] shopNowInfo:', shopNowInfo); console.log("[ShopNow] shopNowInfo:", shopNowInfo);
console.log('[ShopNow] youmaylikeInfos:', youmaylikeInfos); console.log("[ShopNow] youmaylikeInfos:", youmaylikeInfos);
console.log('[ShopNow] version:', version); console.log("[ShopNow] version:", version);
console.log('[ShopNow] tabIndex:', tabIndex); console.log("[ShopNow] tabIndex:", tabIndex);
console.log('====================================='); console.log("=====================================");
}, [shopNowInfo, youmaylikeInfos, version, tabIndex]); }, [shopNowInfo, youmaylikeInfos, version, tabIndex]);
// ShopNow + YouMayLike 통합 아이템 (v2이고 shopNow < 3일 때만) // ShopNow + YouMayLike 통합 아이템 (v2이고 shopNow < 3일 때만)
@@ -69,17 +91,17 @@ export default function ShopNowContents({
// 기본: ShopNow 아이템 // 기본: ShopNow 아이템
let items = shopNowInfo.map((item) => ({ let items = shopNowInfo.map((item) => ({
...item, ...item,
_type: 'shopnow', _type: "shopnow",
})); }));
// v2 + ShopNow < 3 + YouMayLike 데이터 존재 시 통합 // v2 + ShopNow < 3 + YouMayLike 데이터 존재 시 통합
// for Test => // for Test =>
if (version === 2 && youmaylikeInfos && youmaylikeInfos.length > 0) { if (version === 2 && youmaylikeInfos && youmaylikeInfos.length > 0) {
// if (version === 2 && shopNowInfo.length < 3 && youmaylikeInfos && youmaylikeInfos.length > 0) { // if (version === 2 && shopNowInfo.length < 3 && youmaylikeInfos && youmaylikeInfos.length > 0) {
items = items.concat( items = items.concat(
youmaylikeInfos.map((item) => ({ youmaylikeInfos.map((item) => ({
...item, ...item,
_type: 'youmaylike', _type: "youmaylike",
})) }))
); );
} }
@@ -102,7 +124,7 @@ export default function ShopNowContents({
useEffect(() => { useEffect(() => {
return () => { return () => {
const gridListId = 'playVideoShopNowBox'; const gridListId = "playVideoShopNowBox";
const girdList = getContainerNode(gridListId); const girdList = getContainerNode(gridListId);
if (girdList) setContainerLastFocusedElement(null, [gridListId]); if (girdList) setContainerLastFocusedElement(null, [gridListId]);
@@ -144,14 +166,17 @@ export default function ShopNowContents({
const item = combinedItems[index]; const item = combinedItems[index];
// ===== YouMayLike 아이템 처리 ===== // ===== YouMayLike 아이템 처리 =====
if (item._type === 'youmaylike') { if (item._type === "youmaylike") {
const { imgUrl, patnrId, prdtId, prdtNm, priceInfo, offerInfo } = item; const { imgUrl, patnrId, prdtId, prdtNm, priceInfo, offerInfo } = item;
// YouMayLike 시작 지점 여부 (구분선 표시) // YouMayLike 시작 지점 여부 (구분선 표시)
const isYouMayLikeStart = shopNowInfo && index === shopNowInfo.length; const isYouMayLikeStart = shopNowInfo && index === shopNowInfo.length;
const handleYouMayLikeItemClick = () => { const handleYouMayLikeItemClick = () => {
console.log('[ShopNowContents] DetailPanel 진입 - sourceMenu:', SOURCE_MENUS.PLAYER_SHOP_NOW); console.log(
"[ShopNowContents] DetailPanel 진입 - sourceMenu:",
SOURCE_MENUS.PLAYER_SHOP_NOW
);
dispatch( dispatch(
navigateToDetail({ navigateToDetail({
@@ -195,7 +220,7 @@ export default function ShopNowContents({
onSpotlightUp={(e) => { onSpotlightUp={(e) => {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
Spotlight.focus('shownow_close_button'); Spotlight.focus("shownow_close_button");
}} }}
type={TYPES.horizontal} type={TYPES.horizontal}
version={version} version={version}
@@ -216,10 +241,12 @@ export default function ShopNowContents({
patncNm, patncNm,
brndNm, brndNm,
catNm, catNm,
lgCatNm,
} = item; } = item;
// 미리 계산된 가격 정보를 사용 // 미리 계산된 가격 정보를 사용
const { originalPrice, discountedPrice, discountRate } = priceInfoMap[index] || {}; const { originalPrice, discountedPrice, discountRate } =
priceInfoMap[index] || {};
const handleShopNowItemClick = () => { const handleShopNowItemClick = () => {
// ===== 기존 코드 (코멘트 처리) ===== // ===== 기존 코드 (코멘트 처리) =====
@@ -228,20 +255,20 @@ export default function ShopNowContents({
// const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id'); // const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id');
// console.log('[ShopNowContents] 현재 포커스된 spotlightId:', currentSpotlightId); // console.log('[ShopNowContents] 현재 포커스된 spotlightId:', currentSpotlightId);
// const params = { const params = {
// tabTitle: tabTitle[tabIndex], tabTitle: tabTitle[tabIndex],
// productId: prdtId, productId: prdtId,
// productTitle: prdtNm, productTitle: prdtNm,
// partner: patncNm, partner: patncNm,
// brand: brndNm, brand: brndNm,
// price: discountRate ? discountedPrice : originalPrice, price: discountRate ? discountedPrice : originalPrice,
// showType: panelInfo?.shptmBanrTpNm, showType: panelInfo?.shptmBanrTpNm,
// category: catNm, category: catNm ?? lgCatNm,
// discount: discountRate, discount: discountRate,
// contextName: LOG_CONTEXT_NAME.SHOW, contextName: LOG_CONTEXT_NAME.SHOW,
// messageId: LOG_MESSAGE_ID.CONTENTCLICK, messageId: LOG_MESSAGE_ID.CONTENTCLICK,
// }; };
// dispatch(sendLogTotalRecommend(params)); dispatch(sendLogTotalRecommend(params));
// // DetailPanel push 전에 VideoPlayer 오버레이 숨김 // // DetailPanel push 전에 VideoPlayer 오버레이 숨김
// dispatch(hidePlayerOverlays()); // dispatch(hidePlayerOverlays());
@@ -264,7 +291,10 @@ export default function ShopNowContents({
// ); // );
// ===== navigateToDetail 방식 (handleYouMayLikeItemClick 참고) ===== // ===== navigateToDetail 방식 (handleYouMayLikeItemClick 참고) =====
console.log('[ShopNowContents] ShopNow DetailPanel 진입 - sourceMenu:', SOURCE_MENUS.PLAYER_SHOP_NOW); console.log(
"[ShopNowContents] ShopNow DetailPanel 진입 - sourceMenu:",
SOURCE_MENUS.PLAYER_SHOP_NOW
);
dispatch( dispatch(
navigateToDetail({ navigateToDetail({
@@ -276,7 +306,7 @@ export default function ShopNowContents({
showId: playListInfo?.showId, showId: playListInfo?.showId,
liveFlag: playListInfo?.liveFlag, liveFlag: playListInfo?.liveFlag,
thumbnailUrl: playListInfo?.thumbnailUrl, thumbnailUrl: playListInfo?.thumbnailUrl,
liveReqFlag: panelInfo?.shptmBanrTpNm === 'LIVE' && 'Y', liveReqFlag: panelInfo?.shptmBanrTpNm === "LIVE" && "Y",
launchedFromPlayer: true, launchedFromPlayer: true,
}, },
}) })
@@ -304,7 +334,7 @@ export default function ShopNowContents({
// v2에서 첫 번째 아이템일 때 위로 가면 Close 버튼으로 // v2에서 첫 번째 아이템일 때 위로 가면 Close 버튼으로
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
Spotlight.focus('shownow_close_button'); Spotlight.focus("shownow_close_button");
} }
: undefined : undefined
} }
@@ -341,7 +371,9 @@ export default function ShopNowContents({
itemWidth={version === 2 ? 310 : videoVerticalVisible ? 540 : 600} itemWidth={version === 2 ? 310 : videoVerticalVisible ? 540 : 600}
itemHeight={version === 2 ? 445 : 236} itemHeight={version === 2 ? 445 : 236}
spacing={version === 2 ? 30 : 12} spacing={version === 2 ? 30 : 12}
className={videoVerticalVisible ? css.verticalItemList : css.itemList} className={
videoVerticalVisible ? css.verticalItemList : css.itemList
}
noScrollByWheel={false} noScrollByWheel={false}
spotlightId="playVideoShopNowBox" spotlightId="playVideoShopNowBox"
/> />

View File

@@ -4,55 +4,41 @@ import React, {
useMemo, useMemo,
useRef, useRef,
useState, useState,
} from 'react'; } from "react";
import { import { useDispatch, useSelector } from "react-redux";
useDispatch,
useSelector,
} from 'react-redux';
import { Job } from '@enact/core/util'; import { Job } from "@enact/core/util";
import Spotlight from '@enact/spotlight'; import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
from '@enact/spotlight/SpotlightContainerDecorator'; import { setContainerLastFocusedElement } from "@enact/spotlight/src/container";
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';
import { import { sendLogGNB, sendLogTotalRecommend } from "../../actions/logActions";
sendLogGNB, import { getMyRecommandedKeyword } from "../../actions/myPageActions";
sendLogTotalRecommend, import { popPanel, updatePanel } from "../../actions/panelActions";
} from '../../actions/logActions'; import { getSearch, resetSearch } from "../../actions/searchActions";
import { getMyRecommandedKeyword } from '../../actions/myPageActions'; import TBody from "../../components/TBody/TBody";
import { import TInput, { ICONS, KINDS } from "../../components/TInput/TInput";
popPanel, import TPanel from "../../components/TPanel/TPanel";
updatePanel, import TVerticalPagenator from "../../components/TVerticalPagenator/TVerticalPagenator";
} from '../../actions/panelActions'; import usePrevious from "../../hooks/usePrevious";
import { import useSearchVoice from "../../hooks/useSearchVoice";
getSearch,
resetSearch,
} from '../../actions/searchActions';
import TBody from '../../components/TBody/TBody';
import TInput, {
ICONS,
KINDS,
} from '../../components/TInput/TInput';
import TPanel from '../../components/TPanel/TPanel';
import TVerticalPagenator
from '../../components/TVerticalPagenator/TVerticalPagenator';
import usePrevious from '../../hooks/usePrevious';
import useSearchVoice from '../../hooks/useSearchVoice';
import { import {
LOG_CONTEXT_NAME, LOG_CONTEXT_NAME,
LOG_MENU, LOG_MENU,
LOG_MESSAGE_ID, LOG_MESSAGE_ID,
panel_names, panel_names,
} from '../../utils/Config'; } from "../../utils/Config";
import { SpotlightIds } from '../../utils/SpotlightIds'; import { SpotlightIds } from "../../utils/SpotlightIds";
import NoSearchResults from './NoSearchResults/NoSearchResults'; import NoSearchResults from "./NoSearchResults/NoSearchResults";
import RecommendedKeywords from './RecommendedKeywords/RecommendedKeywords'; import RecommendedKeywords from "./RecommendedKeywords/RecommendedKeywords";
import css from './SearchPanel.module.less'; import css from "./SearchPanel.module.less";
import SearchResults from './SearchResults/SearchResults'; import SearchResults from "./SearchResults/SearchResults";
const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); const ContainerBasic = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
const ITEMS_PER_PAGE = 9; const ITEMS_PER_PAGE = 9;
export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
@@ -69,7 +55,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [paginatedKeywords, setPaginatedKeywords] = useState([]); const [paginatedKeywords, setPaginatedKeywords] = useState([]);
const [pageChanged, setPageChanged] = useState(false); const [pageChanged, setPageChanged] = useState(false);
const [searchQuery, setSearchQuery] = useState(panelInfo.searchVal ? panelInfo.searchVal : null); const [searchQuery, setSearchQuery] = useState(
panelInfo.searchVal ? panelInfo.searchVal : null
);
const [position, setPosition] = useState(null); const [position, setPosition] = useState(null);
let searchQueryRef = usePrevious(searchQuery); let searchQueryRef = usePrevious(searchQuery);
@@ -77,12 +65,16 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
const isRecommendedSearchRef = useRef(false); const isRecommendedSearchRef = useRef(false);
const firstButtonSpotlightId = 'first-keyword-button'; const firstButtonSpotlightId = "first-keyword-button";
const focusJob = useRef(new Job((func) => func(), 100)); const focusJob = useRef(new Job((func) => func(), 100));
const cbChangePageRef = useRef(null); const cbChangePageRef = useRef(null);
const [focusedContainerId, setFocusedContainerId] = useState(panelInfo?.focusedContainerId); const [focusedContainerId, setFocusedContainerId] = useState(
panelInfo?.focusedContainerId
);
const focusedContainerIdRef = usePrevious(focusedContainerId); const focusedContainerIdRef = usePrevious(focusedContainerId);
const bestSellerDatas = useSelector((state) => state.product.bestSellerData.bestSeller); const bestSellerDatas = useSelector(
(state) => state.product.bestSellerData.bestSeller
);
useEffect(() => { useEffect(() => {
if (loadingComplete && !recommandedKeywords) { if (loadingComplete && !recommandedKeywords) {
@@ -145,7 +137,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// dispatch( // dispatch(
// sendLogTotalRecommend({ // sendLogTotalRecommend({
// query: searchQuery, // query: searchQuery,
// searchType: searchPerformed ? 'query' : 'keyword', // searchType: searchPerformed ? "query" : "keyword",
// result: result, // result: result,
// contextName: LOG_CONTEXT_NAME.SEARCH, // contextName: LOG_CONTEXT_NAME.SEARCH,
// messageId: LOG_MESSAGE_ID.SEARCH_ITEM, // messageId: LOG_MESSAGE_ID.SEARCH_ITEM,
@@ -160,9 +152,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (query.trim()) { if (query.trim()) {
dispatch( dispatch(
getSearch({ getSearch({
service: 'com.lgshop.app', service: "com.lgshop.app",
query: query, query: query,
domain: 'theme,show,item', domain: "theme,show,item",
}) })
); );
} else { } else {
@@ -176,7 +168,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
// STT 텍스트 수신 핸들러 // STT 텍스트 수신 핸들러
const handleSTTText = useCallback( const handleSTTText = useCallback(
(sttText) => { (sttText) => {
console.log('[SearchPanel] STT text received:', sttText); console.log("[SearchPanel] STT text received:", sttText);
// 1. searchQuery 업데이트 // 1. searchQuery 업데이트
setSearchQuery(sttText); setSearchQuery(sttText);
@@ -185,9 +177,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (sttText && sttText.trim()) { if (sttText && sttText.trim()) {
dispatch( dispatch(
getSearch({ getSearch({
service: 'com.lgshop.app', service: "com.lgshop.app",
query: sttText.trim(), query: sttText.trim(),
domain: 'theme,show,item', domain: "theme,show,item",
}) })
); );
} }
@@ -215,7 +207,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}, [currentPage]); }, [currentPage]);
const hasPrevPage = currentPage > 1; const hasPrevPage = currentPage > 1;
const hasNextPage = currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length; const hasNextPage =
currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length;
useEffect(() => { useEffect(() => {
if (panelInfo && isOnTop) { if (panelInfo && isOnTop) {
@@ -244,19 +237,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
return; return;
} }
if (e.key === 'Enter') { if (e.key === "Enter") {
handleSearchSubmit(searchQuery); handleSearchSubmit(searchQuery);
} }
if (position === 0) { if (position === 0) {
if (e.key === 'Left' || e.key === 'ArrowLeft') { if (e.key === "Left" || e.key === "ArrowLeft") {
e.preventDefault(); e.preventDefault();
} }
} }
}; };
const cursorPosition = () => { const cursorPosition = () => {
const input = document.querySelector(`[data-spotlight-id="input-field-box"] > input`); const input = document.querySelector(
`[data-spotlight-id="input-field-box"] > input`
);
if (input) { if (input) {
setPosition(input.selectionStart); setPosition(input.selectionStart);
} }
@@ -266,13 +261,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
if (!isOnTopRef.current) { if (!isOnTopRef.current) {
return; return;
} }
if (searchQuery === null || searchQuery === '') { if (searchQuery === null || searchQuery === "") {
dispatch(popPanel(panel_names.SEARCH_PANEL)); dispatch(popPanel(panel_names.SEARCH_PANEL));
} else { } else {
setSearchQuery(''); setSearchQuery("");
setCurrentPage(1); setCurrentPage(1);
dispatch(resetSearch()); dispatch(resetSearch());
Spotlight.focus('search-input-box'); Spotlight.focus("search-input-box");
} }
}, [searchQuery, dispatch]); }, [searchQuery, dispatch]);
@@ -284,7 +279,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
Spotlight.resume(); Spotlight.resume();
setFirstSpot(true); setFirstSpot(true);
if (panelInfo.currentSpot) { if (panelInfo.currentSpot) {
if (panels[panels.length - 1]?.name === 'searchpanel') { if (panels[panels.length - 1]?.name === "searchpanel") {
Spotlight.focus(panelInfo.currentSpot); Spotlight.focus(panelInfo.currentSpot);
} }
} }
@@ -303,13 +298,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
}, [panelInfo, firstSpot]); }, [panelInfo, firstSpot]);
return ( return (
<TPanel className={css.container} handleCancel={onCancel} spotlightId={spotlightId}> <TPanel
<TBody className={css.tBody} scrollable={false} spotlightDisabled={!isOnTop}> className={css.container}
handleCancel={onCancel}
spotlightId={spotlightId}
>
<TBody
className={css.tBody}
scrollable={false}
spotlightDisabled={!isOnTop}
>
<ContainerBasic> <ContainerBasic>
{isOnTop && ( {isOnTop && (
<TVerticalPagenator <TVerticalPagenator
className={css.tVerticalPagenator} className={css.tVerticalPagenator}
spotlightId={'search_verticalPagenator'} spotlightId={"search_verticalPagenator"}
defaultContainerId={panelInfo?.focusedContainerId} defaultContainerId={panelInfo?.focusedContainerId}
disabled={!isOnTop} disabled={!isOnTop}
// onScrollStop={onScrollStop} // onScrollStop={onScrollStop}
@@ -320,7 +323,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
<ContainerBasic <ContainerBasic
className={css.inputContainer} className={css.inputContainer}
data-wheel-point={true} data-wheel-point={true}
spotlightId={'search-input-layer'} spotlightId={"search-input-layer"}
> >
<TInput <TInput
className={css.inputBox} className={css.inputBox}
@@ -332,7 +335,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) {
onKeyDown={handleKeydown} onKeyDown={handleKeydown}
onKeyUp={cursorPosition} onKeyUp={cursorPosition}
forcedSpotlight="first-keyword-button" forcedSpotlight="first-keyword-button"
spotlightId={'search-input-box'} spotlightId={"search-input-box"}
/> />
</ContainerBasic> </ContainerBasic>

View File

@@ -80,11 +80,20 @@ export default memo(function SearchItemCard({
const xContainer = tItemCard?.parentNode?.parentNode; const xContainer = tItemCard?.parentNode?.parentNode;
const yContainer = tBody?.children[0]?.children[0]?.children[0]; const yContainer = tBody?.children[0]?.children[0]?.children[0];
// 할인율 계산
const discountRate =
priceNumber > discountPriceNumber
? Math.round(
((priceNumber - discountPriceNumber) / priceNumber) * 100
) + "%"
: "";
sendLog({ sendLog({
productId: prdtId, productId: prdtId,
productTitle: title, productTitle: title,
partner: patncNm, partner: patncNm,
price: dcPrice ? dcPrice : price, price: price,
discount: discountRate,
resultType: "item", resultType: "item",
}); });
if (xContainer && yContainer) { if (xContainer && yContainer) {

View File

@@ -60,6 +60,8 @@ export default memo(function SearchThemeCard({
const yContainer = tBody?.children[0]?.children[0]?.children[0]; const yContainer = tBody?.children[0]?.children[0]?.children[0];
sendLog({ sendLog({
contentId: curationId,
contentTitle: title,
productId: prdtId, productId: prdtId,
productTitle: title, productTitle: title,
partner: patncNm, partner: patncNm,