diff --git a/com.twin.app.shoptime/compare-branches.sh b/com.twin.app.shoptime/compare-branches.sh new file mode 100755 index 00000000..795bcef1 --- /dev/null +++ b/com.twin.app.shoptime/compare-branches.sh @@ -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 "✨ 완료!" + diff --git a/com.twin.app.shoptime/src/App/deepLinkHandler.js b/com.twin.app.shoptime/src/App/deepLinkHandler.js index 67f5e4c7..40c822d3 100644 --- a/com.twin.app.shoptime/src/App/deepLinkHandler.js +++ b/com.twin.app.shoptime/src/App/deepLinkHandler.js @@ -1,4 +1,3 @@ - import { updateHomeInfo } from "../actions/homeActions"; import { pushPanel } from "../actions/panelActions"; import { @@ -252,18 +251,22 @@ export const handleDeepLink = (contentTarget) => (dispatch, _getState) => { // break; } + // 251204 [통합로그] webOS 에서 shoptime 진입점 정보 수집 + const isFirstLaunch = _getState().common.appStatus?.isFirstLaunch; + dispatch( sendLogTotalRecommend({ contextName: LOG_CONTEXT_NAME.ENTRY, messageId: LOG_MESSAGE_ID.ENTRY_INFO, - deeplink: deeplinkPanel, - curationId: curationId ? curationId : showId, - productId: prdtId, - partnerID: patnrId, + entryMenu: linkTpNm, + deeplink: type, + linkTypeCode: linkTpCd, + curationId: curationId, showId: showId, channelId: chanId, + productId: prdtId, category: lgCatNm, - linkTypeCode: linkTpCd, + firstYn: isFirstLaunch ? "Y" : "N", }) ); diff --git a/com.twin.app.shoptime/src/components/MobileSend/MobileSendPopUp.jsx b/com.twin.app.shoptime/src/components/MobileSend/MobileSendPopUp.jsx index 7c10576d..cf0f0aa4 100644 --- a/com.twin.app.shoptime/src/components/MobileSend/MobileSendPopUp.jsx +++ b/com.twin.app.shoptime/src/components/MobileSend/MobileSendPopUp.jsx @@ -4,72 +4,61 @@ import React, { useMemo, useRef, useState, -} from 'react'; +} from "react"; -import classNames from 'classnames'; +import classNames from "classnames"; import { AsYouTypeFormatter, PhoneNumberFormat, PhoneNumberUtil, -} from 'google-libphonenumber'; -import { - useDispatch, - useSelector, -} from 'react-redux'; +} from "google-libphonenumber"; +import { useDispatch, useSelector } from "react-redux"; -import { - off, - on, -} from '@enact/core/dispatcher'; -import spotlight, { Spotlight } from '@enact/spotlight'; -import { - SpotlightContainerDecorator, -} from '@enact/spotlight/SpotlightContainerDecorator'; -import { Spottable } from '@enact/spotlight/Spottable'; +import { off, on } from "@enact/core/dispatcher"; +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 { types } from '../../actions/actionTypes'; -import { - clearSMS, - sendSms, -} from '../../actions/appDataActions'; +import defaultImage from "../../../assets/images/img-thumb-empty-144@3x.png"; +import { types } from "../../actions/actionTypes"; +import { clearSMS, sendSms } from "../../actions/appDataActions"; import { changeLocalSettings, setHidePopup, setShowPopup, -} from '../../actions/commonActions'; +} from "../../actions/commonActions"; import { clearRegisterDeviceInfo, getDeviceAdditionInfo, registerDeviceInfo, -} from '../../actions/deviceActions'; +} from "../../actions/deviceActions"; import { clearCurationCoupon, setEventIssueReq, -} from '../../actions/eventActions'; +} from "../../actions/eventActions"; import { sendLogShopByMobile, sendLogTotalRecommend, -} from '../../actions/logActions'; +} from "../../actions/logActions"; import { ACTIVE_POPUP, LOG_CONTEXT_NAME, LOG_MESSAGE_ID, LOG_TP_NO, -} from '../../utils/Config'; +} from "../../utils/Config"; import { $L, decryptPhoneNumber, encryptPhoneNumber, formatLocalDateTime, -} from '../../utils/helperMethods'; -import CustomImage from '../CustomImage/CustomImage'; -import TButton from '../TButton/TButton'; -import TPopUp from '../TPopUp/TPopUp'; -import HistoryPhoneNumber from './HistoryPhoneNumber/HistoryPhoneNumber'; -import css from './MobileSendPopUp.module.less'; -import PhoneInputSection from './PhoneInputSection'; -import SMSNumKeyPad from './SMSNumKeyPad'; +} from "../../utils/helperMethods"; +import CustomImage from "../CustomImage/CustomImage"; +import TButton from "../TButton/TButton"; +import TPopUp from "../TPopUp/TPopUp"; +import HistoryPhoneNumber from "./HistoryPhoneNumber/HistoryPhoneNumber"; +import css from "./MobileSendPopUp.module.less"; +import PhoneInputSection from "./PhoneInputSection"; +import SMSNumKeyPad from "./SMSNumKeyPad"; const SECRET_KEY = "fy7BTKuM9eeTQqEC9sF3Iw5qG43Aaip"; @@ -463,7 +452,10 @@ export default function MobileSendPopUp({ const logParams = { status: "send", 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, messageId: LOG_MESSAGE_ID.SMB, }; @@ -652,7 +644,7 @@ export default function MobileSendPopUp({ )} {mobileNumber} - + {keyPadOff && recentSentNumber.length > 0 ? ( { if (onFocus) { diff --git a/com.twin.app.shoptime/src/views/DetailPanel/YouMayLike/YouMayLike.jsx b/com.twin.app.shoptime/src/views/DetailPanel/YouMayLike/YouMayLike.jsx index 1f01a705..5172e143 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/YouMayLike/YouMayLike.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/YouMayLike/YouMayLike.jsx @@ -173,8 +173,9 @@ export default memo(function YouMayLike({ productTitle={prdtNm} nowProductId={productInfo?.prdtId} nowProductTitle={productInfo?.prdtNm} - nowCategory={productInfo?.catNm} + nowCategory={productInfo?.catNm ?? productInfo?.lgCatNm} catNm={lgCatNm} + category={lgCatNm} patnerName={patncNm} brandName={brndNm} imageAlt={prdtId} diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx index 598f6c4c..72320b97 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/FeaturedBrandsPanel.jsx @@ -319,10 +319,11 @@ const FeaturedBrandsPanel = ({ isOnTop, panelInfo, spotlightId }) => { return; } + const templateCode = containerId?.split("-")[0] || containerId; 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 currentShelf = `${getMenuByContainerId(containerId)}`; diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/LiveChannels/LiveChannelsVerticalContents/LiveChannelsVerticalContents.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/LiveChannels/LiveChannelsVerticalContents/LiveChannelsVerticalContents.jsx index b961203f..e2f41402 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/LiveChannels/LiveChannelsVerticalContents/LiveChannelsVerticalContents.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/LiveChannels/LiveChannelsVerticalContents/LiveChannelsVerticalContents.jsx @@ -25,7 +25,11 @@ import { finishVideoPreview, startVideoPlayer, } 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 css from "./LiveChannelsVerticalContents.module.less"; import LiveChannelsVerticalProductList from "./LiveChannelsVerticalProductList/LiveChannelsVerticalProductList"; @@ -175,10 +179,14 @@ const LiveChannelsVerticalContents = ({ dispatch( sendLogTotalRecommend({ + contextName: LOG_CONTEXT_NAME.FEATURED_BRANDS, + messageId: LOG_MESSAGE_ID.SHELF_CLICK, partner: chanNm, shelfLocation: shelfOrder, shelfId: spotlightId, shelfTitle: shelfTitle, + showId: showId, + showTitle: showNm, contentId: showId, contentTitle: showNm, brand: brndNm, diff --git a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/Favorites/Favorites.jsx b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/Favorites/Favorites.jsx index 4122bc5c..ec7ee352 100644 --- a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/Favorites/Favorites.jsx +++ b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/Favorites/Favorites.jsx @@ -217,6 +217,8 @@ export default function Favorites({ title, panelInfo, isOnTop }) { (patnrId, prdtId, prdtNm, patncNm, showId, showNm, brndNm) => (ev) => { const params = { menu: "Favorite", + contentId: showId ?? prdtId, + contentTitle: showNm ?? prdtNm, productId: prdtId, productTitle: prdtNm, partner: patncNm, @@ -340,7 +342,10 @@ export default function Favorites({ title, panelInfo, isOnTop }) { item.patnrId, item.prdtId, item.prdtNm, - item.patncNm + item.patncNm, + item.showId, + item.showNm, + item.brndNm )} onToggle={handleItemToggle(item.prdtId)} length={favoritesDatas.length} diff --git a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/RecentlyViewed/RecentlyViewedContents.jsx b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/RecentlyViewed/RecentlyViewedContents.jsx index aa41ecd1..aa1432a8 100644 --- a/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/RecentlyViewed/RecentlyViewedContents.jsx +++ b/com.twin.app.shoptime/src/views/MyPagePanel/MyPageSub/RecentlyViewed/RecentlyViewedContents.jsx @@ -145,6 +145,7 @@ export default function RecentlyViewedContents({ lgCatCd, thumbnailUrl, showNm, + brndNm, } = item; return ( () => { + 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( ({ index, ...rest }) => { const sortedAlertShows = upComingAlertShow.alertShows @@ -298,15 +315,29 @@ export default function Reminders({ title, cbScrollTo }) { showNm={listItem.showNm} strtDt={listItem.strtDt} thumbnailUrl={listItem.thumbnailUrl} + brndNm={listItem.brndNm} activeDelete={activeDelete} selected={selectedItems[listItem.showId]} onToggle={handleItemToggle(listItem.showId)} + onClick={handleItemClick( + listItem.showId, + listItem.showNm, + listItem.patncNm, + listItem.brndNm, + listItem.patnrId + )} index={index} length={upComingAlertShow.alertShows.length} /> ); }, - [upComingAlertShow, activeDelete, selectedItems, handleItemToggle] + [ + upComingAlertShow, + activeDelete, + selectedItems, + handleItemToggle, + handleItemClick, + ] ); return ( diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleContents/OnSaleContents.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleContents/OnSaleContents.jsx index 405b92d2..e7fe7c72 100644 --- a/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleContents/OnSaleContents.jsx +++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSaleContents/OnSaleContents.jsx @@ -36,7 +36,7 @@ export default memo(function OnSaleContents({ messageId: LOG_MESSAGE_ID.SHELF, category: selectedLgCatNm, shelfLocation: shelfOrder, - shelfId: selectedLgCatCd, + shelfId: spotlightId, shelfTitle: saleNm, }; dispatch(sendLogTotalRecommend(params)); diff --git a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx index 6d27bef2..b440330a 100644 --- a/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx +++ b/com.twin.app.shoptime/src/views/OnSalePanel/OnSalePanel.jsx @@ -108,8 +108,9 @@ export default function OnSalePanel({ panelInfo, spotlightId }) { if (categoryInfos) { dispatch(copyCategoryInfos(categoryInfos)); setCategories(categoryInfos); - setSelectedLgCatCd(panelInfo?.lgCatCd); - setSelectedLgCatNm(panelInfo?.lgCatNm); + // GNB 진입 시 panelInfo가 비어있으면 첫 번째 카테고리를 기본값으로 설정 + setSelectedLgCatCd(panelInfo?.lgCatCd ?? categoryInfos[0]?.lgCatCd); + setSelectedLgCatNm(panelInfo?.lgCatNm ?? categoryInfos[0]?.lgCatNm); } } }, [ diff --git a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx index 440d6077..40af25e0 100644 --- a/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx +++ b/com.twin.app.shoptime/src/views/PlayerPanel/PlayerTabContents/TabContents/ShopNowContents.jsx @@ -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 { useDispatch, useSelector } from 'react-redux'; +import classNames from "classnames"; +import { useDispatch, useSelector } from "react-redux"; -import { Job } from '@enact/core/util'; -import Spotlight from '@enact/spotlight'; -import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator'; -import { getContainerNode, setContainerLastFocusedElement } from '@enact/spotlight/src/container'; +import { Job } from "@enact/core/util"; +import Spotlight from "@enact/spotlight"; +import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; +import { + getContainerNode, + setContainerLastFocusedElement, +} from "@enact/spotlight/src/container"; -import { sendLogTotalRecommend } from '../../../../actions/logActions'; -import { navigateToDetail, SOURCE_MENUS, pushPanel } from '../../../../actions/panelActions'; -import { hidePlayerOverlays } from '../../../../actions/videoPlayActions'; -import TItemCard, { TYPES } from '../../../../components/TItemCard/TItemCard'; -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 { scaleH } from '../../../../utils/helperMethods'; -import ListEmptyContents from '../TabContents/ListEmptyContents/ListEmptyContents'; -import css1 from './ShopNowContents.module.less'; -import cssV2 from './ShopNowContents.v2.module.less'; +import { sendLogTotalRecommend } from "../../../../actions/logActions"; +import { + navigateToDetail, + SOURCE_MENUS, + pushPanel, +} from "../../../../actions/panelActions"; +import { hidePlayerOverlays } from "../../../../actions/videoPlayActions"; +import TItemCard, { TYPES } from "../../../../components/TItemCard/TItemCard"; +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 { 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) => { - 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 { - originalPrice: parts[0] || '', - discountedPrice: parts[1] || '', - discountRate: parts[4] || '', + originalPrice: parts[0] || "", + discountedPrice: parts[1] || "", + discountRate: parts[4] || "", }; }; -const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); +const Container = SpotlightContainerDecorator( + { enterTo: "last-focused" }, + "div" +); export default function ShopNowContents({ shopNowInfo, videoVerticalVisible, @@ -42,7 +64,7 @@ export default function ShopNowContents({ panelInfo, tabTitle, version = 1, - direction = 'vertical', + direction = "vertical", }) { const css = version === 2 ? cssV2 : css1; const { getScrollTo, scrollTop } = useScrollTo(); @@ -54,12 +76,12 @@ export default function ShopNowContents({ const gridStyle = useMemo(() => ({ height: `${height}px` }), [height]); useEffect(() => { - console.log('=== [ShopNow] Component Rendered ==='); - console.log('[ShopNow] shopNowInfo:', shopNowInfo); - console.log('[ShopNow] youmaylikeInfos:', youmaylikeInfos); - console.log('[ShopNow] version:', version); - console.log('[ShopNow] tabIndex:', tabIndex); - console.log('====================================='); + console.log("=== [ShopNow] Component Rendered ==="); + console.log("[ShopNow] shopNowInfo:", shopNowInfo); + console.log("[ShopNow] youmaylikeInfos:", youmaylikeInfos); + console.log("[ShopNow] version:", version); + console.log("[ShopNow] tabIndex:", tabIndex); + console.log("====================================="); }, [shopNowInfo, youmaylikeInfos, version, tabIndex]); // ShopNow + YouMayLike 통합 아이템 (v2이고 shopNow < 3일 때만) @@ -69,17 +91,17 @@ export default function ShopNowContents({ // 기본: ShopNow 아이템 let items = shopNowInfo.map((item) => ({ ...item, - _type: 'shopnow', + _type: "shopnow", })); // v2 + ShopNow < 3 + YouMayLike 데이터 존재 시 통합 - // for Test => + // for Test => 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( youmaylikeInfos.map((item) => ({ ...item, - _type: 'youmaylike', + _type: "youmaylike", })) ); } @@ -102,7 +124,7 @@ export default function ShopNowContents({ useEffect(() => { return () => { - const gridListId = 'playVideoShopNowBox'; + const gridListId = "playVideoShopNowBox"; const girdList = getContainerNode(gridListId); if (girdList) setContainerLastFocusedElement(null, [gridListId]); @@ -144,14 +166,17 @@ export default function ShopNowContents({ const item = combinedItems[index]; // ===== YouMayLike 아이템 처리 ===== - if (item._type === 'youmaylike') { + if (item._type === "youmaylike") { const { imgUrl, patnrId, prdtId, prdtNm, priceInfo, offerInfo } = item; // YouMayLike 시작 지점 여부 (구분선 표시) const isYouMayLikeStart = shopNowInfo && index === shopNowInfo.length; const handleYouMayLikeItemClick = () => { - console.log('[ShopNowContents] DetailPanel 진입 - sourceMenu:', SOURCE_MENUS.PLAYER_SHOP_NOW); + console.log( + "[ShopNowContents] DetailPanel 진입 - sourceMenu:", + SOURCE_MENUS.PLAYER_SHOP_NOW + ); dispatch( navigateToDetail({ @@ -195,7 +220,7 @@ export default function ShopNowContents({ onSpotlightUp={(e) => { e.stopPropagation(); e.preventDefault(); - Spotlight.focus('shownow_close_button'); + Spotlight.focus("shownow_close_button"); }} type={TYPES.horizontal} version={version} @@ -216,10 +241,12 @@ export default function ShopNowContents({ patncNm, brndNm, catNm, + lgCatNm, } = item; // 미리 계산된 가격 정보를 사용 - const { originalPrice, discountedPrice, discountRate } = priceInfoMap[index] || {}; + const { originalPrice, discountedPrice, discountRate } = + priceInfoMap[index] || {}; const handleShopNowItemClick = () => { // ===== 기존 코드 (코멘트 처리) ===== @@ -228,20 +255,20 @@ export default function ShopNowContents({ // const currentSpotlightId = currentFocusedElement?.getAttribute('data-spotlight-id'); // console.log('[ShopNowContents] 현재 포커스된 spotlightId:', currentSpotlightId); - // const params = { - // tabTitle: tabTitle[tabIndex], - // productId: prdtId, - // productTitle: prdtNm, - // partner: patncNm, - // brand: brndNm, - // price: discountRate ? discountedPrice : originalPrice, - // showType: panelInfo?.shptmBanrTpNm, - // category: catNm, - // discount: discountRate, - // contextName: LOG_CONTEXT_NAME.SHOW, - // messageId: LOG_MESSAGE_ID.CONTENTCLICK, - // }; - // dispatch(sendLogTotalRecommend(params)); + const params = { + tabTitle: tabTitle[tabIndex], + productId: prdtId, + productTitle: prdtNm, + partner: patncNm, + brand: brndNm, + price: discountRate ? discountedPrice : originalPrice, + showType: panelInfo?.shptmBanrTpNm, + category: catNm ?? lgCatNm, + discount: discountRate, + contextName: LOG_CONTEXT_NAME.SHOW, + messageId: LOG_MESSAGE_ID.CONTENTCLICK, + }; + dispatch(sendLogTotalRecommend(params)); // // DetailPanel push 전에 VideoPlayer 오버레이 숨김 // dispatch(hidePlayerOverlays()); @@ -264,7 +291,10 @@ export default function ShopNowContents({ // ); // ===== 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( navigateToDetail({ @@ -276,7 +306,7 @@ export default function ShopNowContents({ showId: playListInfo?.showId, liveFlag: playListInfo?.liveFlag, thumbnailUrl: playListInfo?.thumbnailUrl, - liveReqFlag: panelInfo?.shptmBanrTpNm === 'LIVE' && 'Y', + liveReqFlag: panelInfo?.shptmBanrTpNm === "LIVE" && "Y", launchedFromPlayer: true, }, }) @@ -304,7 +334,7 @@ export default function ShopNowContents({ // v2에서 첫 번째 아이템일 때 위로 가면 Close 버튼으로 e.stopPropagation(); e.preventDefault(); - Spotlight.focus('shownow_close_button'); + Spotlight.focus("shownow_close_button"); } : undefined } @@ -341,7 +371,9 @@ export default function ShopNowContents({ itemWidth={version === 2 ? 310 : videoVerticalVisible ? 540 : 600} itemHeight={version === 2 ? 445 : 236} spacing={version === 2 ? 30 : 12} - className={videoVerticalVisible ? css.verticalItemList : css.itemList} + className={ + videoVerticalVisible ? css.verticalItemList : css.itemList + } noScrollByWheel={false} spotlightId="playVideoShopNowBox" /> diff --git a/com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.jsx b/com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.jsx index 2cdb026c..dbe94d8b 100644 --- a/com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.jsx +++ b/com.twin.app.shoptime/src/views/SearchPanel/SearchPanel.jsx @@ -4,55 +4,41 @@ import React, { useMemo, useRef, useState, -} from 'react'; +} from "react"; -import { - useDispatch, - useSelector, -} from 'react-redux'; +import { useDispatch, useSelector } from "react-redux"; -import { Job } from '@enact/core/util'; -import Spotlight from '@enact/spotlight'; -import SpotlightContainerDecorator - from '@enact/spotlight/SpotlightContainerDecorator'; -import { setContainerLastFocusedElement } from '@enact/spotlight/src/container'; +import { Job } from "@enact/core/util"; +import Spotlight from "@enact/spotlight"; +import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; +import { setContainerLastFocusedElement } from "@enact/spotlight/src/container"; -import { - sendLogGNB, - sendLogTotalRecommend, -} from '../../actions/logActions'; -import { getMyRecommandedKeyword } from '../../actions/myPageActions'; -import { - popPanel, - updatePanel, -} from '../../actions/panelActions'; -import { - 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 { sendLogGNB, sendLogTotalRecommend } from "../../actions/logActions"; +import { getMyRecommandedKeyword } from "../../actions/myPageActions"; +import { popPanel, updatePanel } from "../../actions/panelActions"; +import { 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 { LOG_CONTEXT_NAME, LOG_MENU, LOG_MESSAGE_ID, panel_names, -} from '../../utils/Config'; -import { SpotlightIds } from '../../utils/SpotlightIds'; -import NoSearchResults from './NoSearchResults/NoSearchResults'; -import RecommendedKeywords from './RecommendedKeywords/RecommendedKeywords'; -import css from './SearchPanel.module.less'; -import SearchResults from './SearchResults/SearchResults'; +} from "../../utils/Config"; +import { SpotlightIds } from "../../utils/SpotlightIds"; +import NoSearchResults from "./NoSearchResults/NoSearchResults"; +import RecommendedKeywords from "./RecommendedKeywords/RecommendedKeywords"; +import css from "./SearchPanel.module.less"; +import SearchResults from "./SearchResults/SearchResults"; -const ContainerBasic = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div'); +const ContainerBasic = SpotlightContainerDecorator( + { enterTo: "last-focused" }, + "div" +); const ITEMS_PER_PAGE = 9; export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { @@ -69,7 +55,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { const [currentPage, setCurrentPage] = useState(1); const [paginatedKeywords, setPaginatedKeywords] = useState([]); 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); let searchQueryRef = usePrevious(searchQuery); @@ -77,12 +65,16 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { const isRecommendedSearchRef = useRef(false); - const firstButtonSpotlightId = 'first-keyword-button'; + const firstButtonSpotlightId = "first-keyword-button"; const focusJob = useRef(new Job((func) => func(), 100)); const cbChangePageRef = useRef(null); - const [focusedContainerId, setFocusedContainerId] = useState(panelInfo?.focusedContainerId); + const [focusedContainerId, setFocusedContainerId] = useState( + panelInfo?.focusedContainerId + ); const focusedContainerIdRef = usePrevious(focusedContainerId); - const bestSellerDatas = useSelector((state) => state.product.bestSellerData.bestSeller); + const bestSellerDatas = useSelector( + (state) => state.product.bestSellerData.bestSeller + ); useEffect(() => { if (loadingComplete && !recommandedKeywords) { @@ -145,7 +137,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { // dispatch( // sendLogTotalRecommend({ // query: searchQuery, - // searchType: searchPerformed ? 'query' : 'keyword', + // searchType: searchPerformed ? "query" : "keyword", // result: result, // contextName: LOG_CONTEXT_NAME.SEARCH, // messageId: LOG_MESSAGE_ID.SEARCH_ITEM, @@ -160,9 +152,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { if (query.trim()) { dispatch( getSearch({ - service: 'com.lgshop.app', + service: "com.lgshop.app", query: query, - domain: 'theme,show,item', + domain: "theme,show,item", }) ); } else { @@ -176,7 +168,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { // STT 텍스트 수신 핸들러 const handleSTTText = useCallback( (sttText) => { - console.log('[SearchPanel] STT text received:', sttText); + console.log("[SearchPanel] STT text received:", sttText); // 1. searchQuery 업데이트 setSearchQuery(sttText); @@ -185,9 +177,9 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { if (sttText && sttText.trim()) { dispatch( getSearch({ - service: 'com.lgshop.app', + service: "com.lgshop.app", query: sttText.trim(), - domain: 'theme,show,item', + domain: "theme,show,item", }) ); } @@ -215,7 +207,8 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { }, [currentPage]); const hasPrevPage = currentPage > 1; - const hasNextPage = currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length; + const hasNextPage = + currentPage * ITEMS_PER_PAGE < recommandedKeywords?.length; useEffect(() => { if (panelInfo && isOnTop) { @@ -244,19 +237,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { return; } - if (e.key === 'Enter') { + if (e.key === "Enter") { handleSearchSubmit(searchQuery); } if (position === 0) { - if (e.key === 'Left' || e.key === 'ArrowLeft') { + if (e.key === "Left" || e.key === "ArrowLeft") { e.preventDefault(); } } }; 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) { setPosition(input.selectionStart); } @@ -266,13 +261,13 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { if (!isOnTopRef.current) { return; } - if (searchQuery === null || searchQuery === '') { + if (searchQuery === null || searchQuery === "") { dispatch(popPanel(panel_names.SEARCH_PANEL)); } else { - setSearchQuery(''); + setSearchQuery(""); setCurrentPage(1); dispatch(resetSearch()); - Spotlight.focus('search-input-box'); + Spotlight.focus("search-input-box"); } }, [searchQuery, dispatch]); @@ -284,7 +279,7 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { Spotlight.resume(); setFirstSpot(true); if (panelInfo.currentSpot) { - if (panels[panels.length - 1]?.name === 'searchpanel') { + if (panels[panels.length - 1]?.name === "searchpanel") { Spotlight.focus(panelInfo.currentSpot); } } @@ -303,13 +298,21 @@ export default function SearchPanel({ panelInfo, isOnTop, spotlightId }) { }, [panelInfo, firstSpot]); return ( - - + + {isOnTop && ( diff --git a/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchItemCard.jsx b/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchItemCard.jsx index d69f81d5..50c41761 100644 --- a/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchItemCard.jsx +++ b/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchItemCard.jsx @@ -80,11 +80,20 @@ export default memo(function SearchItemCard({ const xContainer = tItemCard?.parentNode?.parentNode; const yContainer = tBody?.children[0]?.children[0]?.children[0]; + // 할인율 계산 + const discountRate = + priceNumber > discountPriceNumber + ? Math.round( + ((priceNumber - discountPriceNumber) / priceNumber) * 100 + ) + "%" + : ""; + sendLog({ productId: prdtId, productTitle: title, partner: patncNm, - price: dcPrice ? dcPrice : price, + price: price, + discount: discountRate, resultType: "item", }); if (xContainer && yContainer) { diff --git a/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchThemeCard.jsx b/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchThemeCard.jsx index f227e169..4c64435f 100644 --- a/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchThemeCard.jsx +++ b/com.twin.app.shoptime/src/views/SearchPanel/SearchResults/SearchCard/SearchThemeCard.jsx @@ -60,6 +60,8 @@ export default memo(function SearchThemeCard({ const yContainer = tBody?.children[0]?.children[0]?.children[0]; sendLog({ + contentId: curationId, + contentTitle: title, productId: prdtId, productTitle: title, partner: patncNm,