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 383b0a4d..041ffe4a 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -39,12 +39,12 @@ import ShowUserReviews from "../../UserReview/ShowUserReviews"; // CSS imports // import infoCSS from "../ProductInfoSection/ProductInfoSection.module.less"; // import contentCSS from "../ProductContentSection/ProductContentSection.module.less"; -import css from "./ProductAllSection.module.less"; +import css from './ProductAllSection.module.less'; const Container = SpotlightContainerDecorator( - { - enterTo: "last-focused", - preserveld: true, + { + enterTo: "last-focused", + preserveld: true, leaveFor: { right: "content-scroller-container" }, spotlightDirection: "vertical" }, @@ -52,10 +52,10 @@ const Container = SpotlightContainerDecorator( ); const ContentContainer = SpotlightContainerDecorator( - { - enterTo: "default-element", - preserveld: true, - leaveFor: { + { + enterTo: "default-element", + preserveld: true, + leaveFor: { left: "spotlight-product-info-section-container" }, restrict: "none", @@ -65,9 +65,9 @@ const ContentContainer = SpotlightContainerDecorator( ); const HorizontalContainer = SpotlightContainerDecorator( - { - enterTo: "last-focused", - preserveld: true, + { + enterTo: "last-focused", + preserveld: true, defaultElement: "spotlight-product-info-section-container", spotlightDirection: "horizontal" }, @@ -110,7 +110,7 @@ const extractProductMeta = (productInfo) => ({ const SpottableComponent = Spottable("div"); const LayoutSample = ({ onClick }) => ( - getProductData(productType, themeProductInfo, productInfo), [productType, themeProductInfo, productInfo] ); // useReviews Hook 사용 - 모든 리뷰 관련 로직을 담당 - const { - previewReviews, - stats, + const { + previewReviews, + stats, isLoading: reviewsLoading, hasReviews // 리뷰 존재 여부 플래그 추가 } = useReviews(productData.prdtId); @@ -179,7 +179,7 @@ export default function ProductAllSection({ }) ); }, [dispatch, productData, stats]); - + // 디버깅: 실제 이미지 데이터 확인 useEffect(() => { console.log("[ProductId] ProductAllSection productData check:", { @@ -206,7 +206,7 @@ export default function ProductAllSection({ const [mobileSendPopupOpen, setMobileSendPopupOpen] = useState(false); - + // useReviews에서 모든 리뷰 데이터 관리 const reviewTotalCount = stats.totalReviews; const reviewData = { reviewList: previewReviews, reviewDetail: { totRvwCnt: stats.totalReviews, avgRvwScr: stats.averageRating } }; @@ -281,7 +281,7 @@ export default function ProductAllSection({ {/* Left Margin Section - 60px */}
- + {/* Info Section - 645px */}
- {revwGrd && ( + {revwGrd && revwGrd !== "0.0" && (
- +
@@ -332,6 +336,7 @@ export default function ProductAllSection({ selectedPrdtId={panelInfo && panelInfo.prdtId} favoriteFlag={favoriteFlag} onFavoriteFlagChanged={onFavoriteFlagChanged} + kind={"item_detail"} />
)} @@ -363,7 +368,7 @@ export default function ProductAllSection({ {$L("PRODUCT DETAILS")} {hasReviews && ( - {productData && productData.imgUrls600 && productData.imgUrls600.length > 0 ? ( productData.imgUrls600.map((image, index) => ( - )) ) : ( @@ -452,8 +457,8 @@ export default function ProductAllSection({ <>
- div { + height: 339px; + } } // 3. Content Section - 1180px (1114px 콘텐츠 + 66px 스크롤바) .contentSection { position: absolute; - left: 705px; // 60px + 645px + left: 705px; // 60px + 645px top: 0; - width: 1200px; // 30px 마진 + 1114px 콘텐츠 + 66px 스크롤바 + width: 1210px; // 30px 마진 + 1114px 콘텐츠 + 66px 스크롤바 height: 100%; padding: 0; margin: 0; @@ -84,11 +87,13 @@ flex-direction: column; justify-content: flex-start; align-items: flex-start; - + // gap 대신 margin 사용 (Chromium 68 호환성) > * { margin-bottom: 5px; - &:last-child { margin-bottom: 0; } + &:last-child { + margin-bottom: 0; + } } } @@ -114,7 +119,6 @@ justify-content: flex-start; align-items: flex-start; position: relative; // 자식 absolute 요소의 기준점 - // 스크롤러 오버라이드 (1210px = 30px + content + 스크롤바) .scrollerOverride { @@ -139,17 +143,16 @@ } &::-webkit-scrollbar-thumb { - background: #9C9C9C; // 스크롤바 색상 + background: #9c9c9c; // 스크롤바 색상 border-radius: 3px; // 스크롤바 둥근 모서리 } - // 스크롤바 thumb에 hover 효과 적용 + // 스크롤바 thumb에 hover 효과 적용 &:hover::-webkit-scrollbar-thumb { - background: #C72054; + background: #c72054; } } - - + // 내부 콘텐츠는 별도 너비 계산 없이 100%를 사용 > div { width: 100%; // 부모의 패딩을 제외한 나머지 공간(1114px)을 모두 사용 @@ -174,7 +177,7 @@ display: flex; flex-direction: column; align-items: flex-start; - + // ProductDetail.new 컴포넌트들 > div[class*="rollingWrap"] { width: 100% !important; // 부모 영역 전체 사용 @@ -182,9 +185,9 @@ box-sizing: border-box; } } - + #product-description-section, - #user-reviews-section, + #user-reviews-section, #you-may-also-like-section { width: 100%; // 부모 콘텐츠 영역 전체 사용 max-width: none; @@ -192,13 +195,13 @@ margin: 0; box-sizing: border-box; overflow: visible; - + > * { max-width: 100% !important; // 부모 영역 전체 사용 width: 100% !important; box-sizing: border-box; } - + // 이미지들이 컨테이너를 넘지 않도록 img { max-width: 100% !important; @@ -229,7 +232,6 @@ // (중복 제거됨) 최상위 스크롤러/섹션 정의는 .scrollerWrapper 중첩 내부로 이동 - // ProductDetailCard 스타일 참고 - 크기/간격만 적용 // 헤더 컨텐츠 영역 (태그, 별점) @@ -237,7 +239,6 @@ width: 100%; display: flex; align-items: center; - justify-content: space-between; } // 모바일 쇼핑 섹션 (mobileSection 참고) @@ -247,10 +248,12 @@ display: flex; justify-content: flex-start; align-items: center; - + > * { margin-right: 6px; - &:last-child { margin-right: 0; } + &:last-child { + margin-right: 0; + } } } @@ -258,15 +261,18 @@ flex: 1 1 0 !important; width: auto !important; // flex로 크기 조정 height: 60px !important; - background: rgba(68, 68, 68, 0.50) !important; + background: rgba(68, 68, 68, 0.5) !important; border-radius: 6px !important; border: none !important; padding: 0 !important; - margin: 0 !important; + margin: 0; display: flex !important; align-items: center !important; justify-content: center !important; - + &.shopByMobileOne { + margin: 0 6px 0 0; + } + .shopByMobileText { color: white !important; font-size: 25px !important; @@ -275,12 +281,484 @@ line-height: 35px !important; text-align: center !important; } - + // 포커스 상태 추가 &:focus { background: @PRIMARY_COLOR_RED !important; // 포커스시 빨간색 배경 outline: 2px solid @PRIMARY_COLOR_RED !important; - + + .shopByMobileText { + color: white !important; // 포커스시에도 텍스트는 흰색 유지 + } + } +} + +.favoriteBtnWrapper { + width: 60px; + height: 60px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + margin-left: 6px; + // 배경색과 라운드는 FavoriteBtn 내부에서 처리하므로 제거 +} + +// 주문 전화 섹션 (callToOrderSection 참고) +.callToOrderSection { + align-self: stretch; + height: 40px; + padding: 17px 30px; + border-radius: 6px; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px !important; + .callToOrderText { + color: #eaeaea; + font-size: 25px; + font-family: @baseFont; // LG Smart 폰트 사용 + font-weight: 400; // Bold에서 Regular로 변경 + line-height: 35px; + } + + .phoneSection { + padding: 0 1px; + display: flex; + align-items: center; + + > * { + margin-right: 10px; + &:last-child { + margin-right: 0; + } + } + + .phoneIconContainer { + width: 25px; + height: 25px; + position: relative; + overflow: hidden; + + .phoneIcon { + width: 24.94px; + height: 24.97px; + position: absolute; + left: 0; + top: 0; + // 전화 아이콘 이미지 또는 CSS로 구현 + background-image: url("../../../../assets/images/icons/ic-gr-call-1.png"); + background-size: contain; + background-repeat: no-repeat; + background-position: center; + } + } + + .phoneNumber { + color: #eaeaea; + font-size: 25px; + font-family: "LG Smart UI"; + font-weight: 700; + line-height: 35px; + } + } +} + +// 액션 버튼들 (actionButtons 참고) +.actionButtonsWrapper { + align-self: stretch; + padding-top: 20px; + display: flex; + flex-direction: column; + + > * { + margin-bottom: 5px; + &:last-child { + margin-bottom: 0; + } + } + &:last-child { + padding: 0; + } +} + +// 모든 버튼 기본 스타일 (PRODUCT DETAILS는 빨간색 아님!) +.productDetailsButton, +.userReviewsButton, +.youMayLikeButton { + align-self: stretch; + height: 60px; + background: rgba(255, 255, 255, 0.05); // 기본 회색 배경 + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + + color: #eaeaea; + font-size: 25px; + font-family: @baseFont; // LG Smart 폰트 사용 + font-weight: 400; // Bold에서 Regular로 변경 + line-height: 35px; + + &:focus { + background: #c72054; // 포커스시만 빨간색 + } +} + +.themeButton { + align-self: stretch; + height: 60px; + background: rgba(255, 255, 255, 0.05); + border-radius: 6px; + margin-top: 10px; + + &:focus { + background: #c72054; + } +} + +// QR 래퍼 (imageSection 참고 - 240px 고정) +.qrWrapper { + width: 240px; + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: flex-end; + + > * { + margin-bottom: 10px; + &:last-child { + margin-bottom: 0; + } + } +} + +// ProductOverview 컨테이너 스타일 수정 (자식 요소에 맞게 크기 조정) +[class*="ProductOverview"] { + padding: 0 0 5px; + // 내부 div (productInfoWrapper) + > div { + align-self: stretch; + justify-content: flex-start; + align-items: flex-start; + } + > div:first-child { + text-align: left; + } +} + +// LayoutSample 포커스 테스트용 스타일 +.layoutSample { + width: 1124px; + height: 300px; + background-color: yellow; + margin-bottom: 20px; + display: flex; + justify-content: center; + align-items: center; + color: black; + font-size: 24px; + font-weight: bold; + cursor: pointer; + position: relative; + border-radius: 8px; + + &:focus { + &::after { + .focused(@boxShadow:22px, @borderRadius:8px); + } + } +} + +@import "../../../style/CommonStyle.module.less"; +@import "../../../style/utils.module.less"; + +// 1920px 화면 기준 전체 구조 (절대 위치로 정확히 배치) +.detailArea { + width: 1920px; // 명시적으로 화면 크기 설정 + height: 100%; + padding: 0; // 모든 패딩 제거 + margin: 0; // 모든 마진 제거 + display: flex; + justify-content: flex-start; + align-items: flex-start; + position: relative; // 절대 위치 기준점 + + // Spotlight 좌우 이동을 위한 설정 + &:focus-within { + outline: none; + } +} + +// 1. Left Margin Section - 60px +.leftMarginSection { + position: absolute; + left: 0; + top: 0; + width: 60px; + height: 100%; + padding: 0; + margin: 0; + background: transparent; +} + +// 2. Info Section - 645px +.infoSection { + position: absolute; + left: 60px; + top: 0; + width: 650px; + height: 100%; + padding: 0; + margin: 0; + display: flex; + justify-content: flex-start; + align-items: flex-start; + > div { + height: 339px; + } +} + +// 3. Content Section - 1180px (1114px 콘텐츠 + 66px 스크롤바) +.contentSection { + position: absolute; + left: 705px; // 60px + 645px + top: 0; + width: 1210px; // 30px 마진 + 1114px 콘텐츠 + 66px 스크롤바 + height: 100%; + padding: 0; + margin: 0; + display: flex; + justify-content: flex-start; + align-items: flex-start; +} + +// 4. Scroll Section - 66px (삭제 - contentSection에 포함) +.scrollSection { + display: none; // 사용하지 않음 +} + +// 왼쪽 영역 컨테이너 (infoSection 내부) +.leftInfoContainer { + width: 635px; // 실제 콘텐츠 영역 + margin-right: 10px; // 우측 10px 간격 + padding: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + height: 100%; +} + +// 왼쪽 영역 내부 래퍼 +.leftInfoWrapper { + width: 100%; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + + // gap 대신 margin 사용 (Chromium 68 호환성) + > * { + margin-bottom: 5px; + &:last-child { + margin-bottom: 0; + } + } +} + +// 오른쪽 영역 컨테이너 (contentSection 내부) +.rightContentContainer { + width: 1210px; // 30px 마진 + 1114px 콘텐츠 + 66px 스크롤바 + height: @globalHeight - 136px; + padding: 0; + margin: 0; + overflow: hidden; + display: flex; + justify-content: flex-start; + align-items: flex-start; + + // 스크롤러 래퍼 (contentSection 내부) + .scrollerWrapper { + width: 1210px; // 30px 마진 + 1114px 콘텐츠 + 66px 스크롤바 (절대값) + height: 100%; + padding: 0; + margin: 0; + overflow: visible; // hidden에서 visible로 변경 + display: flex; + justify-content: flex-start; + align-items: flex-start; + position: relative; // 자식 absolute 요소의 기준점 + + // 스크롤러 오버라이드 (1210px = 30px + content + 스크롤바) + .scrollerOverride { + width: 1210px; // 절대 크기 지정 + height: 100%; + // 좌측 30px, 우측 66px(스크롤바) 패딩을 명시적으로 적용 + padding: 0 10px 0 30px; + box-sizing: border-box; + margin: 0; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + + // 스크롤바 너비를 6px로 명확하게 설정 + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: transparent; // 트랙 배경은 투명하게 + } + + &::-webkit-scrollbar-thumb { + background: #9c9c9c; // 스크롤바 색상 + border-radius: 3px; // 스크롤바 둥근 모서리 + } + + // 스크롤바 thumb에 hover 효과 적용 + &:hover::-webkit-scrollbar-thumb { + background: #c72054; + } + } + + // 내부 콘텐츠는 별도 너비 계산 없이 100%를 사용 + > div { + width: 100%; // 부모의 패딩을 제외한 나머지 공간(1114px)을 모두 사용 + padding: 0; + margin: 0; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + overflow: visible; + } + + // 스크롤 콘텐츠 내부의 모든 섹션들 (패딩 제거 - scrollerOverride에서 처리함) + #product-details-section { + width: 1124px; // 부모 콘텐츠 영역 전체 사용 + max-width: none; + padding: 0; + margin: 0; + box-sizing: border-box; + overflow: visible; + display: flex; + flex-direction: column; + align-items: flex-start; + + // ProductDetail.new 컴포넌트들 + > div[class*="rollingWrap"] { + width: 100% !important; // 부모 영역 전체 사용 + max-width: none !important; + box-sizing: border-box; + } + } + + #product-description-section, + #user-reviews-section, + #you-may-also-like-section { + width: 100%; // 부모 콘텐츠 영역 전체 사용 + max-width: none; + padding: 0; + margin: 0; + box-sizing: border-box; + overflow: visible; + + > * { + max-width: 100% !important; // 부모 영역 전체 사용 + width: 100% !important; + box-sizing: border-box; + } + + // 이미지들이 컨테이너를 넘지 않도록 + img { + max-width: 100% !important; + width: auto !important; + height: auto; + } + } + + // 상품 상세 영역 (마진 포함 크기) + .productDetail { + width: 1124px; // 1114px + 10px + max-width: 1124px; + height: auto; + display: flex; + flex-direction: column; + align-items: stretch; // 자식 요소들이 전체 너비 사용하도록 + } + + // 스크롤 마커 + .scrollMarker { + height: 1px; + visibility: hidden; + } + } // .scrollerWrapper +} // .rightContentContainer + +// (중복 제거됨) .scrollerWrapper는 .rightContentContainer 하위로 중첩 이동 + +// (중복 제거됨) 최상위 스크롤러/섹션 정의는 .scrollerWrapper 중첩 내부로 이동 + +// ProductDetailCard 스타일 참고 - 크기/간격만 적용 + +// 헤더 컨텐츠 영역 (태그, 별점) +.headerContent { + width: 100%; + display: flex; + align-items: center; +} + +// 모바일 쇼핑 섹션 (mobileSection 참고) +.buttonContainer { + align-self: stretch; + padding-top: 19px; + display: flex; + justify-content: flex-start; + align-items: center; + + > * { + margin-right: 6px; + &:last-child { + margin-right: 0; + } + } +} + +.shopByMobileButton { + flex: 1 1 0 !important; + width: auto !important; // flex로 크기 조정 + height: 60px !important; + background: rgba(68, 68, 68, 0.5) !important; + border-radius: 6px !important; + border: none !important; + padding: 0 !important; + margin: 0; + display: flex !important; + align-items: center !important; + justify-content: center !important; + &.shopByMobileOne { + margin: 0 6px 0 0; + } + + .shopByMobileText { + color: white !important; + font-size: 25px !important; + font-family: @baseFont !important; // LG Smart 폰트 사용 + font-weight: 400 !important; // Bold에서 Regular로 변경 + line-height: 35px !important; + text-align: center !important; + } + + // 포커스 상태 추가 + &:focus { + background: @PRIMARY_COLOR_RED !important; // 포커스시 빨간색 배경 + outline: 2px solid @PRIMARY_COLOR_RED !important; + .shopByMobileText { color: white !important; // 포커스시에도 텍스트는 흰색 유지 } @@ -306,38 +784,39 @@ display: flex; justify-content: space-between; align-items: center; - + .callToOrderText { - color: #EAEAEA; + color: #eaeaea; font-size: 25px; font-family: @baseFont; // LG Smart 폰트 사용 font-weight: 400; // Bold에서 Regular로 변경 line-height: 35px; } - + .phoneSection { padding: 0 1px; display: flex; align-items: center; - + > * { margin-right: 10px; - &:last-child { margin-right: 0; } + &:last-child { + margin-right: 0; + } } - + .phoneIconContainer { width: 25px; height: 25px; position: relative; overflow: hidden; - + .phoneIcon { width: 24.94px; height: 24.97px; position: absolute; left: 0; top: 0; - background: #EAEAEA; // 전화 아이콘 이미지 또는 CSS로 구현 background-image: url("../../../../assets/images/icons/ic-gr-call-1.png"); background-size: contain; @@ -345,11 +824,11 @@ background-position: center; } } - + .phoneNumber { - color: #EAEAEA; + color: #eaeaea; font-size: 25px; - font-family: 'LG Smart UI'; + font-family: "LG Smart UI"; font-weight: 700; line-height: 35px; } @@ -362,10 +841,15 @@ padding-top: 20px; display: flex; flex-direction: column; - + > * { margin-bottom: 5px; - &:last-child { margin-bottom: 0; } + &:last-child { + margin-bottom: 0; + } + } + &:last-child { + padding: 0; } } @@ -380,15 +864,15 @@ display: flex; align-items: center; justify-content: center; - - color: #EAEAEA; + + color: #eaeaea; font-size: 25px; font-family: @baseFont; // LG Smart 폰트 사용 font-weight: 400; // Bold에서 Regular로 변경 line-height: 35px; - + &:focus { - background: #C72054; // 포커스시만 빨간색 + background: #c72054; // 포커스시만 빨간색 } } @@ -398,9 +882,9 @@ background: rgba(255, 255, 255, 0.05); border-radius: 6px; margin-top: 10px; - + &:focus { - background: #C72054; + background: #c72054; } } @@ -411,65 +895,34 @@ display: flex; flex-direction: column; align-items: flex-end; - + > * { margin-bottom: 10px; - &:last-child { margin-bottom: 0; } + &:last-child { + margin-bottom: 0; + } } } // ProductOverview 컨테이너 스타일 수정 (자식 요소에 맞게 크기 조정) [class*="ProductOverview"] { - align-self: stretch; - padding: 0 0 5px; // ProductDetailCard mainContent와 동일한 패딩 - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - + padding: 0 0 5px; // 내부 div (productInfoWrapper) > div { align-self: stretch; - display: flex; justify-content: flex-start; align-items: flex-start; - - > * { - margin-right: 15px; // ProductDetailCard와 동일한 간격 - &:last-child { margin-right: 0; } - } - - // 가격 섹션 (flex로 남은 공간 차지) - > div:first-child { - flex: 1 1 0; - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - } - - // 이미지 섹션 (QR 포함, 고정 크기) - > div:last-child { - width: 240px; - flex-shrink: 0; - display: flex; - flex-direction: column; - align-items: flex-end; - - > * { - margin-bottom: 10px; - &:last-child { margin-bottom: 0; } - } - } + } + > div:first-child { + text-align: left; } } // LayoutSample 포커스 테스트용 스타일 .layoutSample { width: 1124px; - height: 35px; + height: 300px; background-color: yellow; - // border: 2px solid white; margin-bottom: 20px; display: flex; justify-content: center; @@ -480,7 +933,7 @@ cursor: pointer; position: relative; border-radius: 8px; - + &:focus { &::after { .focused(@boxShadow:22px, @borderRadius:8px); diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductDescription/ProductDescription.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductDescription/ProductDescription.module.less index d465ee52..9075b77c 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductDescription/ProductDescription.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/ProductDescription/ProductDescription.module.less @@ -17,7 +17,7 @@ padding: 10px; border-radius: 6px; cursor: pointer; - + &:focus { background-color: rgba(255, 255, 255, 0.1); outline: 2px solid @PRIMARY_COLOR_RED; @@ -26,7 +26,7 @@ .title { .font(@fontFamily: @baseFont, @fontSize: 30px); font-weight: 700; - margin: 90px 0 20px 0; + margin: 30px 0 20px 0; } } @@ -34,7 +34,7 @@ width: 100%; border-radius: 12px; cursor: pointer; - + &:focus { outline: 6px solid @PRIMARY_COLOR_RED; outline-offset: 2px; diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.jsx index 120ca946..8176db49 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.jsx @@ -1,30 +1,43 @@ -import React, { useCallback, useEffect, useState } from "react"; -import css from "./CustomerImages.module.less"; -import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; -import Spottable from "@enact/spotlight/Spottable"; -import THeader from "../../../../../components/THeader/THeader"; -import { $L } from "../../../../../utils/helperMethods"; -import classNames from "classnames"; -import Spotlight from "@enact/spotlight"; +import React, { + useCallback, + useEffect, + useState, +} from 'react'; + +import classNames from 'classnames'; + +import Spotlight from '@enact/spotlight'; +import SpotlightContainerDecorator + from '@enact/spotlight/SpotlightContainerDecorator'; +import Spottable from '@enact/spotlight/Spottable'; + +import THeader from '../../../../../components/THeader/THeader'; +import { $L } from '../../../../../utils/helperMethods'; +import css from './CustomerImages.module.less'; const Container = SpotlightContainerDecorator( - { - enterTo: "default-element", + { + enterTo: "default-element", preserveld: true, - leaveFor: { - left: "spotlight-product-info-section-container" - } + leaveFor: { + left: "spotlight-product-info-section-container", + }, }, "div" ); const SpottableComponent = Spottable("div"); -export default function CustomerImages({ onImageClick, onViewMoreClick, imageData }) { +export default function CustomerImages({ + onImageClick, + onViewMoreClick, + imageData, +}) { // Props로 전달받은 imageData 사용 (useReviews Hook에서 추출된 이미지 데이터) const [selectedIndex, setSelectedIndex] = useState(null); const [currentPage, setCurrentPage] = useState(1); const IMAGES_PER_PAGE = 5; + const [focusIdx, setFocusIdx] = useState(0); // [CustomerImages] useReviews 이미지 데이터 수신 확인 // useEffect(() => { @@ -43,7 +56,7 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat const handleReviewImageClick = (index) => { setSelectedIndex(index); - + // 이미지 클릭 시 All-Images 모드로 팝업 열기 if (onImageClick) { onImageClick(index); @@ -58,25 +71,35 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat }; // 키 이벤트 처리 (왼쪽 화살표, Enter 키) - const handleKeyDown = useCallback((ev, index) => { - if (ev.keyCode === 37) { // 왼쪽 화살표 키 - ev.preventDefault(); - ev.stopPropagation(); - console.log("[CustomerImages] Left arrow pressed, focusing product-details-button"); - Spotlight.focus("product-details-button"); - } else if (ev.keyCode === 13) { // Enter 키 - ev.preventDefault(); - ev.stopPropagation(); - console.log("[CustomerImages] Enter pressed on image:", index); - handleReviewImageClick(index); - } - }, [handleReviewImageClick]); + const handleKeyDown = useCallback( + (ev, index) => { + if (ev.keyCode === 37) { + // 왼쪽 화살표 키 + ev.preventDefault(); + ev.stopPropagation(); + console.log( + "[CustomerImages] Left arrow pressed, focusing product-details-button" + ); + Spotlight.focus("product-details-button"); + } else if (ev.keyCode === 13) { + // Enter 키 + ev.preventDefault(); + ev.stopPropagation(); + console.log("[CustomerImages] Enter pressed on image:", index); + handleReviewImageClick(index); + } + }, + [handleReviewImageClick] + ); // 이미지가 없을 때도 컴포넌트를 렌더링하되 내용은 표시하지 않음 return ( <> - + {imageData && imageData.length > 0 ? (
{(() => { @@ -84,7 +107,7 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat const endIndex = startIndex + IMAGES_PER_PAGE; const displayImages = imageData.slice(startIndex, endIndex); const hasMoreImages = imageData.length > endIndex; - + console.log("[CustomerImages] Pagination debug:", { currentPage, IMAGES_PER_PAGE, @@ -92,9 +115,9 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat startIndex, endIndex, displayImagesCount: displayImages.length, - hasMoreImages + hasMoreImages, }); - + return ( <> {displayImages.map((reviewImage, displayIndex) => { @@ -105,16 +128,22 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat handleReviewImageClick(actualIndex)} onKeyDown={(ev) => handleKeyDown(ev, actualIndex)} spotlightId={`customer-image-${actualIndex}`} + onFocus={() => + displayIndex < 5 ? setFocusIdx(displayIndex) : "" + } > - {`Review { /* console.log(`[CustomerImages] Image loaded successfully:`, { @@ -124,19 +153,22 @@ export default function CustomerImages({ onImageClick, onViewMoreClick, imageDat }); */ }} onError={(e) => { - console.error(`[CustomerImages] Image load failed:`, { - index: actualIndex, - imgUrl, - imgId, - error: e.target.error - }); - e.target.style.display = 'none'; + console.error( + `[CustomerImages] Image load failed:`, + { + index: actualIndex, + imgUrl, + imgId, + error: e.target.error, + } + ); + e.target.style.display = "none"; }} /> ); })} - + {hasMoreImages && ( ) : (
-
- {imageData ? 'No customer images available' : 'Loading customer images...'} +
+ {imageData + ? "No customer images available" + : "Loading customer images..."}
)} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.module.less index da6f1108..6a81578e 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/CustomerImages/CustomerImages.module.less @@ -3,7 +3,7 @@ .container { width: 1124px; - height: 236px; + height: 299px; max-width: 1124px; display: flex; flex-direction: column; @@ -14,7 +14,7 @@ margin: 0 0 10px 0; > div { - padding: 0; + padding: 0 0 0 20px; > span { .font(@fontFamily: @baseFont, @fontSize: 24px); font-weight: 400; @@ -26,7 +26,7 @@ .wrapper { width: 100%; - height: 190px; + height: 252px; display: flex; justify-content: flex-start; align-items: center; @@ -36,13 +36,25 @@ overflow: visible; .reviewCard { - width: calc((100% - 75px) / 6); // 6개 슬롯(5이미지+1버튼), margin-right 15px * 5 = 75px - height: 150px; + // width: calc( + // (100% - 75px) / 6 + // ); // 6개 슬롯(5이미지+1버튼), margin-right 15px * 5 = 75px + // height: 150px; + width: 157.3px; + height: 192.5px; position: relative; flex-shrink: 0; margin-right: 15px; - + + &.focused { + width: 211.75px; + height: 211.75px; + } + &:focus { + //포커스시 확대 + // width: 211.75px; + // height: 211.75px; &::after { .focused(@boxShadow:22px, @borderRadius:12px); } @@ -61,13 +73,18 @@ } .viewMoreButton { - width: calc((100% - 75px) / 6); // reviewCard와 동일한 크기 - height: 150px; + // width: calc((100% - 75px) / 6); // reviewCard와 동일한 크기 + // height: 150px; + width: 157.3px; + height: 192.5px; position: relative; flex-shrink: 0; cursor: pointer; - + &:focus { + // //포커스시 확대 + // width: 211.75px; + // height: 211.75px; &::after { .focused(@boxShadow:22px, @borderRadius:12px); } @@ -77,7 +94,11 @@ width: 100%; height: 100%; padding: 4px; - background: linear-gradient(0deg, rgba(0, 0, 0, 0.80) 0%, rgba(0, 0, 0, 0.80) 100%); + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.8) 0%, + rgba(0, 0, 0, 0.8) 100% + ); border-radius: 12px; display: flex; justify-content: center; diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewDetail/UserReviewDetail.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewDetail/UserReviewDetail.jsx index 5cd28bb5..7c8dc715 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewDetail/UserReviewDetail.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewDetail/UserReviewDetail.jsx @@ -1,9 +1,12 @@ -import React, { useCallback } from "react"; -import classNames from "classnames"; -import Spottable from "@enact/spotlight/Spottable"; -import StarRating from "../../../components/StarRating"; -import { $L } from "../../../../../utils/helperMethods"; -import css from "./UserReviewDetail.module.less"; +import React, { useCallback } from 'react'; + +import classNames from 'classnames'; + +import Spottable from '@enact/spotlight/Spottable'; + +import { $L } from '../../../../../utils/helperMethods'; +import StarRating from '../../../components/StarRating'; +import css from './UserReviewDetail.module.less'; const SpottableButton = Spottable("div"); @@ -36,9 +39,15 @@ export default function UserReviewDetail({ ); } - const reviewImage = currentReview.reviewImageList && currentReview.reviewImageList[0]; + const reviewImage = + currentReview.reviewImageList && currentReview.reviewImageList[0]; const hasMultipleReviews = totalReviews > 1; + const formatDate = (dateStr) => { + const [year, month, day] = dateStr.split("-"); + return `${year.slice(2)}.${month}.${day}`; + }; + return (
{/* Left Arrow - 이전 리뷰가 있을 때만 표시 */} @@ -57,14 +66,14 @@ export default function UserReviewDetail({ {/* Review Image */} {reviewImage && (
- Review image
)} - + {/* Review Info */}
{/* Rating and Meta */} @@ -78,31 +87,29 @@ export default function UserReviewDetail({ />
)} - + {/* Email */} {(currentReview.wrtrNknm || currentReview.rvwWrtrId) && (
{currentReview.wrtrNknm || currentReview.rvwWrtrId}
)} - + {/* Date */} {currentReview.rvwRgstDtt && (
- {currentReview.rvwRgstDtt} + {formatDate(currentReview.rvwRgstDtt)}
)}
- + {/* Review Text */} {currentReview.rvwCtnt && ( -
- "{currentReview.rvwCtnt}" -
+
"{currentReview.rvwCtnt}"
)}
- + {/* Custom Scrollbar (피그마 스타일) */}
@@ -120,4 +127,4 @@ export default function UserReviewDetail({ )}
); -} \ No newline at end of file +} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx index 4e6c5f0b..7d6b6da2 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.jsx @@ -1,18 +1,32 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import css from "./UserReviews.module.less"; -import UserReviewsScroller from "../../components/UserReviewsScroller/UserReviewsScroller"; -import useScrollTo from "../../../../hooks/useScrollTo"; -import THeaderDetail from "../../components/THeaderDetail"; -import { $L } from "../../../../utils/helperMethods"; -import { useMemo } from "react"; -import Spottable from "@enact/spotlight/Spottable"; -import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; -import { useDispatch, useSelector } from "react-redux"; -import { toggleShowAllReviews } from "../../../../actions/productActions"; -import useReviews from "../../../../hooks/useReviews/useReviews"; -import StarRating from "../../components/StarRating"; -import CustomerImages from "./CustomerImages/CustomerImages"; -import UserReviewsPopup from "./UserReviewsPopup/UserReviewsPopup"; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; + +import classNames from 'classnames'; +import { + useDispatch, + useSelector, +} from 'react-redux'; + +import SpotlightContainerDecorator + from '@enact/spotlight/SpotlightContainerDecorator'; +import Spottable from '@enact/spotlight/Spottable'; + +import { toggleShowAllReviews } from '../../../../actions/productActions'; +import useReviews from '../../../../hooks/useReviews/useReviews'; +import useScrollTo from '../../../../hooks/useScrollTo'; +import { $L } from '../../../../utils/helperMethods'; +import StarRating from '../../components/StarRating'; +import THeaderDetail from '../../components/THeaderDetail'; +import UserReviewsScroller + from '../../components/UserReviewsScroller/UserReviewsScroller'; +import CustomerImages from './CustomerImages/CustomerImages'; +import css from './UserReviews.module.less'; +import UserReviewsPopup from './UserReviewsPopup/UserReviewsPopup'; const SpottableComponent = Spottable("div"); @@ -22,10 +36,10 @@ const Container = SpotlightContainerDecorator( preserveld: true, leaveFor: { left: "spotlight-product-info-section-container", - up: "view-all-reviews-button" + up: "view-all-reviews-button", }, restrict: "none", - spotlightDirection: "vertical" + spotlightDirection: "vertical", }, "div" ); @@ -35,32 +49,39 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { const dispatch = useDispatch(); const containerRef = useRef(null); const tScrollerRef = useRef(null); - + // ProductAllSection에서 전달받은 데이터 사용 (우선순위) // 없으면 자체 useReviews Hook 사용 (UserReviewPanel에서 직접 접근할 때) const fallbackReviews = useReviews(productInfo && productInfo.prdtId); - + const actualReviewsData = reviewsData || { previewReviews: fallbackReviews.previewReviews, stats: fallbackReviews.stats, - isLoading: fallbackReviews.isLoading + isLoading: fallbackReviews.isLoading, }; - + // 팝업 상태 관리 - 모드와 선택된 이미지 인덱스 추가 const [isPopupOpen, setIsPopupOpen] = useState(false); const [popupMode, setPopupMode] = useState("customer-images"); // "customer-images", "all-images" const [selectedImageIndex, setSelectedImageIndex] = useState(0); // Redux에서 showAllReviews 상태 가져오기 const showAllReviews = useSelector((state) => state.product.showAllReviews); - + // 디버깅: showAllReviews 상태 변경 확인 useEffect(() => { console.log("[UserReviews] showAllReviews state changed:", { showAllReviews, - reviewListLength: (actualReviewsData.previewReviews && actualReviewsData.previewReviews.length) || 0, - willShowCount: showAllReviews ? ((actualReviewsData.previewReviews && actualReviewsData.previewReviews.length) || 0) : 5, + reviewListLength: + (actualReviewsData.previewReviews && + actualReviewsData.previewReviews.length) || + 0, + willShowCount: showAllReviews + ? (actualReviewsData.previewReviews && + actualReviewsData.previewReviews.length) || + 0 + : 5, hasReviewsData: !!reviewsData, - isFromProductAllSection: !!reviewsData + isFromProductAllSection: !!reviewsData, }); }, [showAllReviews, actualReviewsData.previewReviews, reviewsData]); @@ -68,33 +89,36 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { useEffect(() => { if (showAllReviews && tScrollerRef.current) { // console.log("[UserReviews] Forcing TScroller to update scroll area for all reviews"); - + // 다음 렌더링 사이클 후 스크롤 영역 재계산 setTimeout(() => { if (tScrollerRef.current) { // TScroller의 스크롤 영역을 강제로 업데이트 - if (typeof tScrollerRef.current.calculateMetrics === 'function') { + if (typeof tScrollerRef.current.calculateMetrics === "function") { tScrollerRef.current.calculateMetrics(); } - + // 또는 scrollTo를 호출해서 스크롤 영역 업데이트 - if (typeof tScrollerRef.current.scrollTo === 'function') { - tScrollerRef.current.scrollTo({ position: { y: 0 }, animate: false }); + if (typeof tScrollerRef.current.scrollTo === "function") { + tScrollerRef.current.scrollTo({ + position: { y: 0 }, + animate: false, + }); } - + // console.log("[UserReviews] TScroller scroll area updated"); } }, 100); } }, [showAllReviews]); - + // actualReviewsData에서 데이터 가져오기 (ProductAllSection에서 전달받거나 자체 useReviews) const reviewListData = actualReviewsData.previewReviews; // 리뷰 리스트 (5개) const reviewTotalCount = actualReviewsData.stats.totalReviews; const reviewDetailData = { totRvwCnt: actualReviewsData.stats.totalReviews, avgRvwScr: actualReviewsData.stats.averageRating, - totRvwAvg: actualReviewsData.stats.averageRating + totRvwAvg: actualReviewsData.stats.averageRating, }; // [UserReviews] 데이터 수신 확인 로그 @@ -105,7 +129,9 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { averageRating: actualReviewsData.stats.averageRating, isLoading: actualReviewsData.isLoading, hasData: reviewListData && reviewListData.length > 0, - dataSource: reviewsData ? 'ProductAllSection props' : 'useReviews fallback' + dataSource: reviewsData + ? "ProductAllSection props" + : "useReviews fallback", }); }, [reviewListData, actualReviewsData, reviewsData]); @@ -118,26 +144,32 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { }; // 리뷰 클릭으로 User Reviews 모드 팝업 열기 - const handleReviewClick = useCallback((reviewIndex) => { - // 클릭한 리뷰 정보 (previewReviews에서) - const clickedReview = reviewListData[reviewIndex]; - - // 전체 리뷰에서 클릭한 리뷰의 실제 인덱스 찾기 - const realIndex = fallbackReviews.allReviews.findIndex( - review => review.rvwId === clickedReview.rvwId - ); - - console.log("[UserReviews] Review clicked, opening popup in User Reviews mode:", { - previewIndex: reviewIndex, - realIndex, - clickedReviewId: clickedReview.rvwId, - totalReviews: fallbackReviews.allReviews.length - }); - - setSelectedImageIndex(realIndex >= 0 ? realIndex : reviewIndex); - setPopupMode("user-reviews"); - setIsPopupOpen(true); - }, [reviewListData, fallbackReviews.allReviews]); + const handleReviewClick = useCallback( + (reviewIndex) => { + // 클릭한 리뷰 정보 (previewReviews에서) + const clickedReview = reviewListData[reviewIndex]; + + // 전체 리뷰에서 클릭한 리뷰의 실제 인덱스 찾기 + const realIndex = fallbackReviews.allReviews.findIndex( + (review) => review.rvwId === clickedReview.rvwId + ); + + console.log( + "[UserReviews] Review clicked, opening popup in User Reviews mode:", + { + previewIndex: reviewIndex, + realIndex, + clickedReviewId: clickedReview.rvwId, + totalReviews: fallbackReviews.allReviews.length, + } + ); + + setSelectedImageIndex(realIndex >= 0 ? realIndex : reviewIndex); + setPopupMode("user-reviews"); + setIsPopupOpen(true); + }, + [reviewListData, fallbackReviews.allReviews] + ); // 팝업 관련 핸들러들 // +View More 버튼으로 Customer Images 모드 팝업 열기 @@ -149,7 +181,10 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { // 이미지 클릭으로 All-Images 모드 팝업 열기 const handleOpenAllImagesPopup = useCallback((imageIndex = 0) => { - console.log("[UserReviews] Opening popup in All-Images mode, image index:", imageIndex); + console.log( + "[UserReviews] Opening popup in All-Images mode, image index:", + imageIndex + ); setSelectedImageIndex(imageIndex); setPopupMode("all-images"); setIsPopupOpen(true); @@ -164,7 +199,10 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { // 팝업 모드 변경 핸들러 const handleModeChange = useCallback((newMode, imageIndex = 0) => { - console.log("[UserReviews] Mode change requested:", { newMode, imageIndex }); + console.log("[UserReviews] Mode change requested:", { + newMode, + imageIndex, + }); setPopupMode(newMode); if (newMode === "all-images" || newMode === "user-reviews") { setSelectedImageIndex(imageIndex); @@ -173,9 +211,6 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { // handleImageClick 제거 - 더 이상 필요없음 - - - // customerImages 로직 제거 - useReviews의 extractImagesFromReviews 사용 return ( @@ -190,13 +225,15 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { verticalScrollbar="auto" cbScrollTo={getScrollTo} forceUpdate={showAllReviews} - key={showAllReviews ? `all-${(reviewListData && reviewListData.length) || 0}` : 'limited-5'} + key={ + showAllReviews + ? `all-${(reviewListData && reviewListData.length) || 0}` + : "limited-5" + } > {reviewDetailData && reviewDetailData.totRvwAvg && ( )} - {reviewListData && (() => { - const reviewsToShow = showAllReviews ? reviewListData : reviewListData.slice(0, 5); + const reviewsToShow = showAllReviews + ? reviewListData + : reviewListData.slice(0, 5); console.log("[UserReviews] Reviews to render:", { showAllReviews, totalReviews: reviewListData.length, reviewsToShowCount: reviewsToShow.length, isShowingAll: showAllReviews, - reviewListData: reviewListData + reviewListData: reviewListData, }); - + // 실제 렌더링될 각 리뷰 로그 reviewsToShow.forEach((review, index) => { - console.log(`[UserReviews] Review ${index + 1}/${reviewsToShow.length}:`, { - rvwId: review.rvwId, - rvwCtnt: (review.rvwCtnt && review.rvwCtnt.substring(0, 50)) + "...", - rvwRtng: review.rvwRtng, - hasImages: (review.reviewImageList && review.reviewImageList.length) || 0, - rvwRgstDtt: review.rvwRgstDtt, - fullReview: review - }); + console.log( + `[UserReviews] Review ${index + 1}/${reviewsToShow.length}:`, + { + rvwId: review.rvwId, + rvwCtnt: + (review.rvwCtnt && review.rvwCtnt.substring(0, 50)) + + "...", + rvwRtng: review.rvwRtng, + hasImages: + (review.reviewImageList && + review.reviewImageList.length) || + 0, + rvwRgstDtt: review.rvwRgstDtt, + fullReview: review, + } + ); }); - + return reviewsToShow; })().map((review, index, array) => { - const { reviewImageList, rvwRtng, rvwRgstDtt, rvwCtnt, rvwId, wrtrNknm, rvwWrtrId } = - review; + const { + reviewImageList, + rvwRtng, + rvwRgstDtt, + rvwCtnt, + rvwId, + wrtrNknm, + rvwWrtrId, + } = review; const isLastReview = index === array.length - 1; /* console.log(`[UserReviews] Rendering review ${index}:`, { rvwId, @@ -258,7 +312,11 @@ export default function UserReviews({ productInfo, panelInfo, reviewsData }) { aria-label={`user-reviews-:${rvwId}`} className={css.reviewContentContainer} onClick={() => handleReviewClick(index)} - spotlightId={isLastReview ? 'user-review-at-last' : `user-review-${index}`} + spotlightId={ + isLastReview + ? "user-review-at-last" + : `user-review-${index}` + } > {reviewImageList && reviewImageList.length > 0 && ( )}
- {rvwCtnt && ( -
{rvwCtnt}
- )} + {rvwCtnt &&
{rvwCtnt}
}
); })} -
- + {/* UserReviewsPopup 추가 - 모드별 데이터 전달 */} * { height: auto !important; @@ -28,14 +28,26 @@ margin-bottom: 20px; display: flex; align-items: center; + &.tHeaderDetail { + height: 56px; + max-width: none; + width: 100%; + justify-content: space-between; + } > div { .size(@w:100%,@h:100%); padding: 0; + &:first-child { + width: 100%; + } + } + > &.title { + width: 100%; } - .averageOverallRating { - .size(@w: 176px,@h:30px); + width: 176px; + height: 30px; display: flex; align-items: center; margin-left: auto; @@ -54,7 +66,7 @@ .reviewItem { width: 100%; height: 100%; - + margin-top: 10px; .showReviewsText { .size(@w:100%, @h:36px); .font(@fontFamily: @baseFont, @fontSize: 24px); @@ -71,7 +83,7 @@ .flex(@justifyCenter:flex-start); padding: 30px; position: relative; - + &:focus { &::after { .focused(@boxShadow:22px, @borderRadius:12px); @@ -92,10 +104,10 @@ display: flex; justify-content: flex-start; align-items: center; - + > * { margin-right: 20px; - + &:last-child { margin-right: 0; } @@ -146,7 +158,7 @@ width: auto; // "View All Reviews +" 한 줄 표시용으로 확장 height: 75px; // 20 + 35 + 20 cursor: pointer; - + &:focus { &::after { .focused(@boxShadow:22px, @borderRadius:6px); @@ -159,13 +171,13 @@ padding: 20px 30px; background: rgba(255, 255, 255, 0.05); border-radius: 6px; - border: 1px solid #EEEEEE; + border: 1px solid #eeeeee; display: flex; justify-content: flex-start; align-items: center; - + .viewAllReviewsText { - color: #EAEAEA; + color: #eaeaea; font-size: 24px; font-family: @baseFont; font-weight: 600; @@ -181,4 +193,4 @@ } } } -} \ No newline at end of file +} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.jsx index a61d79a7..9d258938 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.jsx @@ -1,13 +1,24 @@ -import React, { useCallback, useState, useEffect } from "react"; -import classNames from "classnames"; -import Spottable from "@enact/spotlight/Spottable"; -import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator"; -import TNewPopUp from "../../../../../components/TPopUp/TNewPopUp"; -import TButton from "../../../../../components/TButton/TButton"; -import { $L } from "../../../../../utils/helperMethods"; -import UserReviewDetail from "../UserReviewDetail/UserReviewDetail"; -import ImageSkeleton from "./ImageSkeleton/ImageSkeleton"; -import css from "./UserReviewsPopup.module.less"; +import React, { + useCallback, + useEffect, + useRef, + useState, +} from 'react'; + +import classNames from 'classnames'; + +import SpotlightContainerDecorator + from '@enact/spotlight/SpotlightContainerDecorator'; +import Spottable from '@enact/spotlight/Spottable'; + +import TButton from '../../../../../components/TButton/TButton'; +import TNewPopUp from '../../../../../components/TPopUp/TNewPopUp'; +import useScrollTo from '../../../../../hooks/useScrollTo'; +import { $L } from '../../../../../utils/helperMethods'; +import TScrollerDetail from '../../../components/TScroller/TScrollerDetail'; +import UserReviewDetail from '../UserReviewDetail/UserReviewDetail'; +import ImageSkeleton from './ImageSkeleton/ImageSkeleton'; +import css from './UserReviewsPopup.module.less'; const SpottableImage = Spottable("div"); @@ -24,7 +35,7 @@ const FooterContainer = SpotlightContainerDecorator( { enterTo: "default-element", preserveId: true, - defaultElement: "close-button" + defaultElement: "close-button", }, "div" ); @@ -40,6 +51,9 @@ export default function UserReviewsPopup({ onModeChange, // 모드 변경 콜백 함수 className, }) { + const scrollContainerRef = useRef(null); + const { getScrollTo } = useScrollTo(); + const handleClose = useCallback(() => { if (onClose) { onClose(); @@ -47,12 +61,18 @@ export default function UserReviewsPopup({ }, [onClose]); // Customer Images 모드에서 이미지 클릭 시 All Images 모드로 변경 - const handleCustomerImageClick = useCallback((index) => { - console.log("[UserReviewsPopup] Customer image clicked, switching to All Images mode, index:", index); - if (onModeChange) { - onModeChange("all-images", index); - } - }, [onModeChange]); + const handleCustomerImageClick = useCallback( + (index) => { + console.log( + "[UserReviewsPopup] Customer image clicked, switching to All Images mode, index:", + index + ); + if (onModeChange) { + onModeChange("all-images", index); + } + }, + [onModeChange] + ); // 모드별 헤더 정보 const getHeaderInfo = useCallback((mode) => { @@ -61,20 +81,20 @@ export default function UserReviewsPopup({ return { title: $L("All images"), hasIcon: true, - iconType: "all-images" + iconType: "all-images", }; case "user-reviews": return { title: $L("User Reviews"), hasIcon: true, - iconType: "user-reviews" + iconType: "user-reviews", }; case "customer-images": default: return { title: $L("Customer Images"), hasIcon: false, - iconType: null + iconType: null, }; } }, []); @@ -88,8 +108,8 @@ export default function UserReviewsPopup({ useEffect(() => { if (mode === "all-images" && images && images[selectedImageIndex]) { const selectedImage = images[selectedImageIndex]; - const reviewIndex = reviewsWithImages.findIndex(review => - review.rvwId === selectedImage.reviewId + const reviewIndex = reviewsWithImages.findIndex( + (review) => review.rvwId === selectedImage.reviewId ); if (reviewIndex !== -1) { setCurrentReviewIndex(reviewIndex); @@ -108,42 +128,39 @@ export default function UserReviewsPopup({ }, [currentReviewIndex]); const handleNextReview = useCallback(() => { - const maxIndex = mode === "user-reviews" - ? allReviews.length - 1 - : reviewsWithImages.length - 1; - + const maxIndex = + mode === "user-reviews" + ? allReviews.length - 1 + : reviewsWithImages.length - 1; + if (currentReviewIndex < maxIndex) { setCurrentReviewIndex(currentReviewIndex + 1); } }, [currentReviewIndex, mode, allReviews.length, reviewsWithImages.length]); // All Images 아이콘 컴포넌트 (PNG 파일 사용) - const AllImagesIcon = () => ( -
- ); + const AllImagesIcon = () =>
; // User Reviews 아이콘 컴포넌트 (추후 구현) const UserReviewsIcon = () => ( -
- {/* User Reviews 아이콘 내용 */} -
+
{/* User Reviews 아이콘 내용 */}
); // 이미지 로딩 상태 관리 const [imageLoadStates, setImageLoadStates] = useState({}); const handleImageLoad = useCallback((index) => { - setImageLoadStates(prev => ({ + setImageLoadStates((prev) => ({ ...prev, - [index]: true + [index]: true, })); }, []); const handleImageError = useCallback((index) => { console.error(`[UserReviewsPopup] Image load failed for index: ${index}`); - setImageLoadStates(prev => ({ + setImageLoadStates((prev) => ({ ...prev, - [index]: false + [index]: false, })); }, []); @@ -171,63 +188,76 @@ export default function UserReviewsPopup({ {headerInfo.iconType === "user-reviews" && } )} -
- {headerInfo.title} -
+
{headerInfo.title}
{/* Content - 모드별 내용 */} - {mode === "customer-images" && ( -
- {displayImages.map((image, index) => { - const isLoaded = imageLoadStates[index] === true; - const isFailed = imageLoadStates[index] === false; - - return ( - handleCustomerImageClick(index)} - > - {/* Skeleton UI - 이미지 로딩 중일 때 표시 */} - {!isLoaded && !isFailed && ( - - )} - - {/* 실제 이미지 */} - {`Customer handleImageLoad(index)} - onError={() => handleImageError(index)} - /> - - {/* 이미지 로드 실패 시 표시할 플레이스홀더 */} - {isFailed && ( -
-
이미지 없음
+ +
+ {displayImages.map((image, index) => { + const isLoaded = imageLoadStates[index] === true; + const isFailed = imageLoadStates[index] === false; + + return ( + handleCustomerImageClick(index)} + > +
+ {/* Skeleton UI - 이미지 로딩 중일 때 표시 */} + {!isLoaded && !isFailed && ( + + )} + + {/* 실제 이미지 */} + {`Customer handleImageLoad(index)} + onError={() => handleImageError(index)} + /> + + {/* 이미지 로드 실패 시 표시할 플레이스홀더 */} + {isFailed && ( +
+
+ 이미지 없음 +
+
+ )}
- )} -
- ); - })} -
+ + ); + })} +
+ )} - + {mode === "all-images" && ( )} - + {mode === "user-reviews" && ( ); -} \ No newline at end of file +} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.module.less index 9cc652bd..b3f35cde 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup.module.less @@ -12,16 +12,16 @@ flex-direction: column; justify-content: center; align-items: center; - + // Header 영역 - 모드별 아이콘과 제목 지원 (기존 레이아웃 유지) .header { align-self: stretch; padding: 30px; - background: #E7EBEF; + background: #e7ebef; display: flex; justify-content: flex-start; align-items: center; - + // All Images 아이콘 (PNG 파일 사용) .headerIcon { width: 52px; @@ -32,7 +32,7 @@ background-repeat: no-repeat; margin-right: 15px; // gap: 15 → margin-right: 15px } - + .headerTitle { text-align: center; color: black; @@ -43,7 +43,7 @@ word-wrap: break-word; } } - + // Content 영역 .content { align-self: stretch; @@ -54,123 +54,177 @@ justify-content: center; // 중앙 정렬 align-items: center; // 중앙 정렬 position: relative; - - .imageGrid { - width: 100%; - height: 100%; - display: flex; - justify-content: center; // 중앙 정렬로 균등 배치 - align-items: flex-start; - flex-wrap: wrap; - align-content: flex-start; - overflow-y: scroll; // 스크롤바 항상 표시 - overflow-x: hidden; - padding: 30px 40px; // 좌우 패딩 증가 - box-sizing: border-box; - - // gap 대신 margin 사용 (TV 호환성) - 화면을 적절히 채우도록 조정 - .imageItem { - width: 210px; // 크기 약간 증가 - height: 190px; // 비율 맞춤 - border-radius: 12px; - position: relative; - cursor: pointer; - margin-right: 35px; // 마진 증가로 균등 분배 - margin-bottom: 30px; // 세로 마진도 증가 - - // 4개씩 배치하므로 4번째마다 margin-right 제거 - &:nth-child(4n) { - margin-right: 0; - } - - &:focus { - outline: none; - &::after { - .focused(@boxShadow: 0px, @borderRadius: 12px); - // 프로젝트 표준 포커스 스타일 사용 (4px solid @PRIMARY_COLOR_RED) - } - } - - .image { - width: 100%; - height: 100%; - border-radius: 12px; - object-fit: cover; - padding: 0; - box-sizing: border-box; - } - - // 이미지 로드 실패 시 플레이스홀더 - .imagePlaceholder { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: #f5f5f5; - border-radius: 12px; - display: flex; - justify-content: center; - align-items: center; - - .placeholderText { - color: #999; - font-size: 14px; - font-family: @baseFont; - text-align: center; - } - } - - // selectedImage 스타일 제거 - 오직 포커스만 사용 + .scrollerOverride { + width: 1060px; // 절대 크기 지정 + height: 100%; + // 좌측 30px, 우측 66px(스크롤바) 패딩을 명시적으로 적용 + padding: 30px 20px 30px 20px; + box-sizing: border-box; + margin: 0; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + + // 스크롤바 너비를 6px로 명확하게 설정 + &::-webkit-scrollbar { + width: 6px; } - - // View More 아이템 - .viewMoreItem { - width: 210px; // 크기 증가 - height: 190px; // 비율 맞춤 - border-radius: 12px; - position: relative; - margin-right: 35px; // 마진 증가 - margin-bottom: 30px; // 세로 마진 증가 - - &:nth-child(4n) { - margin-right: 0; - } - - .image { - width: 100%; - height: 100%; + + &::-webkit-scrollbar-track { + background: transparent; // 트랙 배경은 투명하게 + } + + &::-webkit-scrollbar-thumb { + background: #9c9c9c; // 스크롤바 색상 + border-radius: 3px; // 스크롤바 둥근 모서리 + } + + // 스크롤바 thumb에 hover 효과 적용 + &:hover::-webkit-scrollbar-thumb { + background: #c72054; + } + // gap 대신 margin 사용 (TV 호환성) - 화면을 적절히 채우도록 조정 + .imageGrid { + width: 100%; + + padding: 0; + margin: 0; + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: flex-start; + overflow: visible; + height: 100%; + flex-wrap: wrap; + align-content: flex-start; + // padding: 30px 40px; // 좌우 패딩 증가 + // padding: 30px 0px 30px 15px; // 좌우 패딩 증가 + .imageItem { + //스타일 변경 + // width: 210px; // 크기 약간 증가 + // height: 190px; // 비율 맞춤 + + width: 226px; // 크기 약간 증가 + height: 218px; // 비율 맞춤 border-radius: 12px; - object-fit: cover; - padding: 4px; - box-sizing: border-box; + position: relative; + cursor: pointer; + + //스타일 변경 + // margin-right: 35px; // 마진 증가로 균등 분배 + // margin-bottom: 30px; // 세로 마진도 증가 + margin-right: 20px; // 마진 증가로 균등 분배 + margin-top: 20px; // 세로 마진도 증가 + + // 4개씩 배치하므로 4번째마다 margin-right 제거 + &:nth-child(4n) { + margin-right: 0; + } + + &:focus { + //확대 이미지 + width: 240px; + height: 240px; + margin-right: 6px; + margin-top: -2px; + // margin-top: -3px; + outline: none; + &::after { + .focused(@boxShadow: 0px, @borderRadius: 12px); + // 프로젝트 표준 포커스 스타일 사용 (4px solid @PRIMARY_COLOR_RED) + } + &:nth-child(4n) { + margin-right: 0; + margin-left: -14px; + } + } + + .image { + width: 100%; + height: 100%; + border-radius: 12px; + object-fit: cover; + padding: 0; + box-sizing: border-box; + } + + // 이미지 로드 실패 시 플레이스홀더 + .imagePlaceholder { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: #f5f5f5; + border-radius: 12px; + display: flex; + justify-content: center; + align-items: center; + + .placeholderText { + color: #999; + font-size: 14px; + font-family: @baseFont; + text-align: center; + } + } + + // selectedImage 스타일 제거 - 오직 포커스만 사용 } - - .viewMoreOverlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(0deg, rgba(0, 0, 0, 0.80) 0%, rgba(0, 0, 0, 0.80) 100%); + + // View More 아이템 + .viewMoreItem { + width: 210px; // 크기 증가 + height: 190px; // 비율 맞춤 border-radius: 12px; - display: flex; - justify-content: center; - align-items: center; - - .viewMoreText { - color: white; - font-size: 16px; - font-family: @baseFont; - font-weight: 700; - line-height: 31px; - word-wrap: break-word; + position: relative; + margin-right: 35px; // 마진 증가 + margin-bottom: 30px; // 세로 마진 증가 + + &:nth-child(4n) { + margin-right: 0; + } + + .image { + width: 100%; + height: 100%; + border-radius: 12px; + object-fit: cover; + padding: 4px; + box-sizing: border-box; + } + + .viewMoreOverlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 0deg, + rgba(0, 0, 0, 0.8) 0%, + rgba(0, 0, 0, 0.8) 100% + ); + border-radius: 12px; + display: flex; + justify-content: center; + align-items: center; + + .viewMoreText { + color: white; + font-size: 16px; + font-family: @baseFont; + font-weight: 700; + line-height: 31px; + word-wrap: break-word; + } } } } } - + // User Reviews 모드용 reviewGrid 스타일 .reviewGrid { width: 100%; @@ -181,20 +235,20 @@ box-sizing: border-box; overflow-y: scroll; overflow-x: hidden; - + // User Reviews 콘텐츠 스타일 - 추후 구현 } - + // UserReviewDetail 컴포넌트를 위한 컨테이너 .reviewDetailContainer { width: 100%; height: 100%; } - + // 스크롤바 표시 (기본 브라우저 스타일 사용) // 별도 커스텀 스크롤바 스타일 없음 } - + // Footer 영역 .footer { align-self: stretch; @@ -202,31 +256,31 @@ display: flex; justify-content: center; align-items: center; - + .closeButton { width: 300px !important; height: 78px !important; - background: #7A808D !important; + background: #7a808d !important; border-radius: 12px !important; border: none !important; display: flex !important; justify-content: center !important; align-items: center !important; - + color: white !important; font-size: 30px !important; font-family: @baseFont !important; font-weight: 700 !important; line-height: 30px !important; text-align: center !important; - + &:focus { background: @PRIMARY_COLOR_RED !important; color: white !important; outline: none !important; border: none !important; } - + &:hover { background: @PRIMARY_COLOR_RED !important; color: white !important; @@ -234,4 +288,4 @@ } } } -} \ No newline at end of file +} 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 69d352b7..0894414c 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 @@ -1,8 +1,10 @@ -import React, { useMemo } from "react"; -import css from "./QRCode.module.less"; -import { getQRCodeUrl } from "../../../../utils/helperMethods"; -import TQRCode from "../../../../components/TQRCode/TQRCode"; -import { useSelector } from "react-redux"; +import React, { useMemo } from 'react'; + +import { useSelector } from 'react-redux'; + +import TQRCode from '../../../../components/TQRCode/TQRCode'; +import { getQRCodeUrl } from '../../../../utils/helperMethods'; +import css from './QRCode.module.less'; export default function QRCode({ productType, @@ -59,7 +61,7 @@ export default function QRCode({ {kind === "detail" ? ( ) : ( - "" + qrCodeUrl && )} {/* todo : 시나리오,UI 릴리즈 후 */} {/*
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductOverview.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductOverview.module.less index 01c97513..584c5e4a 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductOverview.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductOverview.module.less @@ -2,11 +2,12 @@ @import "../../../style/utils.module.less"; .container { - .size(@w:100%,@h:100%); - + // .size(@w:100%,@h:100%); + .size(@w:100%,@h:334px); .productInfoWrapper { .flex(@justifyCenter:flex-start,@alignCenter:flex-start); - margin: 54px 0 10px 0; + // margin: 54px 0 10px 0; + margin: 20px 0 10px 0; // 고정 높이로 인해 QR 영역과 하단 버튼 영역 사이에 과도한 여백이 생김 // 콘텐츠 높이에 맞춰 자동으로 계산되도록 변경하여 불필요한 간격 제거 .size(@w:100%,@h:auto); diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.jsx index 0b238174..74d3b633 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.jsx @@ -1,7 +1,11 @@ -import React, { useCallback, useMemo } from "react"; -import usePriceInfo from "../../../../../hooks/usePriceInfo"; -import { $L } from "../../../../../utils/helperMethods"; -import css from "./BuyNowPriceDisplay.module.less"; +import React, { + useCallback, + useMemo, +} from 'react'; + +import usePriceInfo from '../../../../../hooks/usePriceInfo'; +import { $L } from '../../../../../utils/helperMethods'; +import css from './BuyNowPriceDisplay.module.less'; export default function BuyNowPriceDisplay({ priceData, @@ -41,16 +45,26 @@ export default function BuyNowPriceDisplay({ {discountRate && Number(discountRate.replace("%", "")) > 4 && (
{discountRate}
)} - - {isDiscountedPriceEmpty ? offerInfo : discountedPrice} - - {isDiscounted && ( - - {originalPrice && isOriginalPriceEmpty - ? offerInfo - : originalPrice} +
+ + {isDiscountedPriceEmpty ? offerInfo : discountedPrice} - )} + {isDiscounted && ( + + {originalPrice && isOriginalPriceEmpty + ? offerInfo + : originalPrice} + + )} +
{/* 할부 */}
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.module.less index e69de29b..239ced95 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.module.less +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/BuyNowPriceDisplay/BuyNowPriceDisplay.module.less @@ -0,0 +1,105 @@ +@import "../../../../../style/CommonStyle.module.less"; +@import "../../../../../style/utils.module.less"; + +.wrapper { + height: 100%; + + .partnerName { + font-weight: bold; + font-size: 36px; + color: @COLOR_WHITE; + margin-bottom: 24px; + } + .flex(@alignCenter:flex-start,@direction:column); + .topLayer { + height: 42px; + flex-wrap: wrap; + // margin-bottom: 20px; + margin-bottom: 10px; + } + .rateTag { + background: linear-gradient(309.43deg, #ef775b 23.84%, #c70850 100%); + width: 70px; + height: 42px; + padding: 6px 12px; + border-radius: 6px; + color: @COLOR_WHITE; + .font(@fontFamily: @baseFont, @fontSize: 24px); + font-weight: 700; + .flex(); + } + .name { + font-weight: bold; + font-size: 36px; + color: @COLOR_WHITE; + width: 100%; + } + .btmLayer { + //margin-top:14px; + margin: 10px 0; + // display: flex;rateTag + align-items: center; + } + .price { + font-weight: bold; + font-size: 60px; + color: @COLOR_WHITE; + margin-right: 9px; + line-height: 1; + } + .offerInfo { + .elip(4); + font-size: 40px; + font-weight: bold; + line-height: 1; + color: #808080; + padding-bottom: 5px; + } + .discountedPrc { + font-size: 24px; + color: @COLOR_GRAY03; + text-decoration: line-through; + } + //rewd Layer + .rewdTopLayer { + width: 500px; + padding-bottom: 24px; + border-bottom: 1px solid @COLOR_GRAY02; + + > span { + font-weight: bold; + font-size: 36px; + color: @COLOR_GRAY03; + } + .partnerPrc { + text-decoration: line-through; + align-items: center; + } + } + .rewdBtmLayer { + padding-top: 24px; + .flex(@direction:column,@alignCenter:flex-start); + .rewdNm { + font-weight: bold; + font-size: 36px; + color: @COLOR_BLACK; + } + + .btmPrc { + margin-top: 17px; + .flex(); + .rewdPrc { + font-size: 24px; + color: #808080; + margin-right: 10px; + padding-top: 14px; + } + } + .rewdRate { + font-size: 60px; + font-weight: bold; + color: @PRIMARY_COLOR_RED; + margin-top: 18px; + } + } +} diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/ProductPriceDisplay.jsx b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/ProductPriceDisplay.jsx index 79dd3299..24f242b5 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/ProductPriceDisplay.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductOverview/ProductPriceDisplay/ProductPriceDisplay.jsx @@ -68,7 +68,8 @@ export default function ProductPriceDisplay({ productType, productInfo }) { return ( <> {productType && productInfo && ( -
+ /*
*/ +
{/* shop by mobile (구매불가) 상품 price render */} {(productType === "shopByMobile" || isThemeShopByMobile) && (