From 219582aaf2d01784308d4d43020323e4c4c31ec2 Mon Sep 17 00:00:00 2001 From: optrader Date: Tue, 4 Nov 2025 12:27:45 +0900 Subject: [PATCH] [251104] fix: UserReviewsPanel Review Filters-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 04. 12:27:44 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 7๊ฐœ โ€ข ์ถ”๊ฐ€: +155์ค„ โ€ข ์‚ญ์ œ: -43์ค„ ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx ~ com.twin.app.shoptime/src/views/UserReview/UserReviewPanel.jsx ~ com.twin.app.shoptime/src/views/UserReview/components/FilterItemButton.module.less ~ com.twin.app.shoptime/src/views/UserReview/components/UserReviewItem.jsx ~ com.twin.app.shoptime/src/views/UserReview/components/UserReviewsList.jsx ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข UI ์ปดํฌ๋„ŒํŠธ ์•„ํ‚คํ…์ฒ˜ ๊ฐœ์„  โ€ข ์ค‘๊ฐ„ ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  โ€ข ๋ชจ๋“ˆ ๊ตฌ์กฐ ๊ฐœ์„  --- .../src/views/CheckOutPanel/CheckOutPanel.jsx | 12 +- .../ProductAllSection/ProductAllSection.jsx | 2 +- .../DetailPanel/components/BuyOption.jsx | 47 +++++-- .../src/views/UserReview/UserReviewPanel.jsx | 115 ++++++++++++++---- .../components/FilterItemButton.module.less | 8 +- .../UserReview/components/UserReviewItem.jsx | 5 + .../UserReview/components/UserReviewsList.jsx | 9 +- 7 files changed, 155 insertions(+), 43 deletions(-) diff --git a/com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx b/com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx index 734c8c9f..2e19a704 100644 --- a/com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx +++ b/com.twin.app.shoptime/src/views/CheckOutPanel/CheckOutPanel.jsx @@ -361,17 +361,15 @@ export default function CheckOutPanel({ panelInfo }) { return () => { console.log('[BuyOption][CheckOutPanel] cleanup useEffect - calling resetCheckoutData'); - // Mock ๋ชจ๋“œ์—์„œ๋„ ์ƒํƒœ ์ดˆ๊ธฐํ™” ํ•„์š” (Firefox ํ™€์ˆ˜/์ง์ˆ˜๋ฒˆ ํŒจํ„ด ๋ฌธ์ œ ํ•ด๊ฒฐ) + // API Mode์—์„œ๋งŒ checkout data ์ดˆ๊ธฐํ™” ํ•„์š” + // Mock Mode์—์„œ๋Š” popup ์ƒํƒœ๋งŒ ์ •๋ฆฌ (Redux checkout state ์œ ์ง€) if (!BUYNOW_CONFIG.isMockMode()) { dispatch(resetCheckoutData()); } else { - console.log('[BuyOption][CheckOutPanel] Mock Mode - Partial cleanup to prevent state accumulation'); - // Mock Mode์—์„œ๋„ ํŒ์—… ์ƒํƒœ์™€ ๊ด€๋ จ๋œ ๋ถ€๋ถ„ ์ดˆ๊ธฐํ™” - dispatch(setHidePopup()); - - // empTermsData ์ดˆ๊ธฐํ™”๋ฅผ ์œ„ํ•œ ์•ก์…˜ ๋””์ŠคํŒจ์น˜ (empActions์—์„œ reset ์•ก์…˜์ด ์žˆ๋Š”์ง€ ํ™•์ธ ํ•„์š”) - // dispatch({ type: 'RESET_EMP_TERMS' }); + console.log('[BuyOption][CheckOutPanel] Mock Mode - Cleaning up popup state only'); } + + dispatch(setHidePopup()); }; }, [dispatch]); 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 cc88bc5f..00fa9fd0 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx @@ -174,7 +174,7 @@ export default function ProductAllSection({ const youmaylikeData = useSelector((state) => state.main.youmaylikeData); // ProductVideo ๋ฒ„์ „ ๊ด€๋ฆฌ (1: ๊ธฐ์กด modal ๋ฐฉ์‹, 2: ๋‚ด์žฅ ๋ฐฉ์‹ , 3: ๋น„๋””์˜ค ์ƒ๋žต) - const [productVideoVersion, setProductVideoVersion] = useState(2); + const [productVideoVersion, setProductVideoVersion] = useState(3); // const [currentHeight, setCurrentHeight] = useState(0); //ํ•˜๋‹จ๋ถ€๋ถ„๊นŒ์ง€ ๊ฐ”์„๋•Œ ์ฒดํฌ์šฉ diff --git a/com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx b/com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx index 87b86c54..4231a26f 100644 --- a/com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx +++ b/com.twin.app.shoptime/src/views/DetailPanel/components/BuyOption.jsx @@ -35,7 +35,7 @@ import { popPanel, pushPanel, } from '../../../actions/panelActions'; -import { finishVideoPreview } from '../../../actions/playActions'; +import { clearAllVideoTimers } from '../../../actions/playActions'; import { getProductOption, getProductOptionId, @@ -374,8 +374,23 @@ const BuyOption = ({ const { mbrId, prdtId, prodSno } = response.data.productList[0]; const cartTpSno = `${mbrId}_${prdtId}_${prodSno}`; // dispatch(popPanel(Config.panel_names.DETAIL_PANEL)); - dispatch(finishVideoPreview()); - dispatch(finishMediaPreview()); + clearAllVideoTimers(); // ProductVideoV2์˜ ํƒ€์ด๋จธ ์ •๋ฆฌ (์ผ๋ฐ˜ ํ•จ์ˆ˜ ์ง์ ‘ ํ˜ธ์ถœ) + dispatch(finishMediaPreview()); // MediaPanel ์ •๋ฆฌ + + // ๐Ÿ”ด CRITICAL: DetailPanel ๋’ค์— ์žˆ์„ ์ˆ˜ ์žˆ๋Š” PlayerPanel๋„ ํ•จ๊ป˜ ์ œ๊ฑฐ (API Mode) + dispatch((dispatchFn, getState) => { + const panels = getState().panels?.panels || []; + const playerPanelExists = panels.some(p => + p.name === Config.panel_names.PLAYER_PANEL || + p.name === Config.panel_names.PLAYER_PANEL_NEW + ); + + if (playerPanelExists) { + console.log('[BuyOption] โš ๏ธ API Mode - Found PlayerPanel in stack - removing before checkout'); + dispatchFn(popPanel(Config.panel_names.PLAYER_PANEL)); + } + }); + dispatch( pushPanel({ name: Config.panel_names.CHECKOUT_PANEL, @@ -620,10 +635,26 @@ const BuyOption = ({ console.log('[BuyOption] logInfo:', logInfo); console.log('[BuyOption] Dispatching pushPanel to CHECKOUT_PANEL'); - // CheckOutPanel ์ด๋™ ์ „์— PlayerPanel/MediaPanel ์ƒํƒœ ์ •๋ฆฌ - console.log('[BuyOption] Mock Mode - Cleaning up PlayerPanel/MediaPanel before checkout'); - dispatch(finishVideoPreview()); - dispatch(finishMediaPreview()); + // CheckOutPanel ์ด๋™ ์ „์— ProductVideoV2 ํƒ€์ด๋จธ ๋ฐ MediaPanel/PlayerPanel ์ •๋ฆฌ + console.log('[BuyOption] Mock Mode - Cleaning up ProductVideoV2 timers and all media panels before checkout'); + clearAllVideoTimers(); // ProductVideoV2์˜ ํƒ€์ด๋จธ ์ •๋ฆฌ (์ผ๋ฐ˜ ํ•จ์ˆ˜ ์ง์ ‘ ํ˜ธ์ถœ) + dispatch(finishMediaPreview()); // MediaPanel ์ •๋ฆฌ + + // ๐Ÿ”ด CRITICAL: DetailPanel ๋’ค์— ์žˆ์„ ์ˆ˜ ์žˆ๋Š” PlayerPanel๋„ ํ•จ๊ป˜ ์ œ๊ฑฐ + // (finishMediaPreview๋Š” MediaPanel๋งŒ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ PlayerPanel์ด ๋‚จ์•„์žˆ์„ ์ˆ˜ ์žˆ์Œ) + // dispatch๋Š” thunk function์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ getState๋กœ panels ์ ‘๊ทผ + dispatch((dispatchFn, getState) => { + const panels = getState().panels?.panels || []; + const playerPanelExists = panels.some(p => + p.name === Config.panel_names.PLAYER_PANEL || + p.name === Config.panel_names.PLAYER_PANEL_NEW + ); + + if (playerPanelExists) { + console.log('[BuyOption] โš ๏ธ Found PlayerPanel in stack - removing before checkout'); + dispatchFn(popPanel(Config.panel_names.PLAYER_PANEL)); + } + }); // Mock ๋ชจ๋“œ: ์„ ํƒ ์ƒํ’ˆ์˜ ์ •๋ณด๋ฅผ panelInfo์— ๋‹ด์•„์„œ ์ „๋‹ฌ // CheckOutPanel์—์„œ ์ด ์ •๋ณด๋กœ Mock ์ƒํ’ˆ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ @@ -724,7 +755,7 @@ const BuyOption = ({ name: Config.panel_names.CHECKOUT_PANEL, panelInfo: fallbackPanelInfo, }) - ); + ); } else { // ์ •์ƒ ์ผ€์ด์Šค: checkoutPanelInfo ์‚ฌ์šฉ dispatch( diff --git a/com.twin.app.shoptime/src/views/UserReview/UserReviewPanel.jsx b/com.twin.app.shoptime/src/views/UserReview/UserReviewPanel.jsx index a78cb35b..158a901d 100644 --- a/com.twin.app.shoptime/src/views/UserReview/UserReviewPanel.jsx +++ b/com.twin.app.shoptime/src/views/UserReview/UserReviewPanel.jsx @@ -11,6 +11,7 @@ import useReviews, { REVIEW_VERSION } from '../../hooks/useReviews/useReviews'; import fp from '../../utils/fp'; import { panel_names } from '../../utils/Config'; import StarRating from '../DetailPanel/components/StarRating'; +import UserReviewsPopup from '../DetailPanel/ProductContentSection/UserReviews/UserReviewsPopup/UserReviewsPopup'; import FilterItemButton from './components/FilterItemButton'; import UserReviewsList from './components/UserReviewsList'; import UserReviewHeader from './UserReviewHeader'; @@ -46,10 +47,19 @@ const UserReviewPanel = ({ className, panelInfo, spotlightId }) => { filters, filteredReviewListData, currentReviewFilter, + // ์ „์ฒด ๋ฆฌ๋ทฐ ๋ฐ์ดํ„ฐ (ํŒ์—…์šฉ) + allReviews, + getReviewsWithImages, + extractImagesFromReviews, } = useReviews(prdtId, patnrId); // REVIEW_VERSION ์ƒ์ˆ˜์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ API ์„ ํƒ const [isPaging, setIsPaging] = useState(false); + // ํŒ์—… ์ƒํƒœ ๊ด€๋ฆฌ + const [isPopupOpen, setIsPopupOpen] = useState(false); + const [popupMode, setPopupMode] = useState('user-reviews'); + const [selectedImageIndex, setSelectedImageIndex] = useState(0); + const productImage = fp.pipe( () => panelInfo, fp.get('productImage'), @@ -372,6 +382,41 @@ const UserReviewPanel = ({ className, panelInfo, spotlightId }) => { dispatch(popPanel()); }, [dispatch, bgVideoInfo]); + // ๋ฆฌ๋ทฐ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ - ProductAllSection์˜ UserReviews.jsx ํŒจํ„ด + const handleReviewClick = useCallback( + (review, index) => { + console.log('[UserReviewPanel] Review clicked, opening popup:', { + rvwId: review.rvwId, + index, + }); + + // ์ „์ฒด ๋ฆฌ๋ทฐ์—์„œ ํด๋ฆญํ•œ ๋ฆฌ๋ทฐ์˜ ์‹ค์ œ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ + const realIndex = allReviews.findIndex((r) => r.rvwId === review.rvwId); + + setSelectedImageIndex(realIndex >= 0 ? realIndex : index); + setPopupMode('user-reviews'); + setIsPopupOpen(true); + }, + [allReviews] + ); + + // ํŒ์—… ๋‹ซ๊ธฐ ํ•ธ๋“ค๋Ÿฌ + const handleClosePopup = useCallback(() => { + console.log('[UserReviewPanel] Closing popup'); + setIsPopupOpen(false); + setPopupMode('user-reviews'); + setSelectedImageIndex(0); + }, []); + + // ํŒ์—… ๋ชจ๋“œ ๋ณ€๊ฒฝ ํ•ธ๋“ค๋Ÿฌ + const handleModeChange = useCallback((newMode, imageIndex = 0) => { + console.log('[UserReviewPanel] Mode change requested:', { newMode, imageIndex }); + setPopupMode(newMode); + if (newMode === 'all-images' || newMode === 'user-reviews') { + setSelectedImageIndex(imageIndex); + } + }, []); + return ( {
{ ) : ( // ํด๋ฐฑ: API ๋ฐ์ดํ„ฐ ์—†์„ ๊ฒฝ์šฐ ๊ธฐ์กด ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๋ฒ„ํŠผ ํ‘œ์‹œ <> - - + {(filterCounts?.sentiment?.positive || 0) > 0 && ( + + )} + {(filterCounts?.sentiment?.negative || 0) > 0 && ( + 0 ? "filter-positive" : "filter-quality"} + isActive={ + currentFilter.type === 'sentiment' && currentFilter.value === 'negative' + } + /> + )} )}
@@ -609,10 +658,32 @@ const UserReviewPanel = ({ className, panelInfo, spotlightId }) => { onPrevPage={handlePrevPage} isPaging={isPaging} showAllReviews + panelInfo={{ + prdtId: prdtId, + patnrId: patnrId, + productImage: productImage, + brandLogo: brandLogo, + productName: productName, + avgRating: avgRating, + reviewCount: reviewCount, + }} + onReviewClick={handleReviewClick} /> + + {/* UserReviewsPopup ์ถ”๊ฐ€ - ProductAllSection๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹ */} +
); }; diff --git a/com.twin.app.shoptime/src/views/UserReview/components/FilterItemButton.module.less b/com.twin.app.shoptime/src/views/UserReview/components/FilterItemButton.module.less index 58b2e9f7..d3f3dfea 100644 --- a/com.twin.app.shoptime/src/views/UserReview/components/FilterItemButton.module.less +++ b/com.twin.app.shoptime/src/views/UserReview/components/FilterItemButton.module.less @@ -39,15 +39,15 @@ } } - // ์„ ํƒ๋œ ์ƒํƒœ: ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ (ํฌ์ปค์Šค๋ณด๋‹ค ์šฐ์„ ๋„ ๋†’๊ฒŒ) + // ์„ ํƒ๋œ ์ƒํƒœ: ๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ์ƒ‰๊ณผ ๋™์ผํ•˜๊ฒŒ (ํ™œ์„ฑํ™” ํ‘œ์‹œ ์•ˆ ํ•จ) &--active { display: flex; padding: 20px; flex-direction: column; align-items: flex-start; - background: #7a808d !important; // ํšŒ์ƒ‰ (์„ ํƒ๋จ) + background: #4a4c50 !important; // ๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ์ƒ‰๊ณผ ๋™์ผ border-radius: 100px; - border: 1px solid #7a808d !important; + border: 1px solid #585858 !important; white-space: nowrap; } @@ -61,7 +61,7 @@ &--active { text-align: center; - color: white; + color: #eaeaea; // ๊ธฐ๋ณธ ํ…์ŠคํŠธ์ƒ‰๊ณผ ๋™์ผ font-size: 24px; font-family: "LG Smart UI"; font-weight: 400; diff --git a/com.twin.app.shoptime/src/views/UserReview/components/UserReviewItem.jsx b/com.twin.app.shoptime/src/views/UserReview/components/UserReviewItem.jsx index 62821242..c9f5f02e 100644 --- a/com.twin.app.shoptime/src/views/UserReview/components/UserReviewItem.jsx +++ b/com.twin.app.shoptime/src/views/UserReview/components/UserReviewItem.jsx @@ -47,6 +47,11 @@ const UserReviewItem = ({ return iso.replace(/-/g, '.'); }; + // review๊ฐ€ ์—†์œผ๋ฉด ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Œ + if (!review) { + return null; + } + const { reviewImageList, rvwRtng, rvwRgstDtt, rvwCtnt, rvwId, wrtrNknm, rvwWrtrId } = review; return ( diff --git a/com.twin.app.shoptime/src/views/UserReview/components/UserReviewsList.jsx b/com.twin.app.shoptime/src/views/UserReview/components/UserReviewsList.jsx index 9a3e2b51..0b5676d8 100644 --- a/com.twin.app.shoptime/src/views/UserReview/components/UserReviewsList.jsx +++ b/com.twin.app.shoptime/src/views/UserReview/components/UserReviewsList.jsx @@ -30,6 +30,8 @@ const UserReviewsList = ({ onNextPage, onPrevPage, isPaging = false, + panelInfo = null, + onReviewClick, // ์ƒ์œ„์—์„œ ์ „๋‹ฌ๋ฐ›์€ ๋ฆฌ๋ทฐ ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ }) => { const handleReviewClick = useCallback((review, index) => { console.log('[UserReviewsList] Review item clicked:', { @@ -37,7 +39,12 @@ const UserReviewsList = ({ index: index, review: review, }); - }, []); + + // ์ƒ์œ„๋กœ ํด๋ฆญ ์ด๋ฒคํŠธ ์ „๋‹ฌ + if (onReviewClick) { + onReviewClick(review, index); + } + }, [onReviewClick]); // โœ… API ํ•„ํ„ฐ ํ™œ์„ฑํ™” ์—ฌ๋ถ€ ํ™•์ธ // ํ•„ํ„ฐ๊ฐ€ ํ™œ์„ฑํ™”๋˜๋ฉด filteredReviewCount (ํ•„ํ„ฐ๋ง๋œ ๋ฆฌ๋ทฐ ๊ฐœ์ˆ˜) ์‚ฌ์šฉ