[장바구니] 수정#1

- cartAction 상품 옵션 받는부분이 없어 수정
 - tablayout 노출관련 cartpanel추가
 - cartpanel 가로값 1800으로 변경 및 노출 수정
 - buyoption에 add cart 버튼클릭시 buynow와 같은 작동하도록 수정
 - 상품 노출관련 수정(좀더 수정필요함)
 - 사이드바 수정 진행중

 - 상품이 추가되지않는부분에 대해서는 현재 백엔드에서 확인중입니다.
This commit is contained in:
junghoon86.park
2025-10-31 15:07:18 +09:00
parent 9378e75c0b
commit d0d33db004
10 changed files with 234 additions and 134 deletions

View File

@@ -1,6 +1,6 @@
import { URLS } from "../api/apiConfig";
import { TAxios } from "../api/TAxios";
import { types } from "./actionTypes";
import { URLS } from '../api/apiConfig';
import { TAxios } from '../api/TAxios';
import { types } from './actionTypes';
/**
* 회원 장바구니 정보 조회
@@ -55,7 +55,7 @@ export const getMyInfoCartSearch = (props) => (dispatch, getState) => {
* @param {Object} props - 장바구니 상품 정보
*/
export const addToCart = (props) => (dispatch, getState) => {
const { mbrNo, patnrId, prdtId, prodOptCdCval, prodQty } = props;
const { mbrNo, patnrId, prdtId, prodOptCdCval, prodQty, prdtOpt } = props;
const onSuccess = (response) => {
console.log("addToCart onSuccess: ", response.data);
@@ -78,7 +78,7 @@ export const addToCart = (props) => (dispatch, getState) => {
console.warn("ADD_TO_CART URL이 정의되지 않았습니다.");
dispatch({
type: types.ADD_TO_CART,
payload: { patnrId, prdtId, prodOptCdCval, prodQty },
payload: { patnrId, prdtId, prodOptCdCval, prodQty, prdtOpt },
});
return;
}
@@ -89,7 +89,7 @@ export const addToCart = (props) => (dispatch, getState) => {
"post",
URLS.ADD_TO_CART,
{},
{ mbrNo, patnrId, prdtId, prodOptCdCval, prodQty },
{ mbrNo, patnrId, prdtId, prodOptCdCval, prodQty, prdtOpt },
onSuccess,
onFail
);

View File

@@ -137,6 +137,7 @@ const PANELS_HAS_TAB = [
panel_names.ON_SALE_PANEL,
panel_names.SEARCH_PANEL,
panel_names.TRENDING_NOW_PANEL,
panel_names.CART_PANEL,
];
export default function TabLayout({ topPanelName, onTabActivated, panelInfo }) {

View File

@@ -21,7 +21,7 @@ import CartProductBar from './CartProductBar';
import CartSidebar from './CartSidebar';
export default function CartPanel({ spotlightId, scrollOptions = [] }) {
const cartData = useSelector((state) => state.cart.getMyinfoCartSearch);
const cartData = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
const { userNumber } = useSelector(
(state) => state.common.appStatus.loginUserData
);
@@ -38,10 +38,6 @@ export default function CartPanel({ spotlightId, scrollOptions = [] }) {
}
}, [dispatch, userNumber]);
useEffect(() => {
console.log("###cartData", cartData);
}, [cartData]);
const {
getScrollTo,
getScrollTo: getScrollToBody,
@@ -49,7 +45,7 @@ export default function CartPanel({ spotlightId, scrollOptions = [] }) {
} = useScrollTo();
return (
<TPanel spotlightId={spotlightId} isTabActivated={false}>
<TPanel spotlightId={spotlightId} isTabActivated={true}>
<TBody className={css.tbody}>
<THeader
className={css.theader}
@@ -60,18 +56,21 @@ export default function CartPanel({ spotlightId, scrollOptions = [] }) {
/>
<div className={css.Wrap}>
<div className={css.leftSection}>
<CartSidebar />
<CartSidebar cartInfo={cartData}/>
</div>
<div className={css.rightSection} cbScrollTo={getScrollToBody}>
{/* 오른쪽 상품 영역 */}
{/* <TScroller
className={css.tScroller}
scrollTopBody={scrollTopBody}
{...scrollOptions}
>
<CartProductBar />
</TScroller> */}
<CartEmpty />
{cartData && cartData?.length > 0 ? (
<TScroller
className={css.tScroller}
scrollTopBody={scrollTopBody}
{...scrollOptions}
>
<CartProductBar cartInfo={cartData} />
</TScroller>
) : (
<CartEmpty />
)}
</div>
</div>
</TBody>

View File

@@ -2,7 +2,7 @@
@import "../../style/utils.module.less";
.tbody {
.size(@w: 1920px, @h: 100%);
.size(@w: 1800px, @h: 100%);
.theader {
position: sticky;
left: 0;

View File

@@ -6,6 +6,7 @@ import React, {
} from 'react';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator
@@ -20,7 +21,55 @@ import css from './CartProduct.module.less';
const Container = SpotlightContainerDecorator("div");
const CartProduct = () => {
const randomCount = useMemo(() => Math.floor(Math.random() * 3) + 1, []);
const cartData = useSelector((state) => state.cart.getMyinfoCartSearch.cartInfo);
//카트 데이타 그룹화 - 수정된 부분
const groupedCartData = useMemo(() => {
if (!cartData || !Array.isArray(cartData)) return {};
return cartData.reduce((acc, item) => {
const groupKey = item.patncNm || item.patnrId || 'unknown';
if (!acc[groupKey]) {
// 객체 구조로 초기화 (수정됨)
acc[groupKey] = {
partnerInfo: {
id: item.patnrId,
name: item.patncNm,
logo: item.patncLogPath
},
items: []
};
}
acc[groupKey].items.push(item);
return acc;
}, {});
}, [cartData]);
useEffect(()=>{
console.log("###groupedCartData",groupedCartData);
},[groupedCartData])
// 파트너사별 총합 계산
const calculatePartnerTotal = (items) => {
const productTotal = items.reduce((sum, item) =>
sum + (parseFloat(item.price3 || item.price2 || 0) * item.prodQty), 0
);
const optionTotal = items.reduce((sum, item) =>
sum + (parseFloat(item.optPrc || 0) * item.prodQty), 0
);
const shippingTotal = items.reduce((sum, item) =>
sum + parseFloat(item.shippingCharge || 0), 0
);
return {
productTotal,
optionTotal,
shippingTotal,
total: productTotal + optionTotal + shippingTotal
};
};
const [pdEa, setPdEa] = useState(1);
@@ -29,6 +78,7 @@ const CartProduct = () => {
setPdEa(pdEa - 1);
}
}, [pdEa]);
const handleIncreseClick = useCallback(() => {
setPdEa(pdEa + 1);
}, [pdEa]);
@@ -38,97 +88,139 @@ const CartProduct = () => {
Spotlight.focus("pd_ea_increse");
}
}, [pdEa]);
return (
<Container className={css.productBox}>
<div className={css.productCompany}>
{/* 장바구니에 담긴 상품의 회사정보 */}
<div className={css.logo}>
<CustomImage
className={css.img}
src={logoImage}
fallbackSrc={defaultImage}
/>
</div>
<span className={css.company}>
Juvelirochka
<span className={css.productEa}>(1 ITEM)</span>
</span>
</div>
<div className={css.productPrice}>
{/* 그 회사에 장바구니에 담긴 상품의 총합 */}
<div className={css.leftSection}>
Product Total $ 99,999.99 + Option $ 99,999.99 + S&H $ 99,999.9
</div>
<div className={css.rightSection}>
Total
<span className={css.total}>
$<span className={css.totalAcc}>999,999,999</span>
</span>
</div>
</div>
{/* 상품 정보 반복가능 */}
{Array.from({ length: randomCount }, (_, index) => (
<div className={css.product}>
<div className={css.leftBox}>
<div className={css.checkBox}>
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId="productCheckbox"
/>
<span className={css.productId}>ID : A565145</span>
</div>
<div className={css.productInfo}>
<div className={css.leftSection}>
<CustomImage
className={css.productImage}
src={defaultImage}
fallbackSrc={defaultImage}
/>
</div>
<div className={css.rightSection}>
<div className={css.productNm}>
Farmer Jon`s 25-ct Mini Bags Of Microwave Popcorn in Flavor
Choice Area
<>
{Object.entries(groupedCartData).map(([partnerKey, group]) => {
const totals = calculatePartnerTotal(group.items);
return (
<Container className={css.productBox}>
<div key={partnerKey} className={css.partnerSection}>
{/* 파트너사 정보 - 한 번만 표시 */}
<div className={css.productCompany}>
<div className={css.logo}>
<CustomImage
className={css.img}
src={group.partnerInfo.logo}
fallbackSrc={defaultImage}
/>
</div>
<div className={css.optionNm}>Sliver Metallic / XL</div>
<div className={css.accountBox}>
<span className={css.account}>$38.09</span>
<div className={css.accountInfo}>
<span className={css.originalAcc}>$41.99</span>
<span className={css.optionAcc}>OPTION : $5.50</span>
<span className={css.shippingAcc}>S&H: $5.50</span>
<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.toFixed(2)} +
Option ${totals.optionTotal.toFixed(2)} +
S&H ${totals.shippingTotal.toFixed(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>
</span>
</div>
</div>
{/* 해당 파트너사의 상품 리스트 - map으로 반복 */}
{group.items.map((item) => (
<div key={item.prodSno} className={css.product}>
<div className={css.leftBox}>
<div className={css.checkBox}>
<TCheckBoxSquare
className={css.customeCheckbox}
spotlightId="productCheckbox"
/>
<span className={css.productId}>
ID : {item.prdtId}
</span>
</div>
<div className={css.productInfo}>
<div className={css.leftSection}>
<CustomImage
className={css.productImage}
src={item.imgUrl}
fallbackSrc={defaultImage}
/>
</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).toFixed(2)}
</span>
<div className={css.accountInfo}>
{item.price2 && (
<span className={css.originalAcc}>
${parseFloat(item.price2).toFixed(2)}
</span>
)}
{item.price5 && parseFloat(item.price5) > 0 && (
<span className={css.optionAcc}>
OPTION : ${parseFloat(item.price5).toFixed(2)}
</span>
)}
{item.shippingCharge && parseFloat(item.shippingCharge) > 0 && (
<span className={css.shippingAcc}>
S&H: ${parseFloat(item.shippingCharge).toFixed(2)}
</span>
)}
</div>
</div>
<div className={css.eaBox}>
<TButton
className={classNames(
css.minusBox,
item.prodQty === 1 ? css.dimm : ""
)}
size="cartEa"
onClick={() => handleDecreseClick(item.prodSno)}
spotlightId={"pd_ea_decrese"}
spotlightDisabled={item.prodQty === 1}
/>
<div className={css.ea}>{item.prodQty}</div>
<TButton
onClick={() => handleIncreseClick(item.prodSno)}
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>
<div className={css.eaBox}>
<TButton
className={classNames(
css.minusBox,
pdEa === 1 ? css.dimm : ""
)}
size="cartEa"
onClick={handleDecreseClick}
spotlightId={"pd_ea_decrese"}
spotlightDisabled={pdEa === 1 ? true : false}
/>
<div className={css.ea}>{pdEa}</div>
<TButton
onClick={handleIncreseClick}
className={css.plusBox}
spotlightId={"pd_ea_increse"}
size="cartEa"
/>
</div>
</div>
))}
</div>
</div>
<div className={css.rightBox}>
{/* 휴지통 */}
<TButton className={css.trashImg} size="cartTrash" />
</div>
</div>
))}
</Container>
</Container>
);
})}
</>
);
};
export default CartProduct;
export default CartProduct;

View File

@@ -34,7 +34,7 @@
}
}
.productPrice {
width: 1198px;
width: 1078px;
height: 81px;
padding: 20px 29px 20px 28px;
background-color: #f2f2f2;

View File

@@ -9,13 +9,14 @@ import css from './CartProductBar.module.less';
const CartProductBar = ({ scrollOptions = {} }) => {
const Container = SpotlightContainerDecorator("div");
const randomCount = useMemo(() => Math.floor(Math.random() * 3) + 1, []);
// const randomCount = useMemo(() => Math.floor(Math.random() * 3) + 1, []);
return (
<Container className={css.productContainer}>
{Array.from({ length: randomCount }, (_, index) => (
<CartProduct key={index} />
))}
<CartProduct />
{/* {Array.from({ length: randomCount }, (_, index) => ( */}
{/* <CartProduct key={index} /> */}
{/* ))} */}
</Container>
);
};

View File

@@ -3,7 +3,7 @@
.productContainer {
background-color: #f8f8f8;
width: 1320px;
width: 1200px;
height: 100%;
padding: 60px;
.tScroller {

View File

@@ -729,7 +729,8 @@ export default function ProductAllSection({
<TButton
spotlightId="detail-add-to-cart-button"
className={css.addToCartButton}
onClick={handleAddToCartClick}
// onClick={handleAddToCartClick}
onClick={handleBuyNowClick}
onSpotlightUp={handleSpotlightUpFromBuyButtons}
type="detail_small"
>

View File

@@ -462,21 +462,21 @@ const BuyOption = ({
const discountRate = priceInfo?.split('|')[4];
// 로그 전송
dispatch(
sendLogTotalRecommend({
nowMenu: nowMenu,
productId: prdtId,
productTitle: prdtNm,
partner: patncNm,
price: discountRate ? discountPrice : regularPrice,
discount: discountRate,
brand: brndNm,
productOption: prodOptCval || '',
category: catNm,
contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
messageId: Config.LOG_MESSAGE_ID.ADD_TO_CART,
})
);
// dispatch(
// sendLogTotalRecommend({
// nowMenu: nowMenu,
// productId: prdtId,
// productTitle: prdtNm,
// partner: patncNm,
// price: discountRate ? discountPrice : regularPrice,
// discount: discountRate,
// brand: brndNm,
// productOption: prodOptCval || '',
// category: catNm,
// contextName: Config.LOG_CONTEXT_NAME.DETAILPAGE,
// messageId: Config.LOG_MESSAGE_ID.ADD_TO_CART,
// })
// );
// 장바구니에 추가
dispatch(
@@ -484,9 +484,15 @@ const BuyOption = ({
mbrNo: userNumber,
patnrId: selectedPatnrId,
prdtId: selectedPrdtId,
prodOptCdCval: selectedOptions?.prodOptCdCval || null,
prodQty: quantity,
prodOptTpCdCval: productOptionInfos[0]?.prodOptTpCdCval,
prodQty: String(quantity),
prdtOpt: {
prodOptCdCval: selectedOptions?.prodOptCdCval
? selectedOptions.prodOptCdCval
: "",
prodOptCval:productOptionInfos[0]?.optNm,
prodOptSno: productOptionInfos[0]?.prodOptSno,
prodOptTpCdCval: productOptionInfos[0]?.prodOptTpCdCval,
}
})
);