diff --git a/com.twin.app.shoptime/src/actions/actionTypes.js b/com.twin.app.shoptime/src/actions/actionTypes.js
index 23e0d69d..c2926686 100644
--- a/com.twin.app.shoptime/src/actions/actionTypes.js
+++ b/com.twin.app.shoptime/src/actions/actionTypes.js
@@ -156,6 +156,7 @@ export const types = {
GET_PRODUCT_OPTION_ID: "GET_PRODUCT_OPTION_ID",
CLEAR_PRODUCT_OPTIONS: "CLEAR_PRODUCT_OPTIONS",
GET_USER_REVIEW: "GET_USER_REVIEW",
+ TOGGLE_SHOW_ALL_REVIEWS: "TOGGLE_SHOW_ALL_REVIEWS",
// search actions
GET_SEARCH: "GET_SEARCH",
diff --git a/com.twin.app.shoptime/src/actions/productActions.js b/com.twin.app.shoptime/src/actions/productActions.js
index 67ab1991..c380c9dd 100644
--- a/com.twin.app.shoptime/src/actions/productActions.js
+++ b/com.twin.app.shoptime/src/actions/productActions.js
@@ -194,6 +194,11 @@ const createMockReviewData = () => ({
}
});
+// showAllReviews 상태 토글
+export const toggleShowAllReviews = () => ({
+ type: types.TOGGLE_SHOW_ALL_REVIEWS
+});
+
// 상품별 유저 리뷰 리스트 조회 : IF-LGSP-0002
export const getUserReviews = (requestParams) => (dispatch, getState) => {
const { prdtId } = requestParams;
diff --git a/com.twin.app.shoptime/src/reducers/productReducer.js b/com.twin.app.shoptime/src/reducers/productReducer.js
index a32edfe7..72bb02f4 100644
--- a/com.twin.app.shoptime/src/reducers/productReducer.js
+++ b/com.twin.app.shoptime/src/reducers/productReducer.js
@@ -6,6 +6,7 @@ const initialState = {
productImageLength: 0,
prdtOptInfo: {},
reviewData: null, // 리뷰 데이터 추가
+ showAllReviews: false, // 전체 리뷰 보기 상태
};
// FP: handlers map (curried), pure and immutable updates only
@@ -50,6 +51,13 @@ const handleUserReview = curry((state, action) => {
return set("reviewData", reviewData, state);
});
+// showAllReviews 토글 핸들러
+const handleToggleShowAllReviews = curry((state, action) => {
+ const currentValue = get("showAllReviews", state);
+ console.log("[UserReviews] Toggle showAllReviews:", !currentValue);
+ return set("showAllReviews", !currentValue, state);
+});
+
const handlers = {
[types.GET_BEST_SELLER]: handleBestSeller,
[types.GET_PRODUCT_OPTION]: handleProductOption,
@@ -59,6 +67,7 @@ const handlers = {
[types.CLEAR_PRODUCT_DETAIL]: handleClearProductDetail,
[types.GET_PRODUCT_OPTION_ID]: handleProductOptionId,
[types.GET_USER_REVIEW]: handleUserReview, // GET_USER_REVIEW 핸들러 추가
+ [types.TOGGLE_SHOW_ALL_REVIEWS]: handleToggleShowAllReviews, // showAllReviews 토글 핸들러
};
export const productReducer = (state = initialState, action = {}) => {
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 11491d2d..40b03a61 100644
--- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
+++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
@@ -1,8 +1,9 @@
/* eslint-disable react/jsx-no-bind */
// src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
+import Spottable from "@enact/spotlight/Spottable";
import React, { useCallback, useRef, useState, useMemo, useEffect } from "react";
-import { useSelector } from "react-redux";
+import { useSelector, useDispatch } from "react-redux";
import Spotlight from "@enact/spotlight";
import { PropTypes } from "prop-types";
@@ -12,6 +13,7 @@ import { $L } from "../../../utils/helperMethods";
import {
curry, pipe, when, isVal, isNotNil, defaultTo, defaultWith, get, identity, isEmpty, isNil, andThen, tap
} from "../../../utils/fp";
+import { toggleShowAllReviews } from "../../../actions/productActions";
import FavoriteBtn from "../components/FavoriteBtn";
import StarRating from "../components/StarRating";
import ProductTag from "../components/ProductTag";
@@ -99,22 +101,17 @@ const extractProductMeta = (productInfo) => ({
orderPhnNo: get("orderPhnNo", productInfo)
});
-// 레이아웃 확인용 샘플 컴포넌트
-const LayoutSample = () => (
-
- Layout Sample (1124px x 300px)
-
+// 레이아웃 확인용 샘플 컴포넌트 - Spottable로 변경
+const SpottableComponent = Spottable("div");
+
+const LayoutSample = ({ onClick }) => (
+
+ Layout Sample - Click to Show All Reviews (1124px x 300px)
+
);
export default function ProductAllSection({
@@ -129,10 +126,18 @@ export default function ProductAllSection({
setOpenThemeItemOverlay,
themeProductInfo,
}) {
+ const dispatch = useDispatch();
+
const productData = useMemo(() =>
getProductData(productType, themeProductInfo, productInfo),
[productType, themeProductInfo, productInfo]
);
+
+ // [임시 테스트] LayoutSample 클릭 핸들러
+ const handleLayoutSampleClick = useCallback(() => {
+ console.log("[Test] LayoutSample clicked - dispatching toggleShowAllReviews");
+ dispatch(toggleShowAllReviews());
+ }, [dispatch]);
// 디버깅: 실제 이미지 데이터 확인
useEffect(() => {
@@ -383,7 +388,7 @@ export default function ProductAllSection({
id="scroll-marker-product-details"
className={css.scrollMarker}
>
-
+
{productData?.imgUrls600 && productData.imgUrls600.length > 0 ? (
productData.imgUrls600.map((image, index) => (
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less
index 58a69c8e..7ea63d15 100644
--- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less
+++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less
@@ -463,3 +463,26 @@
}
}
}
+
+// 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);
+ }
+ }
+}
\ 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 e3eaba9b..770cd2d8 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
@@ -8,7 +8,7 @@ import { useMemo } from "react";
import Spottable from "@enact/spotlight/Spottable";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { useDispatch, useSelector } from "react-redux";
-import { getUserReviews } from "../../../../actions/productActions";
+import { getUserReviews, toggleShowAllReviews } from "../../../../actions/productActions";
import StarRating from "../../components/StarRating";
import CustomerImages from "./CustomerImages/CustomerImages";
import UserReviewsPopup from "./UserReviewsPopup/UserReviewsPopup";
@@ -17,11 +17,13 @@ const SpottableComponent = Spottable("div");
const Container = SpotlightContainerDecorator(
{
- enterTo: "last-focused",
+ enterTo: "default-element",
preserveld: true,
leaveFor: {
left: "spotlight-product-info-section-container"
- }
+ },
+ restrict: "none",
+ spotlightDirection: "vertical"
},
"div"
);
@@ -30,10 +32,46 @@ export default function UserReviews({ productInfo, panelInfo }) {
const { getScrollTo, scrollTop } = useScrollTo();
const dispatch = useDispatch();
const containerRef = useRef(null);
+ const tScrollerRef = useRef(null);
// 팝업 상태 관리
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [selectedImageIndex, setSelectedImageIndex] = useState(0);
+ // Redux에서 showAllReviews 상태 가져오기
+ const showAllReviews = useSelector((state) => state.product.showAllReviews);
+
+ // 디버깅: showAllReviews 상태 변경 확인
+ useEffect(() => {
+ console.log("[UserReviews] showAllReviews state changed:", {
+ showAllReviews,
+ reviewListLength: reviewListData?.length || 0,
+ willShowCount: showAllReviews ? (reviewListData?.length || 0) : 5
+ });
+ }, [showAllReviews, reviewListData]);
+
+ // showAllReviews 상태 변경 시 TScroller 스크롤 영역 강제 재계산
+ 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') {
+ tScrollerRef.current.calculateMetrics();
+ }
+
+ // 또는 scrollTo를 호출해서 스크롤 영역 업데이트
+ if (typeof tScrollerRef.current.scrollTo === 'function') {
+ tScrollerRef.current.scrollTo({ position: { y: 0 }, animate: false });
+ }
+
+ console.log("[UserReviews] TScroller scroll area updated");
+ }
+ }, 100);
+ }
+ }, [showAllReviews]);
const reviewListData = useSelector(
(state) => state.product.reviewData && state.product.reviewData.reviewList
);
@@ -51,9 +89,11 @@ export default function UserReviews({ productInfo, panelInfo }) {
useEffect(() => {
console.log("[UserReviews] Review data received:", {
reviewListData,
+ reviewListLength: reviewListData?.length || 0,
reviewTotalCount,
reviewDetailData,
- hasData: reviewListData && reviewListData.length > 0
+ hasData: reviewListData && reviewListData.length > 0,
+ actualDataLength: Array.isArray(reviewListData) ? reviewListData.length : 'not array'
});
}, [reviewListData, reviewTotalCount, reviewDetailData]);
@@ -116,6 +156,13 @@ export default function UserReviews({ productInfo, panelInfo }) {
setSelectedImageIndex(index);
}, []);
+
+ const handleViewAllReviewsClick = useCallback(() => {
+ console.log("[UserReviews] View All Reviews clicked - dispatching toggleShowAllReviews");
+ dispatch(toggleShowAllReviews());
+ }, [dispatch]);
+
+
// 이미지 데이터 가공 (CustomerImages와 동일한 로직)
const customerImages = useMemo(() => {
if (!reviewListData || !Array.isArray(reviewListData)) {
@@ -155,9 +202,11 @@ export default function UserReviews({ productInfo, panelInfo }) {
spotlightId="user-reviews-container"
>
{$L(
- `Showing ${reviewListData ? reviewListData.length : 0} out of ${reviewTotalCount} reviews`
+ `Showing ${reviewListData ? (showAllReviews ? reviewListData.length : Math.min(reviewListData.length, 5)) : 0} out of ${reviewTotalCount} reviews`
)}
{reviewListData &&
- reviewListData.map((review, index) => {
+ (() => {
+ const reviewsToShow = showAllReviews ? reviewListData : reviewListData.slice(0, 5);
+ console.log("[UserReviews] Reviews to render:", {
+ showAllReviews,
+ totalReviews: reviewListData.length,
+ reviewsToShowCount: reviewsToShow.length,
+ isShowingAll: showAllReviews
+ });
+ return reviewsToShow;
+ })().map((review, index) => {
const { reviewImageList, rvwRtng, rvwRgstDtt, rvwCtnt, rvwId, wrtrNknm, rvwWrtrId } =
review;
console.log(`[UserReviews] Rendering review ${index}:`, { rvwId, hasImages: reviewImageList && reviewImageList.length > 0 });
@@ -224,7 +282,22 @@ export default function UserReviews({ productInfo, panelInfo }) {
);
})}
+
+ {/* View All Reviews 버튼 - 일시적으로 코멘트 처리 */}
+ {/* {!showAllReviews && reviewListData && reviewListData.length > 5 && (
+
+ )} */}
{/* UserReviewsPopup 추가 */}
@@ -237,4 +310,4 @@ export default function UserReviews({ productInfo, panelInfo }) {
/>
);
-}
+}
\ No newline at end of file
diff --git a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.module.less b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.module.less
index 9b559131..64e155c4 100644
--- a/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.module.less
+++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductContentSection/UserReviews/UserReviews.module.less
@@ -2,8 +2,10 @@
@import "../../../../style/utils.module.less";
.tScroller {
- .size(@w: 1124px, @h: 100%); // 마진 포함 전체 크기 (1054px + 60px)
+ .size(@w: 1124px, @h: auto); // auto height to accommodate dynamic content
max-width: 1124px;
+ min-height: 500px; // 최소 높이 보장
+ max-height: none; // 최대 높이 제한 없음
padding: 0;
box-sizing: border-box;
}
@@ -108,4 +110,52 @@
}
}
}
+
+ .viewAllReviewsSection {
+ width: 100%;
+ height: 105px; // 75px + 30px margin-bottom
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ margin-top: 20px;
+
+ .viewAllReviewsButton {
+ width: auto; // "View All Reviews +" 한 줄 표시용으로 확장
+ height: 75px; // 20 + 35 + 20
+ cursor: pointer;
+
+ &:focus {
+ &::after {
+ .focused(@boxShadow:22px, @borderRadius:6px);
+ }
+ }
+
+ .viewAllReviewsContent {
+ width: 100%;
+ height: 100%;
+ padding: 20px 30px;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 6px;
+ border: 1px solid #EEEEEE;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+
+ .viewAllReviewsText {
+ color: #EAEAEA;
+ font-size: 24px;
+ font-family: @baseFont;
+ font-weight: 600;
+ line-height: 35px;
+ margin-right: 10px;
+ }
+
+ .viewAllReviewsIcon {
+ width: 17px;
+ height: 17px;
+ // 플러스 아이콘을 위한 스타일 (향후 추가 가능)
+ }
+ }
+ }
+ }
}
\ No newline at end of file