[장바구니]

- 포커스 관련 추가수정
This commit is contained in:
junghoon86.park
2025-11-06 16:01:24 +09:00
parent 14e5ea9ac0
commit 1a3d1bb77c
3 changed files with 165 additions and 168 deletions

View File

@@ -167,7 +167,7 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
return (
<TPanel spotlightId={spotlightId} isTabActivated={true}>
<TBody className={css.tbody} cbScrollTo={getScrollTo} >
<TBody className={css.tbody} cbScrollTo={getScrollTo}>
<THeader
className={css.theader}
kind={"cartPanel"}
@@ -179,11 +179,11 @@ export default function CartPanel({ spotlightId, scrollOptions = [], panelInfo }
<div className={css.leftSection}>
<CartSidebar cartInfo={displayCartData}/>
</div>
<div className={css.rightSection} >
<div className={css.rightSection}>
{/* 오른쪽 상품 영역 */}
{displayCartData && displayCartData?.length > 0 ? (
<TScroller
className={css.tScroller}
className={css.tScroller}
{...scrollOptions}
>
<CartProductBar cartInfo={displayCartData} getScrollTo={getScrollTo} scrollTop={scrollTop}/>

View File

@@ -14,7 +14,6 @@ import {
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import logoImage from '../../../assets/images/ic-partners-qvc@3x.png';
import defaultImage from '../../../assets/images/img-thumb-empty-144@3x.png';
@@ -30,7 +29,6 @@ import {
import CustomImage from '../../components/CustomImage/CustomImage';
import TButton from '../../components/TButton/TButton';
import TCheckBoxSquare from '../../components/TCheckBox/TCheckBoxSquare';
import useScrollTo from '../../hooks/useScrollTo';
import store from '../../store/store';
import { BUYNOW_CONFIG } from '../../utils/BuyNowConfig';
import {
@@ -93,9 +91,7 @@ const OptimizedImage = ({ src, alt, className, fallbackSrc }) => {
};
const Container = SpotlightContainerDecorator("div");
const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
const dispatch = useDispatch();
// 항상 호출되어야 하는 Hook들
@@ -268,182 +264,180 @@ const CartProduct = ({ cartInfo, getScrollTo, scrollTop }) => {
[scrollTop]
);
return (
<>
{Object.entries(groupedCartData).map(([partnerKey, group],index) => {
{Object.entries(groupedCartData).map(([partnerKey, group], index) => {
const totals = calculatePartnerTotal(group.items);
return (
<Container className={css.productBox} spotlightId={"cartProduct_" + index} key={partnerKey}>
{index === 0 && (
return (
<Container key={partnerKey} className={css.productBox}>
{index === 0 && (
<div className={css.scrollMarker} id="cartFocus_0"></div>
)}
<div className={css.partnerSection}>
{/* 파트너사 정보 - 한 번만 표시 */}
<div className={css.productCompany}>
<div className={css.logo}>
<CustomImage
className={css.img}
src={group.partnerInfo.logo}
fallbackSrc={defaultImage}
/>
</div>
<span className={css.company}>
{group.partnerInfo.name}
<span className={css.productEa}>
({group.items.length} ITEM{group.items.length > 1 ? 'S' : ''})
</span>
)}
<div className={css.partnerSection}>
{/* 파트너사 정보 - 한 번만 표시 */}
<div className={css.productCompany}>
<div className={css.logo}>
<CustomImage
className={css.img}
src={group.partnerInfo.logo}
fallbackSrc={defaultImage}
/>
</div>
<span className={css.company}>
{group.partnerInfo.name}
<span className={css.productEa}>
({group.items.length} ITEM{group.items.length > 1 ? 'S' : ''})
</span>
</span>
</div>
{/* 파트너사별 가격 총합 - 한 번만 표시 */}
<div className={css.productPrice}>
<div className={css.leftSection}>
Product Total ${totals.productTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})} +
{/* Option ${totals.optionTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})} + */}
Option $0.00 +
S&H ${totals.shippingTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</div>
<div className={css.rightSection}>
Total
<span className={css.total}>
$<span className={css.totalAcc}>
{totals.total.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
</div>
</span>
</div>
</div>
{/* 파트너사별 가격 총합 - 한 번만 표시 */}
<div className={css.productPrice}>
<div className={css.leftSection}>
Product Total ${totals.productTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})} +
{/* Option ${totals.optionTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})} + */}
Option $0.00 +
S&H ${totals.shippingTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</div>
<div className={css.rightSection}>
Total
<span className={css.total}>
$<span className={css.totalAcc}>
{totals.total.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
{/* 해당 파트너사의 상품 리스트 - map으로 반복 */}
{group.items.map((item, groupIndex) => {
// CheckOutPanel과 동일한 이미지 처리 방식 적용
const normalizedItem = normalizeProductDataForDisplay(item);
// ✅ 이미지 우선순위: ProductAllSection 고품질 이미지 → 기타 모든 이미지 필드
const imageSrc = (item.imgUrls600 && item.imgUrls600[0]) || // ✅ ProductAllSection의 고품질 이미지
(item.thumbnailUrl960) || // ✅ 960px 썸네일
(normalizedItem.imgUrls && normalizedItem.imgUrls[0]?.imgUrl) ||
(item.imgUrls && item.imgUrls[0]?.imgUrl) ||
(item.imgList && item.imgList[0]?.imgUrl) ||
normalizedItem.imgUrl ||
item.imgUrl ||
item.thumbnailUrl ||
defaultImage;
return (
<div key={item.prodSno} className={css.product}>
<div className={css.leftBox}>
<div className={css.checkBox}>
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId={`productCheckbox-${item.prodSno}`}
selected={isItemSelected(item.prodSno)}
onToggle={handleCheckboxToggle(item.prodSno)}
onFocus={()=> {handleFocus(index, groupIndex)}}
/>
<span className={css.productId}>
ID : {item.prdtId}
</span>
</div>
</div>
{/* 해당 파트너사의 상품 리스트 - map으로 반복 */}
{group.items.map((item, groupIndex) => {
// CheckOutPanel과 동일한 이미지 처리 방식 적용
const normalizedItem = normalizeProductDataForDisplay(item);
// ✅ 이미지 우선순위: ProductAllSection 고품질 이미지 → 기타 모든 이미지 필드
const imageSrc = (item.imgUrls600 && item.imgUrls600[0]) || // ✅ ProductAllSection의 고품질 이미지
(item.thumbnailUrl960) || // ✅ 960px 썸네일
(normalizedItem.imgUrls && normalizedItem.imgUrls[0]?.imgUrl) ||
(item.imgUrls && item.imgUrls[0]?.imgUrl) ||
(item.imgList && item.imgList[0]?.imgUrl) ||
normalizedItem.imgUrl ||
item.imgUrl ||
item.thumbnailUrl ||
defaultImage;
return (
<div key={item.prodSno} className={css.product}>
<div className={css.leftBox}>
<div className={css.checkBox}>
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId={`productCheckbox-${item.prodSno}`}
selected={isItemSelected(item.prodSno)}
onToggle={handleCheckboxToggle(item.prodSno)}
onFocus={()=> {handleFocus(index, groupIndex)}}
/>
<span className={css.productId}>
ID : {item.prdtId}
</span>
<div className={css.productInfo}>
<div className={css.leftSection}>
<OptimizedImage
className={css.productImage}
src={imageSrc}
fallbackSrc={defaultImage}
alt={item.prdtNm || 'Product Image'}
/>
</div>
<div className={css.rightSection}>
<div className={css.productNm}>
{item.prdtNm}
</div>
<div className={css.productInfo}>
<div className={css.leftSection}>
<OptimizedImage
className={css.productImage}
src={imageSrc}
fallbackSrc={defaultImage}
alt={item.prdtNm || 'Product Image'}
/>
</div>
<div className={css.rightSection}>
<div className={css.productNm}>
{item.prdtNm}
</div>
{item.optNm && (
<div className={css.optionNm}>{item.optNm}</div>
)}
<div className={css.accountBox}>
<span className={css.account}>
${parseFloat(item.price3 || item.price2 || 0).toLocaleString('en-US', {
{item.optNm && (
<div className={css.optionNm}>{item.optNm}</div>
)}
<div className={css.accountBox}>
<span className={css.account}>
${parseFloat(item.price3 || item.price2 || 0).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
<div className={css.accountInfo}>
{item.price2 && (
<span className={css.originalAcc}>
${parseFloat(item.price2).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
<div className={css.accountInfo}>
{item.price2 && (
<span className={css.originalAcc}>
${parseFloat(item.price2).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
)}
{item.price5 && parseFloat(item.price5) > 0 && (
<span className={css.optionAcc}>
OPTION : ${parseFloat(item.price5).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
)}
{item.shippingCharge && parseFloat(item.shippingCharge) > 0 && (
<span className={css.shippingAcc}>
S&H: ${parseFloat(item.shippingCharge).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
)}
</div>
</div>
<div className={css.eaBox}>
<TButton
className={classNames(
css.minusBox,
item.prodQty === 1 ? css.dimm : ""
)}
size="cartEa"
onClick={() => handleDecreseClick(item.prodSno, item.prodQty)}
spotlightId={"pd_ea_decrese"}
spotlightDisabled={item.prodQty === 1}
/>
<div className={css.ea}>{item.prodQty}</div>
<TButton
onClick={() => handleIncreseClick(item.prodSno, item.prodQty)}
className={css.plusBox}
spotlightId={"pd_ea_increse"}
size="cartEa"
/>
</div>
)}
{item.price5 && parseFloat(item.price5) > 0 && (
<span className={css.optionAcc}>
OPTION : ${parseFloat(item.price5).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
)}
{item.shippingCharge && parseFloat(item.shippingCharge) > 0 && (
<span className={css.shippingAcc}>
S&H: ${parseFloat(item.shippingCharge).toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}
</span>
)}
</div>
</div>
</div>
<div className={css.rightBox}>
<TButton
className={css.trashImg}
size="cartTrash"
onClick={() => handleDeleteClick(item.prodSno)}
/>
<div className={css.eaBox}>
<TButton
className={classNames(
css.minusBox,
item.prodQty === 1 ? css.dimm : ""
)}
size="cartEa"
onClick={() => handleDecreseClick(item.prodSno, item.prodQty)}
spotlightId={"pd_ea_decrese"}
spotlightDisabled={item.prodQty === 1}
/>
<div className={css.ea}>{item.prodQty}</div>
<TButton
onClick={() => handleIncreseClick(item.prodSno, item.prodQty)}
className={css.plusBox}
spotlightId={"pd_ea_increse"}
size="cartEa"
/>
</div>
</div>
</div>
);
})}
</div>
<div className={css.rightBox}>
<TButton
className={css.trashImg}
size="cartTrash"
onClick={() => handleDeleteClick(item.prodSno)}
/>
</div>
</div>
</Container>
);
})}
</div>
</Container>
);
})}
</>

View File

@@ -6,11 +6,14 @@ import SpotlightContainerDecorator
import CartProduct from './CartProduct';
import css from './CartProductBar.module.less';
const CartProductBar = ({ cartInfo, getScrollTo, scrollTop ,scrollOptions = {} }) => {
const Container = SpotlightContainerDecorator("div");
const CartProductBar = ({ cartInfo, getScrollTo, scrollTop ,scrollOptions = {} }) => {
const Container = SpotlightContainerDecorator({
enterTo: "last-focused"
},"div");
return (
<Container className={css.productContainer}>
<CartProduct cartInfo={cartInfo} getScrollTo={getScrollTo} scrollTop={scrollTop}/>
<CartProduct cartInfo={cartInfo} getScrollTo={getScrollTo} scrollTop={scrollTop}/>
</Container>
);
};