"[GIT AUTO COMMIT]: Fri Sep 05 2025 16:21:25 GMT+0900 (대한민국 표준시)"
This commit is contained in:
BIN
com.twin.app.shoptime/assets/images/default_bg_image.png
Normal file
BIN
com.twin.app.shoptime/assets/images/default_bg_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 MiB |
BIN
com.twin.app.shoptime/assets/images/icons/ic_heart_3x.png
Normal file
BIN
com.twin.app.shoptime/assets/images/icons/ic_heart_3x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -11,8 +11,8 @@
|
|||||||
rgba(0, 0, 0, 0.539) 73.73%,
|
rgba(0, 0, 0, 0.539) 73.73%,
|
||||||
rgba(0, 0, 0, 0.7) 100%
|
rgba(0, 0, 0, 0.7) 100%
|
||||||
),
|
),
|
||||||
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4));
|
linear-gradient(0deg, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)),
|
||||||
// var(--bg-url);
|
url("../../../assets/images/default_bg_image.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|||||||
@@ -1,49 +1,75 @@
|
|||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { PropTypes } from 'prop-types';
|
||||||
|
import {
|
||||||
|
useDispatch,
|
||||||
|
useSelector,
|
||||||
|
} from 'react-redux';
|
||||||
|
|
||||||
|
import Spotlight from '@enact/spotlight';
|
||||||
/* eslint-disable react/jsx-no-bind */
|
/* eslint-disable react/jsx-no-bind */
|
||||||
// src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
|
// src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx
|
||||||
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
|
import SpotlightContainerDecorator
|
||||||
import Spottable from "@enact/spotlight/Spottable";
|
from '@enact/spotlight/SpotlightContainerDecorator';
|
||||||
import React, { useCallback, useRef, useState, useMemo, useEffect } from "react";
|
import Spottable from '@enact/spotlight/Spottable';
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
|
||||||
import Spotlight from "@enact/spotlight";
|
|
||||||
import { PropTypes } from "prop-types";
|
|
||||||
|
|
||||||
|
import { toggleShowAllReviews } from '../../../actions/productActions';
|
||||||
// ProductInfoSection imports
|
// ProductInfoSection imports
|
||||||
import TButton from "../../../components/TButton/TButton";
|
import TButton from '../../../components/TButton/TButton';
|
||||||
import { $L } from "../../../utils/helperMethods";
|
import useScrollTo from '../../../hooks/useScrollTo';
|
||||||
import {
|
import {
|
||||||
curry, pipe, when, isVal, isNotNil, defaultTo, defaultWith, get, identity, isEmpty, isNil, andThen, tap
|
andThen,
|
||||||
} from "../../../utils/fp";
|
curry,
|
||||||
import { toggleShowAllReviews } from "../../../actions/productActions";
|
defaultTo,
|
||||||
import FavoriteBtn from "../components/FavoriteBtn";
|
defaultWith,
|
||||||
import StarRating from "../components/StarRating";
|
get,
|
||||||
import ProductTag from "../components/ProductTag";
|
identity,
|
||||||
import DetailMobileSendPopUp from "../components/DetailMobileSendPopUp";
|
isEmpty,
|
||||||
import { SpotlightIds } from "../../../utils/SpotlightIds";
|
isNil,
|
||||||
import QRCode from "../ProductInfoSection/QRCode/QRCode";
|
isNotNil,
|
||||||
import ProductOverview from "../ProductOverview/ProductOverview";
|
isVal,
|
||||||
|
pipe,
|
||||||
|
tap,
|
||||||
|
when,
|
||||||
|
} from '../../../utils/fp';
|
||||||
|
import { $L } from '../../../utils/helperMethods';
|
||||||
|
import { SpotlightIds } from '../../../utils/SpotlightIds';
|
||||||
|
import CustomScrollbar from '../components/CustomScrollbar/CustomScrollbar';
|
||||||
|
import DetailMobileSendPopUp from '../components/DetailMobileSendPopUp';
|
||||||
|
import FavoriteBtn from '../components/FavoriteBtn';
|
||||||
|
import ProductTag from '../components/ProductTag';
|
||||||
|
import StarRating from '../components/StarRating';
|
||||||
// ProductContentSection imports
|
// ProductContentSection imports
|
||||||
import TScrollerDetail from "../components/TScroller/TScrollerDetail";
|
import TScrollerDetail from '../components/TScroller/TScrollerDetail';
|
||||||
import CustomScrollbar from "../components/CustomScrollbar/CustomScrollbar";
|
import ProductDescription
|
||||||
import useScrollTo from "../../../hooks/useScrollTo";
|
from '../ProductContentSection/ProductDescription/ProductDescription';
|
||||||
import ProductDetail from "../ProductContentSection/ProductDetail/ProductDetail.new";
|
import ProductDetail
|
||||||
import UserReviews from "../ProductContentSection/UserReviews/UserReviews";
|
from '../ProductContentSection/ProductDetail/ProductDetail.new';
|
||||||
import YouMayAlsoLike from "../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike";
|
import UserReviews from '../ProductContentSection/UserReviews/UserReviews';
|
||||||
import ProductDescription from "../ProductContentSection/ProductDescription/ProductDescription";
|
import YouMayAlsoLike
|
||||||
|
from '../ProductContentSection/YouMayAlsoLike/YouMayAlsoLike';
|
||||||
|
import QRCode from '../ProductInfoSection/QRCode/QRCode';
|
||||||
|
import ProductOverview from '../ProductOverview/ProductOverview';
|
||||||
// CSS imports
|
// CSS imports
|
||||||
// import infoCSS from "../ProductInfoSection/ProductInfoSection.module.less";
|
// import infoCSS from "../ProductInfoSection/ProductInfoSection.module.less";
|
||||||
// import contentCSS from "../ProductContentSection/ProductContentSection.module.less";
|
// import contentCSS from "../ProductContentSection/ProductContentSection.module.less";
|
||||||
import css from "./ProductAllSection.module.less";
|
import css from './ProductAllSection.module.less';
|
||||||
|
|
||||||
const Container = SpotlightContainerDecorator(
|
const Container = SpotlightContainerDecorator(
|
||||||
{
|
{
|
||||||
enterTo: "last-focused",
|
enterTo: "last-focused",
|
||||||
preserveld: true,
|
preserveld: true,
|
||||||
leaveFor: { right: "content-scroller-container" },
|
leaveFor: { right: "content-scroller-container" },
|
||||||
spotlightDirection: "vertical"
|
spotlightDirection: "vertical",
|
||||||
},
|
},
|
||||||
"div",
|
"div"
|
||||||
);
|
);
|
||||||
|
|
||||||
const ContentContainer = SpotlightContainerDecorator(
|
const ContentContainer = SpotlightContainerDecorator(
|
||||||
@@ -51,12 +77,12 @@ const ContentContainer = SpotlightContainerDecorator(
|
|||||||
enterTo: "default-element",
|
enterTo: "default-element",
|
||||||
preserveld: true,
|
preserveld: true,
|
||||||
leaveFor: {
|
leaveFor: {
|
||||||
left: "spotlight-product-info-section-container"
|
left: "spotlight-product-info-section-container",
|
||||||
},
|
},
|
||||||
restrict: "none",
|
restrict: "none",
|
||||||
spotlightDirection: "vertical"
|
spotlightDirection: "vertical",
|
||||||
},
|
},
|
||||||
"div",
|
"div"
|
||||||
);
|
);
|
||||||
|
|
||||||
const HorizontalContainer = SpotlightContainerDecorator(
|
const HorizontalContainer = SpotlightContainerDecorator(
|
||||||
@@ -64,17 +90,19 @@ const HorizontalContainer = SpotlightContainerDecorator(
|
|||||||
enterTo: "last-focused",
|
enterTo: "last-focused",
|
||||||
preserveld: true,
|
preserveld: true,
|
||||||
defaultElement: "spotlight-product-info-section-container",
|
defaultElement: "spotlight-product-info-section-container",
|
||||||
spotlightDirection: "horizontal"
|
spotlightDirection: "horizontal",
|
||||||
},
|
},
|
||||||
"div",
|
"div"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// FP: Pure function to determine product data based on type
|
// FP: Pure function to determine product data based on type
|
||||||
const getProductData = curry((productType, themeProductInfo, productInfo) =>
|
const getProductData = curry((productType, themeProductInfo, productInfo) =>
|
||||||
pipe(
|
pipe(
|
||||||
when(
|
when(
|
||||||
() => isVal(productType) && productType === "theme" && isVal(themeProductInfo),
|
() =>
|
||||||
|
isVal(productType) &&
|
||||||
|
productType === "theme" &&
|
||||||
|
isVal(themeProductInfo),
|
||||||
() => themeProductInfo
|
() => themeProductInfo
|
||||||
),
|
),
|
||||||
defaultTo(productInfo),
|
defaultTo(productInfo),
|
||||||
@@ -86,19 +114,14 @@ const getProductData = curry((productType, themeProductInfo, productInfo) =>
|
|||||||
const deriveFavoriteFlag = curry((favoriteOverride, productData) =>
|
const deriveFavoriteFlag = curry((favoriteOverride, productData) =>
|
||||||
pipe(
|
pipe(
|
||||||
when(isNotNil, identity),
|
when(isNotNil, identity),
|
||||||
defaultWith(() =>
|
defaultWith(() => pipe(get("favorYn"), defaultTo("N"))(productData))
|
||||||
pipe(
|
|
||||||
get("favorYn"),
|
|
||||||
defaultTo("N")
|
|
||||||
)(productData)
|
|
||||||
)
|
|
||||||
)(favoriteOverride)
|
)(favoriteOverride)
|
||||||
);
|
);
|
||||||
|
|
||||||
// FP: Pure function to extract review grade and order phone
|
// FP: Pure function to extract review grade and order phone
|
||||||
const extractProductMeta = (productInfo) => ({
|
const extractProductMeta = (productInfo) => ({
|
||||||
revwGrd: get("revwGrd", productInfo),
|
revwGrd: get("revwGrd", productInfo),
|
||||||
orderPhnNo: get("orderPhnNo", productInfo)
|
orderPhnNo: get("orderPhnNo", productInfo),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 레이아웃 확인용 샘플 컴포넌트 - Spottable로 변경
|
// 레이아웃 확인용 샘플 컴포넌트 - Spottable로 변경
|
||||||
@@ -128,14 +151,16 @@ export default function ProductAllSection({
|
|||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const productData = useMemo(() =>
|
const productData = useMemo(
|
||||||
getProductData(productType, themeProductInfo, productInfo),
|
() => getProductData(productType, themeProductInfo, productInfo),
|
||||||
[productType, themeProductInfo, productInfo]
|
[productType, themeProductInfo, productInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
// [임시 테스트] LayoutSample 클릭 핸들러
|
// [임시 테스트] LayoutSample 클릭 핸들러
|
||||||
const handleLayoutSampleClick = useCallback(() => {
|
const handleLayoutSampleClick = useCallback(() => {
|
||||||
console.log("[Test] LayoutSample clicked - dispatching toggleShowAllReviews");
|
console.log(
|
||||||
|
"[Test] LayoutSample clicked - dispatching toggleShowAllReviews"
|
||||||
|
);
|
||||||
dispatch(toggleShowAllReviews());
|
dispatch(toggleShowAllReviews());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
@@ -145,19 +170,21 @@ export default function ProductAllSection({
|
|||||||
hasProductData: !!productData,
|
hasProductData: !!productData,
|
||||||
imgUrls600: productData?.imgUrls600,
|
imgUrls600: productData?.imgUrls600,
|
||||||
imgUrls600Length: productData?.imgUrls600?.length,
|
imgUrls600Length: productData?.imgUrls600?.length,
|
||||||
imgUrls600Type: Array.isArray(productData?.imgUrls600) ? 'array' : typeof productData?.imgUrls600
|
imgUrls600Type: Array.isArray(productData?.imgUrls600)
|
||||||
|
? "array"
|
||||||
|
: typeof productData?.imgUrls600,
|
||||||
});
|
});
|
||||||
}, [productData]);
|
}, [productData]);
|
||||||
|
|
||||||
const { revwGrd, orderPhnNo } = useMemo(() =>
|
const { revwGrd, orderPhnNo } = useMemo(
|
||||||
extractProductMeta(productInfo),
|
() => extractProductMeta(productInfo),
|
||||||
[productInfo]
|
[productInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
// FP: derive favorite flag from props with local override, avoid non-I/O useEffect
|
// FP: derive favorite flag from props with local override, avoid non-I/O useEffect
|
||||||
const [favoriteOverride, setFavoriteOverride] = useState(null);
|
const [favoriteOverride, setFavoriteOverride] = useState(null);
|
||||||
const favoriteFlag = useMemo(() =>
|
const favoriteFlag = useMemo(
|
||||||
deriveFavoriteFlag(favoriteOverride, productData),
|
() => deriveFavoriteFlag(favoriteOverride, productData),
|
||||||
[favoriteOverride, productData]
|
[favoriteOverride, productData]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -167,10 +194,12 @@ export default function ProductAllSection({
|
|||||||
const showUserReviewsButton = false; // 임시 변경 - 버튼 숨김
|
const showUserReviewsButton = false; // 임시 변경 - 버튼 숨김
|
||||||
const showUserReviewsSection = true; // 임시 변경 - 섹션은 항상 표시
|
const showUserReviewsSection = true; // 임시 변경 - 섹션은 항상 표시
|
||||||
|
|
||||||
const reviewTotalCount = useSelector(pipe(
|
const reviewTotalCount = useSelector(
|
||||||
|
pipe(
|
||||||
get(["product", "reviewData", "reviewDetail", "totRvwCnt"]),
|
get(["product", "reviewData", "reviewDetail", "totRvwCnt"]),
|
||||||
defaultTo(0)
|
defaultTo(0)
|
||||||
));
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// User Reviews 스크롤 핸들러 추가
|
// User Reviews 스크롤 핸들러 추가
|
||||||
const handleUserReviewsClick = useCallback(
|
const handleUserReviewsClick = useCallback(
|
||||||
@@ -181,13 +210,9 @@ export default function ProductAllSection({
|
|||||||
const scrollContainerRef = useRef(null);
|
const scrollContainerRef = useRef(null);
|
||||||
const { getScrollTo, scrollTop } = useScrollTo();
|
const { getScrollTo, scrollTop } = useScrollTo();
|
||||||
|
|
||||||
|
|
||||||
// FP: Pure function for mobile popup state change
|
// FP: Pure function for mobile popup state change
|
||||||
const handleShopByMobileOpen = useCallback(
|
const handleShopByMobileOpen = useCallback(
|
||||||
pipe(
|
pipe(() => true, setMobileSendPopupOpen),
|
||||||
() => true,
|
|
||||||
setMobileSendPopupOpen
|
|
||||||
),
|
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -237,7 +262,6 @@ export default function ProductAllSection({
|
|||||||
[scrollToSection]
|
[scrollToSection]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HorizontalContainer className={css.detailArea}>
|
<HorizontalContainer className={css.detailArea}>
|
||||||
{/* Left Margin Section - 60px */}
|
{/* Left Margin Section - 60px */}
|
||||||
@@ -277,7 +301,10 @@ export default function ProductAllSection({
|
|||||||
<Container className={css.buttonContainer}>
|
<Container className={css.buttonContainer}>
|
||||||
<TButton
|
<TButton
|
||||||
spotlightId={SpotlightIds.DETAIL_SHOPBYMOBILE}
|
spotlightId={SpotlightIds.DETAIL_SHOPBYMOBILE}
|
||||||
className={css.shopByMobileButton}
|
className={classNames(
|
||||||
|
css.shopByMobileButton,
|
||||||
|
css.shopByMobileOne
|
||||||
|
)}
|
||||||
onClick={handleShopByMobileOpen}
|
onClick={handleShopByMobileOpen}
|
||||||
onSpotlightUp={handleSpotlightUpToBackButton}
|
onSpotlightUp={handleSpotlightUpToBackButton}
|
||||||
>
|
>
|
||||||
@@ -293,6 +320,7 @@ export default function ProductAllSection({
|
|||||||
selectedPrdtId={panelInfo && panelInfo.prdtId}
|
selectedPrdtId={panelInfo && panelInfo.prdtId}
|
||||||
favoriteFlag={favoriteFlag}
|
favoriteFlag={favoriteFlag}
|
||||||
onFavoriteFlagChanged={onFavoriteFlagChanged}
|
onFavoriteFlagChanged={onFavoriteFlagChanged}
|
||||||
|
kind={"item_detail"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -324,14 +352,15 @@ export default function ProductAllSection({
|
|||||||
{$L("PRODUCT DETAILS")}
|
{$L("PRODUCT DETAILS")}
|
||||||
</TButton>
|
</TButton>
|
||||||
{/* 🔧 [임시] 고객 데모용 조건 변경: showUserReviewsButton (원래: reviewTotalCount > 0) */}
|
{/* 🔧 [임시] 고객 데모용 조건 변경: showUserReviewsButton (원래: reviewTotalCount > 0) */}
|
||||||
{showUserReviewsButton && (
|
{/* {showUserReviewsButton && ( */}
|
||||||
|
{reviewTotalCount > 0 && (
|
||||||
<TButton
|
<TButton
|
||||||
className={css.userReviewsButton}
|
className={css.userReviewsButton}
|
||||||
onClick={handleUserReviewsClick}
|
onClick={handleUserReviewsClick}
|
||||||
spotlightId="user-reviews-button"
|
spotlightId="user-reviews-button"
|
||||||
>
|
>
|
||||||
{$L(
|
{$L(
|
||||||
`USER REVIEWS (${reviewTotalCount > 100 ? "100" : reviewTotalCount || "0"})`,
|
`USER REVIEWS (${reviewTotalCount > 100 ? "100" : reviewTotalCount || "0"})`
|
||||||
)}
|
)}
|
||||||
</TButton>
|
</TButton>
|
||||||
)}
|
)}
|
||||||
@@ -344,7 +373,8 @@ export default function ProductAllSection({
|
|||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
{panelInfo &&
|
{panelInfo &&
|
||||||
panelInfo && panelInfo.type === "theme" &&
|
panelInfo &&
|
||||||
|
panelInfo.type === "theme" &&
|
||||||
!openThemeItemOverlay && (
|
!openThemeItemOverlay && (
|
||||||
<TButton
|
<TButton
|
||||||
className={css.themeButton}
|
className={css.themeButton}
|
||||||
@@ -390,7 +420,8 @@ export default function ProductAllSection({
|
|||||||
></div>
|
></div>
|
||||||
<LayoutSample onClick={handleLayoutSampleClick} />
|
<LayoutSample onClick={handleLayoutSampleClick} />
|
||||||
<div id="product-details-section">
|
<div id="product-details-section">
|
||||||
{productData?.imgUrls600 && productData.imgUrls600.length > 0 ? (
|
{productData?.imgUrls600 &&
|
||||||
|
productData.imgUrls600.length > 0 ? (
|
||||||
productData.imgUrls600.map((image, index) => (
|
productData.imgUrls600.map((image, index) => (
|
||||||
<ProductDetail
|
<ProductDetail
|
||||||
key={`product-detail-${index}`}
|
key={`product-detail-${index}`}
|
||||||
@@ -398,7 +429,7 @@ export default function ProductAllSection({
|
|||||||
...productData,
|
...productData,
|
||||||
singleImage: image,
|
singleImage: image,
|
||||||
imageIndex: index,
|
imageIndex: index,
|
||||||
totalImages: productData.imgUrls600.length
|
totalImages: productData.imgUrls600.length,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
@@ -409,10 +440,16 @@ export default function ProductAllSection({
|
|||||||
<div id="product-description-section">
|
<div id="product-description-section">
|
||||||
<ProductDescription productInfo={productData} />
|
<ProductDescription productInfo={productData} />
|
||||||
</div>
|
</div>
|
||||||
<div id="scroll-marker-user-reviews" className={css.scrollMarker}></div>
|
<div
|
||||||
|
id="scroll-marker-user-reviews"
|
||||||
|
className={css.scrollMarker}
|
||||||
|
></div>
|
||||||
{/* Description 바로 아래에 UserReviews 항상 표시 (조건 제거) */}
|
{/* Description 바로 아래에 UserReviews 항상 표시 (조건 제거) */}
|
||||||
<div id="user-reviews-section">
|
<div id="user-reviews-section">
|
||||||
<UserReviews productInfo={productData} panelInfo={panelInfo} />
|
<UserReviews
|
||||||
|
productInfo={productData}
|
||||||
|
panelInfo={panelInfo}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -420,13 +457,15 @@ export default function ProductAllSection({
|
|||||||
className={css.scrollMarker}
|
className={css.scrollMarker}
|
||||||
></div>
|
></div>
|
||||||
<div id="you-may-also-like-section">
|
<div id="you-may-also-like-section">
|
||||||
<YouMayAlsoLike productInfo={productData} panelInfo={panelInfo} />
|
<YouMayAlsoLike
|
||||||
|
productInfo={productData}
|
||||||
|
panelInfo={panelInfo}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TScrollerDetail>
|
</TScrollerDetail>
|
||||||
</div>
|
</div>
|
||||||
</ContentContainer>
|
</ContentContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</HorizontalContainer>
|
</HorizontalContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 60px;
|
left: 60px;
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 645px;
|
width: 650px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -88,7 +88,9 @@
|
|||||||
// gap 대신 margin 사용 (Chromium 68 호환성)
|
// gap 대신 margin 사용 (Chromium 68 호환성)
|
||||||
> * {
|
> * {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
&:last-child { margin-bottom: 0; }
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +117,6 @@
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
position: relative; // 자식 absolute 요소의 기준점
|
position: relative; // 자식 absolute 요소의 기준점
|
||||||
|
|
||||||
|
|
||||||
// 스크롤러 오버라이드 (1210px = 30px + content + 스크롤바)
|
// 스크롤러 오버라이드 (1210px = 30px + content + 스크롤바)
|
||||||
.scrollerOverride {
|
.scrollerOverride {
|
||||||
width: 1210px; // 절대 크기 지정
|
width: 1210px; // 절대 크기 지정
|
||||||
@@ -139,17 +140,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: #9C9C9C; // 스크롤바 색상
|
background: #9c9c9c; // 스크롤바 색상
|
||||||
border-radius: 3px; // 스크롤바 둥근 모서리
|
border-radius: 3px; // 스크롤바 둥근 모서리
|
||||||
}
|
}
|
||||||
|
|
||||||
// 스크롤바 thumb에 hover 효과 적용
|
// 스크롤바 thumb에 hover 효과 적용
|
||||||
&:hover::-webkit-scrollbar-thumb {
|
&:hover::-webkit-scrollbar-thumb {
|
||||||
background: #C72054;
|
background: #c72054;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 내부 콘텐츠는 별도 너비 계산 없이 100%를 사용
|
// 내부 콘텐츠는 별도 너비 계산 없이 100%를 사용
|
||||||
> div {
|
> div {
|
||||||
width: 100%; // 부모의 패딩을 제외한 나머지 공간(1114px)을 모두 사용
|
width: 100%; // 부모의 패딩을 제외한 나머지 공간(1114px)을 모두 사용
|
||||||
@@ -229,7 +229,6 @@
|
|||||||
|
|
||||||
// (중복 제거됨) 최상위 스크롤러/섹션 정의는 .scrollerWrapper 중첩 내부로 이동
|
// (중복 제거됨) 최상위 스크롤러/섹션 정의는 .scrollerWrapper 중첩 내부로 이동
|
||||||
|
|
||||||
|
|
||||||
// ProductDetailCard 스타일 참고 - 크기/간격만 적용
|
// ProductDetailCard 스타일 참고 - 크기/간격만 적용
|
||||||
|
|
||||||
// 헤더 컨텐츠 영역 (태그, 별점)
|
// 헤더 컨텐츠 영역 (태그, 별점)
|
||||||
@@ -237,7 +236,6 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 모바일 쇼핑 섹션 (mobileSection 참고)
|
// 모바일 쇼핑 섹션 (mobileSection 참고)
|
||||||
@@ -250,7 +248,9 @@
|
|||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
&:last-child { margin-right: 0; }
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,14 +258,17 @@
|
|||||||
flex: 1 1 0 !important;
|
flex: 1 1 0 !important;
|
||||||
width: auto !important; // flex로 크기 조정
|
width: auto !important; // flex로 크기 조정
|
||||||
height: 60px !important;
|
height: 60px !important;
|
||||||
background: rgba(68, 68, 68, 0.50) !important;
|
background: rgba(68, 68, 68, 0.5) !important;
|
||||||
border-radius: 6px !important;
|
border-radius: 6px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin: 0 !important;
|
margin: 0;
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center !important;
|
align-items: center !important;
|
||||||
justify-content: center !important;
|
justify-content: center !important;
|
||||||
|
&.shopByMobileOne {
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.shopByMobileText {
|
.shopByMobileText {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
@@ -308,7 +311,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.callToOrderText {
|
.callToOrderText {
|
||||||
color: #EAEAEA;
|
color: #eaeaea;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
font-family: @baseFont; // LG Smart 폰트 사용
|
font-family: @baseFont; // LG Smart 폰트 사용
|
||||||
font-weight: 400; // Bold에서 Regular로 변경
|
font-weight: 400; // Bold에서 Regular로 변경
|
||||||
@@ -322,7 +325,9 @@
|
|||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
&:last-child { margin-right: 0; }
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.phoneIconContainer {
|
.phoneIconContainer {
|
||||||
@@ -337,7 +342,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: #EAEAEA;
|
|
||||||
// 전화 아이콘 이미지 또는 CSS로 구현
|
// 전화 아이콘 이미지 또는 CSS로 구현
|
||||||
background-image: url("../../../../assets/images/icons/ic-gr-call-1.png");
|
background-image: url("../../../../assets/images/icons/ic-gr-call-1.png");
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
@@ -347,9 +351,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.phoneNumber {
|
.phoneNumber {
|
||||||
color: #EAEAEA;
|
color: #eaeaea;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
font-family: 'LG Smart UI';
|
font-family: "LG Smart UI";
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
@@ -365,7 +369,9 @@
|
|||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
&:last-child { margin-bottom: 0; }
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,14 +387,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
color: #EAEAEA;
|
color: #eaeaea;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
font-family: @baseFont; // LG Smart 폰트 사용
|
font-family: @baseFont; // LG Smart 폰트 사용
|
||||||
font-weight: 400; // Bold에서 Regular로 변경
|
font-weight: 400; // Bold에서 Regular로 변경
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: #C72054; // 포커스시만 빨간색
|
background: #c72054; // 포커스시만 빨간색
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,7 +406,7 @@
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: #C72054;
|
background: #c72054;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +420,9 @@
|
|||||||
|
|
||||||
> * {
|
> * {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
&:last-child { margin-bottom: 0; }
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,37 +438,12 @@
|
|||||||
// 내부 div (productInfoWrapper)
|
// 내부 div (productInfoWrapper)
|
||||||
> div {
|
> div {
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
> * {
|
|
||||||
margin-right: 15px; // ProductDetailCard와 동일한 간격
|
|
||||||
&:last-child { margin-right: 0; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 가격 섹션 (flex로 남은 공간 차지)
|
|
||||||
> div:first-child {
|
> div:first-child {
|
||||||
flex: 1 1 0;
|
width: 380px;
|
||||||
display: flex;
|
text-align: left;
|
||||||
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; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
import React, { useCallback, useMemo } from "react";
|
import React, {
|
||||||
import { useSelector } from "react-redux";
|
useCallback,
|
||||||
import usePriceInfo from "../../../../hooks/usePriceInfo";
|
useMemo,
|
||||||
import css from "./ProductPriceDisplay.module.less";
|
} from 'react';
|
||||||
import { $L } from "../../../../utils/helperMethods";
|
|
||||||
import classNames from "classnames";
|
import classNames from 'classnames';
|
||||||
import ShopByMobilePriceDisplay from "./ShopByMobilePriceDisplay/ShopByMobilePriceDisplay";
|
import { useSelector } from 'react-redux';
|
||||||
import BuyNowPriceDisplay from "./BuyNowPriceDisplay/BuyNowPriceDisplay";
|
|
||||||
|
import usePriceInfo from '../../../../hooks/usePriceInfo';
|
||||||
|
import { $L } from '../../../../utils/helperMethods';
|
||||||
|
import BuyNowPriceDisplay from './BuyNowPriceDisplay/BuyNowPriceDisplay';
|
||||||
|
import css from './ProductPriceDisplay.module.less';
|
||||||
|
import ShopByMobilePriceDisplay
|
||||||
|
from './ShopByMobilePriceDisplay/ShopByMobilePriceDisplay';
|
||||||
|
|
||||||
export default function ProductPriceDisplay({ productType, productInfo }) {
|
export default function ProductPriceDisplay({ productType, productInfo }) {
|
||||||
const webOSVersion = useSelector(
|
const webOSVersion = useSelector(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
.partnerName {
|
.partnerName {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
color: @COLOR_BLACK;
|
color: @COLOR_WHITE;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
.flex(@alignCenter:flex-start,@direction:column);
|
.flex(@alignCenter:flex-start,@direction:column);
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import React, { useCallback, useMemo } from "react";
|
import React, {
|
||||||
import usePriceInfo from "../../../../../hooks/usePriceInfo";
|
useCallback,
|
||||||
import { $L } from "../../../../../utils/helperMethods";
|
useMemo,
|
||||||
import css from "./ShopByMobilePriceDisplay.module.less";
|
} from 'react';
|
||||||
import classNames from "classnames";
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import usePriceInfo from '../../../../../hooks/usePriceInfo';
|
||||||
|
import { $L } from '../../../../../utils/helperMethods';
|
||||||
|
import css from './ShopByMobilePriceDisplay.module.less';
|
||||||
|
|
||||||
export default function ShopByMobilePriceDisplay({
|
export default function ShopByMobilePriceDisplay({
|
||||||
priceData,
|
priceData,
|
||||||
@@ -103,14 +108,14 @@ export default function ShopByMobilePriceDisplay({
|
|||||||
return (
|
return (
|
||||||
<div className={css.wrapper}>
|
<div className={css.wrapper}>
|
||||||
<div className={css.topLayer}>
|
<div className={css.topLayer}>
|
||||||
{discountRate && Number(discountRate.replace("%", "")) > 4 && (
|
|
||||||
<div className={css.rateTag}>{discountRate}</div>
|
|
||||||
)}
|
|
||||||
<span className={css.name}>
|
<span className={css.name}>
|
||||||
{patncNm
|
{patncNm
|
||||||
? patncNm + " " + $L("Price")
|
? patncNm + " " + $L("Price")
|
||||||
: patnrName + $L("Price")}
|
: patnrName + $L("Price")}
|
||||||
</span>
|
</span>
|
||||||
|
{discountRate && Number(discountRate.replace("%", "")) > 4 && (
|
||||||
|
<div className={css.rateTag}>{discountRate}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={css.btmLayer}>
|
<div className={css.btmLayer}>
|
||||||
<span className={css.price}>
|
<span className={css.price}>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
.partnerName {
|
.partnerName {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
color: @COLOR_BLACK;
|
color: @COLOR_WHITE;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
.flex(@alignCenter:flex-start,@direction:column);
|
.flex(@alignCenter:flex-start,@direction:column);
|
||||||
@@ -31,7 +31,8 @@
|
|||||||
.name {
|
.name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
color: @COLOR_GRAY07;
|
color: @COLOR_WHITE;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
.btmLayer {
|
.btmLayer {
|
||||||
.flex();
|
.flex();
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
import React, { useCallback, useEffect } from "react";
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
import classNames from "classnames";
|
import classNames from 'classnames';
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import {
|
||||||
|
useDispatch,
|
||||||
|
useSelector,
|
||||||
|
} from 'react-redux';
|
||||||
|
|
||||||
import Spotlight from "@enact/spotlight";
|
import Spotlight from '@enact/spotlight';
|
||||||
|
|
||||||
import { setHidePopup, setShowPopup } from "../../../actions/commonActions";
|
import {
|
||||||
|
setHidePopup,
|
||||||
|
setShowPopup,
|
||||||
|
} from '../../../actions/commonActions';
|
||||||
|
import { sendLogTotalRecommend } from '../../../actions/logActions';
|
||||||
import {
|
import {
|
||||||
getMyFavoriteFlag,
|
getMyFavoriteFlag,
|
||||||
setMainLikeCategory,
|
setMainLikeCategory,
|
||||||
} from "../../../actions/mainActions";
|
} from '../../../actions/mainActions';
|
||||||
import { deleteMyFavorite } from "../../../actions/myPageActions";
|
import { deleteMyFavorite } from '../../../actions/myPageActions';
|
||||||
import TButton from "../../../components/TButton/TButton";
|
import TButton from '../../../components/TButton/TButton';
|
||||||
import TPopUp from "../../../components/TPopUp/TPopUp";
|
import TPopUp from '../../../components/TPopUp/TPopUp';
|
||||||
import * as Config from "../../../utils/Config";
|
import { disableNotification } from '../../../lunaSend';
|
||||||
import { $L } from "../../../utils/helperMethods";
|
import * as Config from '../../../utils/Config';
|
||||||
import css from "./FavoriteBtn.module.less";
|
import { $L } from '../../../utils/helperMethods';
|
||||||
import { disableNotification } from "../../../lunaSend";
|
import css from './FavoriteBtn.module.less';
|
||||||
import { sendLogTotalRecommend } from "../../../actions/logActions";
|
|
||||||
|
|
||||||
export default function FavoriteBtn({
|
export default function FavoriteBtn({
|
||||||
selectedPatnrId,
|
selectedPatnrId,
|
||||||
@@ -25,6 +34,7 @@ export default function FavoriteBtn({
|
|||||||
favoriteFlag,
|
favoriteFlag,
|
||||||
onFavoriteFlagChanged,
|
onFavoriteFlagChanged,
|
||||||
logMenu,
|
logMenu,
|
||||||
|
kind,
|
||||||
}) {
|
}) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { popupVisible, activePopup } = useSelector(
|
const { popupVisible, activePopup } = useSelector(
|
||||||
@@ -61,7 +71,7 @@ export default function FavoriteBtn({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [dispatch, favoriteFlag, selectedPatnrId, selectedPrdtId , logMenu]);
|
}, [dispatch, favoriteFlag, selectedPatnrId, selectedPrdtId, logMenu]);
|
||||||
|
|
||||||
const PopUpOnClick = useCallback(() => {
|
const PopUpOnClick = useCallback(() => {
|
||||||
setTimeout(() => Spotlight.focus("favoriteBtn"));
|
setTimeout(() => Spotlight.focus("favoriteBtn"));
|
||||||
@@ -70,7 +80,12 @@ export default function FavoriteBtn({
|
|||||||
}, [dispatch, favoriteFlag, onFavoriteFlagChanged]);
|
}, [dispatch, favoriteFlag, onFavoriteFlagChanged]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={css.favorBtnContainer}>
|
<div
|
||||||
|
className={classNames(
|
||||||
|
css.favorBtnContainer,
|
||||||
|
kind === "item_detail" ? css.smallSize : ""
|
||||||
|
)}
|
||||||
|
>
|
||||||
<TButton
|
<TButton
|
||||||
className={classNames(
|
className={classNames(
|
||||||
favoriteFlag === "N" ? css.favorBtn : css.favorUnableBtn
|
favoriteFlag === "N" ? css.favorBtn : css.favorUnableBtn
|
||||||
|
|||||||
@@ -3,6 +3,29 @@
|
|||||||
|
|
||||||
.favorBtnContainer {
|
.favorBtnContainer {
|
||||||
height: 78px;
|
height: 78px;
|
||||||
|
&.smallSize {
|
||||||
|
height: 60px;
|
||||||
|
|
||||||
|
.flex();
|
||||||
|
.favorBtn {
|
||||||
|
background-color: rgba(68, 68, 68, 0.5) !important;
|
||||||
|
min-width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background-image: url(../../../../assets/images/icons/ic_heart_3x.png);
|
||||||
|
.imgElement(29px, 25px, center, center);
|
||||||
|
&:focus {
|
||||||
|
background-color: @PRIMARY_COLOR_RED !important; // 포커스시 빨간색 배경
|
||||||
|
outline: 2px solid @PRIMARY_COLOR_RED !important;
|
||||||
|
background-image: url(../../../../assets/images/icons/ic_heart_3x.png);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.favorUnableBtn {
|
||||||
|
min-width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
background-image: url(../../../../assets/images/icons/ic-heart-nor@3x.png);
|
||||||
|
.imgElement(54px, 54px, center, center);
|
||||||
|
}
|
||||||
|
}
|
||||||
.flex();
|
.flex();
|
||||||
.favorBtn {
|
.favorBtn {
|
||||||
min-width: 78px;
|
min-width: 78px;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ProductBadge {
|
.ProductBadge {
|
||||||
margin-left: auto;
|
|
||||||
height: 42px;
|
height: 42px;
|
||||||
> span {
|
> span {
|
||||||
height: 42px;
|
height: 42px;
|
||||||
|
|||||||
1
git-auto-commit
Submodule
1
git-auto-commit
Submodule
Submodule git-auto-commit added at d42d415623
Reference in New Issue
Block a user