From d640bb74ef73ac9d2409fbb076df9dc4a0de6370 Mon Sep 17 00:00:00 2001 From: optrader Date: Thu, 11 Dec 2025 13:46:12 +0900 Subject: [PATCH] [251211] feat: FeaturedBrandsPanel , TopBannerImage Focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 12. 11. 13:46:11 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 6๊ฐœ โ€ข ์ถ”๊ฐ€: +48์ค„ โ€ข ์‚ญ์ œ: -16์ค„ ๐Ÿ“ ์ถ”๊ฐ€๋œ ํŒŒ์ผ: + com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.jsx + com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.module.less ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductInfoSection/QRCode/QRCode.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx ~ com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.module.less ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ์†Œ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  โ€ข ๋ชจ๋“ˆ ๊ตฌ์กฐ ๊ฐœ์„  --- .../src/components/TQRCode/TQRCodeNew.jsx | 93 +++++++++++++++++++ .../components/TQRCode/TQRCodeNew.module.less | 0 .../ProductAllSection/ProductAllSection.jsx | 15 +++ .../ProductInfoSection/QRCode/QRCode.jsx | 18 +++- .../TopBannerImage/TopBannerImage.jsx | 12 +-- .../TopBannerImage/TopBannerImage.module.less | 19 ++-- 6 files changed, 141 insertions(+), 16 deletions(-) create mode 100644 com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.jsx create mode 100644 com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.module.less diff --git a/com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.jsx b/com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.jsx new file mode 100644 index 00000000..1f7738fb --- /dev/null +++ b/com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.jsx @@ -0,0 +1,93 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; + +import { useDispatch, useSelector } from "react-redux"; + +import { getDeviceAdditionInfo } from "../../actions/deviceActions"; +import { scaleH, scaleW } from "../../utils/helperMethods"; + +export default function TQRCodeNew({ + isBillingProductVisible, + ariaLabel, + text, + width = "128", + height = "128", +}) { + const qrcodeRef = useRef(null); + const deviceInfo = useSelector((state) => state.device.deviceInfo); + const { entryMenu, nowMenu } = useSelector((state) => state.common.menu); + const dispatch = useDispatch(); + + useEffect(() => { + if (!deviceInfo) { + dispatch(getDeviceAdditionInfo()); + } + }, [deviceInfo, dispatch]); + + const applyCircularMask = (scaledWidth, scaledHeight) => { + if (!qrcodeRef.current) return; + + const canvas = qrcodeRef.current.querySelector('canvas'); + if (!canvas) return; + + const radius = scaledWidth / 2; + + // ์›๋ณธ canvas ์ €์žฅ + const tempCanvas = document.createElement('canvas'); + tempCanvas.width = scaledWidth; + tempCanvas.height = scaledHeight; + const tempCtx = tempCanvas.getContext('2d'); + tempCtx.drawImage(canvas, 0, 0); + + // ์›๋ณธ canvas ์ดˆ๊ธฐํ™” + const ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, scaledWidth, scaledHeight); + + // ์›ํ˜• ๋งˆ์Šคํฌ ์ ์šฉ + ctx.beginPath(); + ctx.arc(radius, radius, radius, 0, Math.PI * 2); + ctx.clip(); + + // ์ด๋ฏธ์ง€ ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ + ctx.drawImage(tempCanvas, 0, 0); + }; + + useEffect(() => { + if (typeof window === "object" && entryMenu && nowMenu) { + if (qrcodeRef.current) { + while (qrcodeRef.current.firstChild) { + qrcodeRef.current.removeChild(qrcodeRef.current.firstChild); + } + } + // nowMenu ๋ฐ์ดํ„ฐ๋ฅผ Base64๋กœ ์ธ์ฝ”๋”ฉ + const encodedNowMenu = encodeURIComponent(nowMenu); + const encodeEntryMenu = encodeURIComponent(entryMenu); + + let idx; + + if (deviceInfo === null || !deviceInfo) { + idx = 0; + } else { + idx = deviceInfo?.dvcIndex; + } + + const scaledWidth = scaleW(width); + const scaledHeight = scaleH(height); + + const qrcode = new window.QRCode(qrcodeRef.current, { + text: isBillingProductVisible + ? text + : `${text}&entryMenu=${encodeEntryMenu}&nowMenu=${encodedNowMenu}&idx=${idx}`, + width: scaledWidth, + height: scaledHeight, + correctLevel: window.QRCode.CorrectLevel.L, + }); + + // QR์ฝ”๋“œ ์ƒ์„ฑ ์™„๋ฃŒ ํ›„ ์›ํ˜• ๋งˆ์Šคํ‚น ์ ์šฉ + setTimeout(() => { + applyCircularMask(scaledWidth, scaledHeight); + }, 100); + } + }, [text, deviceInfo, entryMenu, nowMenu, isBillingProductVisible, width, height]); + + return
; +} diff --git a/com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.module.less b/com.twin.app.shoptime/src/components/TQRCode/TQRCodeNew.module.less new file mode 100644 index 00000000..e69de29b diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx index 4b6b0978..c35283d9 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -570,6 +570,21 @@ export default function ProductAllSection({ [productType, themeProductInfo, themeProducts, selectedIndex, productInfo] ); + // ๐Ÿ†• [251211] patnrId=21์ธ ๊ฒฝ์šฐ QR ๋ฐ์ดํ„ฐ ํ™•์ธ + useEffect(() => { + if (productData?.patnrId === 21 || productData?.patnrId === "21") { + console.log('[QR-Data] patnrId=21 QR ๋ฐ์ดํ„ฐ ํ™•์ธ:', { + patnrId: productData?.patnrId, + prdtId: productData?.prdtId, + qrImgUrl: productData?.qrImgUrl, + qrCodeUrl: productData?.qrCodeUrl, + hasQrImgUrl: !!productData?.qrImgUrl, + hasQrCodeUrl: !!productData?.qrCodeUrl, + allData: productData, + }); + } + }, [productData]); + // ๋‹จํ’ˆ(๊ฒฐ์ œ ๊ฐ€๋Šฅ ์ƒํ’ˆ) - DetailPanel.backup.jsx์™€ ๋™์ผํ•œ ๋กœ์ง const isBillingProductVisible = useMemo(() => { // API Mode: ๊ธฐ์กด ๋กœ์ง 100% ์œ ์ง€ (์ ˆ๋Œ€ ์ˆ˜์ • ์•ˆ ํ•จ) diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductInfoSection/QRCode/QRCode.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductInfoSection/QRCode/QRCode.jsx index 35f1f1c6..9da15d9d 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductInfoSection/QRCode/QRCode.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductInfoSection/QRCode/QRCode.jsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; import { useSelector } from 'react-redux'; import TQRCode from '../../../../components/TQRCode/TQRCode'; +import TQRCodeNew from '../../../../components/TQRCode/TQRCodeNew'; import { getQRCodeUrl } from '../../../../utils/helperMethods'; import css from './QRCode.module.less'; @@ -56,13 +57,26 @@ export default function QRCode({ return detailUrl; }, [productInfo, isShopByMobile, detailUrl]); + // patnrId === 21์ธ ๊ฒฝ์šฐ TQRCodeNew ์‚ฌ์šฉ (์›ํ˜• QR์ฝ”๋“œ) + const isPatnrId21 = productInfo?.patnrId === 21 || productInfo?.patnrId === "21"; + return (
{/* {qrCodeUrl && } */} {kind === "detail" ? ( - + isPatnrId21 ? ( + + ) : ( + + ) ) : ( - qrCodeUrl && + qrCodeUrl && ( + isPatnrId21 ? ( + + ) : ( + + ) + ) )} {/* todo : ์‹œ๋‚˜๋ฆฌ์˜ค,UI ๋ฆด๋ฆฌ์ฆˆ ํ›„ */}
diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx index af115f70..d4afc1c4 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.jsx @@ -2,13 +2,10 @@ import React, { memo, useCallback, useState } from "react"; import { useDispatch } from "react-redux"; import { setShowPopup } from "../../../actions/commonActions"; import CustomImage from "../../../components/CustomImage/CustomImage"; -import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; +import Spottable from "@enact/spotlight/Spottable"; import css from "./TopBannerImage.module.less"; -const Container = SpotlightContainerDecorator( - { leaveFor: { left: "", right: "", down: "best-seller-spotlightId" }, enterTo: "default-element" }, - "div" -); +const SpottableDiv = Spottable("div"); const TopBannerImage = memo(({ banrImgUrl, banrImgNm, banrNm, pupBanrImgUrl, pupBanrImgNm, spotlightId }) => { console.log("[TOP-BANNER-IMG] Rendering with URL:", banrImgUrl); @@ -51,10 +48,9 @@ const TopBannerImage = memo(({ banrImgUrl, banrImgNm, banrNm, pupBanrImgUrl, pup } return ( - - + ); }); diff --git a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.module.less b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.module.less index b294d855..ce715027 100644 --- a/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.module.less +++ b/com.twin.app.shoptime/src/views/FeaturedBrandsPanel/TopBannerImage/TopBannerImage.module.less @@ -1,25 +1,32 @@ +@import "../../../style/CommonStyle.module.less"; +@import "../../../style/utils.module.less"; + .topBannerContainer { position: absolute; right: 60px; top: 48px; padding: 15px; background-color: transparent; + cursor: pointer; - // Spotlight ํฌ์ปค์Šค ์Šคํƒ€์ผ + // Spotlight ํฌ์ปค์Šค ์Šคํƒ€์ผ (TItemCard ๋ฐฉ์‹) &:focus { + &::after { + .focused(@boxShadow: 10px, @borderRadius: 4px); + } + } + + // ๋งˆ์šฐ์Šค ํ˜ธ๋ฒ„ ์Šคํƒ€์ผ + &:hover { outline: 2px solid #fff; outline-offset: 2px; background-color: rgba(255, 255, 255, 0.1); } - - // Spotlight hover ํšจ๊ณผ - &[data-spotlight-id] { - cursor: pointer; - } } .topBannerImage { display: block; + pointer-events: none; // ํฌ๊ธฐ๋Š” JavaScript์—์„œ ๋™์ ์œผ๋กœ ์„ค์ • border-radius: 4px; } \ No newline at end of file