[checkoutpanel] 결제 관련 api 추가 및 action, reducer 추가 / UI 작업

This commit is contained in:
hyunwoo93.cha
2024-05-08 10:39:40 +09:00
parent 5c05c78fcd
commit bf00d9bfac
24 changed files with 1061 additions and 345 deletions

View File

@@ -124,6 +124,10 @@ export const types = {
// checkout actions
GET_CHECKOUT_INFO: "GET_CHECKOUT_INFO",
INSERT_MY_INFO_CHECKOUT_ORDER: "INSERT_MY_INFO_CHECKOUT_ORDER",
GET_TAX_INFOS: "GET_TAX_INFOS",
UPDATE_SELECTED_SHIPPING_ADDR: "UPDATE_SELECTED_SHIPPING_ADDR",
UPDATE_SELECTED_BILLING_ADDR: "UPDATE_SELECTED_BILLING_ADDR",
// order actions
SET_PURCHASE_TERMS_AGREE: "SET_PURCHASE_TERMS_AGREE",

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';
// 회원 체크아웃 정보 조회 IF-LGSP-345
export const getMyInfoCheckoutInfo = (props) => (dispatch, getState) => {
@@ -30,3 +30,98 @@ export const getMyInfoCheckoutInfo = (props) => (dispatch, getState) => {
onFail
);
};
// 회원 CheckOut 상품 주문 IF-LGSP-346
export const insertMyInfoCheckoutOrder = (props) => (dispatch, getState) => {
const { mbrNo, bilAddrSno, dlvrAddrSno, pinCd, orderProductCoup, ontUse } =
props;
const onSuccess = (response) => {
console.log("insertMyInfoCheckoutOrder onSuccess: ", response.data);
dispatch({
type: types.INSERT_MY_INFO_CHECKOUT_ORDER,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("insertMyInfoCheckoutOrder onFail: ", error);
};
TAxios(
dispatch,
getState,
"post",
URLS.INSERT_MY_INFO_CHECKOUT_ORDER,
{},
{ mbrNo, bilAddrSno, dlvrAddrSno, pinCd, orderProductCoup, ontUse },
onSuccess,
onFail
);
};
// 파트너사 배송비 및 LBP 관련 세금 정보 IF-LGSP-362
export const getTaxInfos = (props) => (dispatch, getState) => {
const {
mbrNo,
bilAddrSno,
dlvrAddrSno,
reqCheckoutTaxInfoItemList,
patnrId,
prdtId,
prodQty,
prodPrc,
taxCd,
frgTaxCd,
dcAmt,
cpnSno,
} = props;
const onSuccess = (response) => {
console.log("getTaxInfos onSuccess: ", response.data);
dispatch({
type: types.GET_TAX_INFOS,
payload: response.data.data,
});
};
const onFail = (error) => {
console.error("getTaxInfos onFail: ", error);
};
TAxios(
dispatch,
getState,
"post",
URLS.GET_TAX_INFOS,
{},
{
mbrNo,
bilAddrSno,
dlvrAddrSno,
reqCheckoutTaxInfoItemList,
patnrId,
prdtId,
prodQty,
prodPrc,
taxCd,
frgTaxCd,
dcAmt,
cpnSno,
},
onSuccess,
onFail
);
};
export const updateSelectedShippingAddr = (dlvrAddrSno) => ({
type: types.UPDATE_SELECTED_SHIPPING_ADDR,
payload: dlvrAddrSno,
});
export const updateSelectedBillingAddr = (bilAddrSno) => ({
type: types.UPDATE_SELECTED_BILLING_ADDR,
payload: bilAddrSno,
});

View File

@@ -1,7 +1,9 @@
import { types } from "../actions/actionTypes";
import { types } from '../actions/actionTypes';
const initialState = {
checkoutData: {},
taxInfosData: {},
infoForCheckoutData: {},
};
export const checkoutReducer = (state = initialState, action) => {
@@ -12,6 +14,30 @@ export const checkoutReducer = (state = initialState, action) => {
checkoutData: action.payload,
};
case types.GET_TAX_INFOS:
return {
...state,
taxInfosData: action.payload,
};
case types.UPDATE_SELECTED_SHIPPING_ADDR:
return {
...state,
infoForCheckoutData: {
...state.infoForCheckoutData,
dlvrAddrSno: action.payload,
},
};
case types.UPDATE_SELECTED_BILLING_ADDR:
return {
...state,
infoForCheckoutData: {
...state.infoForCheckoutData,
bilAddrSno: action.payload,
},
};
default:
return state;
}

View File

@@ -1,30 +1,53 @@
import React, { useCallback, useEffect } from "react";
import React, {
useCallback,
useEffect,
useState,
} from 'react';
import { useDispatch, useSelector } from "react-redux";
import {
useDispatch,
useSelector,
} from 'react-redux';
import { getMyInfoCheckoutInfo } from "../../actions/checkoutActions";
import { popPanel } from "../../actions/panelActions";
import TBody from "../../components/TBody/TBody";
import THeader from "../../components/THeader/THeader";
import TPanel from "../../components/TPanel/TPanel";
import TScroller from "../../components/TScroller/TScroller";
import css from "./CheckOutPanel.module.less";
import CheckoutQRCode from "./components/CheckoutQRCode";
import CheckOutTerms from "./components/CheckOutTerms";
import PinCode from "./components/PinCode";
import InformationContainer from "./container/InformationContainer";
import SummaryContainer from "./container/SummaryCotainer";
import {
getMyInfoCheckoutInfo,
getTaxInfos,
} from '../../actions/checkoutActions';
import { popPanel } from '../../actions/panelActions';
import TBody from '../../components/TBody/TBody';
import THeader from '../../components/THeader/THeader';
import TPanel from '../../components/TPanel/TPanel';
import TScroller from '../../components/TScroller/TScroller';
import css from './CheckOutPanel.module.less';
import CheckoutQRCode from './components/CheckoutQRCode';
import CheckOutTerms from './components/CheckOutTerms';
import PinCode from './components/PinCode';
import FixedSideBar from './container/FixedSideBar';
import InformationContainer from './container/InformationContainer';
import OrderItemsSideBar from './container/OrderItemsSideBar';
import SummaryContainer from './container/SummaryCotainer';
export default function CheckOutPanel() {
const dispatch = useDispatch();
const panels = useSelector((state) => state.panels.panels);
const { userInfo } = useSelector(
(state) => state.common.appStatus.loginUserData
);
const panels = useSelector((state) => state.panels.panels);
const checkoutData = useSelector((state) => state.checkout?.checkoutData);
const testData = useSelector((state) => state);
const checkoutPanelInfo = panels.find(
(panel) => panel.name === "checkoutpanel"
)?.panelInfo;
const productData = useSelector(
(state) => state.checkout?.checkoutData.productList
);
const infoForCheckoutData = useSelector(
(state) => state.checkout?.infoForCheckoutData
);
console.log("#panels", panels);
const [orderSideBarOpen, setOrderSideBarOpen] = useState(false);
const [offerSideBarOpen, setOfferSideBarOpen] = useState(false);
console.log("chw", infoForCheckoutData);
console.log("chw", productData);
useEffect(() => {
dispatch(
@@ -33,10 +56,32 @@ export default function CheckOutPanel() {
dirPurcSelYn: "Y",
cartList: [
{
patnrId: "11",
prdtId: "7280567",
prodOptCdCval: "7326490",
prodQty: 1,
patnrId: checkoutPanelInfo.cartList.patnrId,
prdtId: checkoutPanelInfo.cartList.prdtId,
prodOptCdCval: checkoutPanelInfo.cartList.prodOptCdCval,
prodQty: checkoutPanelInfo.cartList.prodQty,
},
],
})
);
}, [dispatch]);
useEffect(() => {
dispatch(
getTaxInfos({
mbrNo: userInfo,
bilAddrSno: infoForCheckoutData?.bilAddrSno || 206,
dlvrAddrSno: infoForCheckoutData?.dlvrAddrSno || 3003,
reqCheckoutTaxInfoItemList: [
{
cpnSno: null,
dcAmt: null,
frgtTaxCd: productData?.[0].frgtTaxCd || "FR020900",
patnrId: productData?.[0].patnrId || "11",
prdtId: productData?.[0].prdtId || "7127927",
prodPrc: productData?.[0].price3 || 9.99,
prodQty: productData?.[0].prodQty || 1,
taxCd: productData?.[0].taxCd || "P0000000",
},
],
})
@@ -46,10 +91,21 @@ export default function CheckOutPanel() {
const onBackClick = useCallback(() => {
dispatch(popPanel());
}, [dispatch]);
//{ name: panel_names.ON_SALE_PANEL }
const toggleOrderSideBar = useCallback(() => {
setOrderSideBarOpen((prev) => !prev);
}, [orderSideBarOpen]);
const toggleOfferSideBar = useCallback(() => {
setOfferSideBarOpen((prev) => !prev);
}, [offerSideBarOpen]);
return (
<>
<TPanel isTabActivated={false}>
<TPanel
isTabActivated={false}
spotlightDisabled={orderSideBarOpen || offerSideBarOpen}
>
<TBody className={css.tbody}>
<THeader
className={css.theader}
@@ -59,13 +115,19 @@ export default function CheckOutPanel() {
/>
<div className={css.Wrap}>
<SummaryContainer userInfo={userInfo} />
<InformationContainer checkoutData={checkoutData} />
<InformationContainer
toggleOrderSideBar={toggleOrderSideBar}
toggleOfferSideBar={toggleOfferSideBar}
/>
</div>
<CheckoutQRCode open={false} />
{/* <CheckoutQRCode open={false} /> */}
</TBody>
</TPanel>
{/* <CheckOutTerms /> 약관부분 */}
{/* <PinCode /> */}
{orderSideBarOpen && (
<OrderItemsSideBar closeSideBar={toggleOrderSideBar} />
)}
{offerSideBarOpen && <FixedSideBar closeSideBar={toggleOfferSideBar} />}
</>
);
}

View File

@@ -1,64 +1,116 @@
import React, { useCallback } from "react";
import React, {
memo,
useCallback,
useEffect,
useState,
} from 'react';
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../hooks/useScrollTo";
import BillingAddressItem from "../../MyPagePanel/MyPageSub/MyInfo/MyInfoTabContents/BillingAddressTab/BillingAddressItem";
import MyInfoNoResults from "../../MyPagePanel/MyPageSub/MyInfo/MyInfoTabContents/MyInfoNoResults/MyInfoNoResults";
import css from "./BillingAddressCard.module.less";
import CheckOutContainer from "./CheckOutContainer";
import { useDispatch } from 'react-redux';
export const ITEM_SIZE = {
itemWidth: 444,
itemHeight: 348,
spacing: 18,
};
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import { updateSelectedBillingAddr } from '../../../actions/checkoutActions';
import TCheckBox from '../../../components/TCheckBox/TCheckBox';
import TScroller from '../../../components/TScroller/TScroller';
import css from './BillingAddressCard.module.less';
import CheckOutContainer from './CheckOutContainer';
const ItemContainer = SpotlightContainerDecorator("li");
export default memo(function BillingAddressCard({ list, ...rest }) {
const dispatch = useDispatch();
const [selected, setSelected] = useState(null);
useEffect(() => {
if (list) {
setSelected(list[0].bilAddrSno);
dispatch(updateSelectedBillingAddr(list[0]?.bilAddrSno));
}
}, [dispatch, list]);
const handleSelectAddress = useCallback(
(bilAddrSno) => {
if (bilAddrSno !== selected) {
setSelected(bilAddrSno);
dispatch(updateSelectedBillingAddr(bilAddrSno));
}
},
[dispatch, selected]
);
export default function BillingAddressCard({ list }) {
const { getScrollTo, scrollLeft } = useScrollTo();
console.log("###list", list);
return (
<CheckOutContainer>
<div className={css.billingBox}>
{list &&
list.map(
(
{
bilAddrSno,
bilCityNm,
bilCtpt,
bilDtlAddr,
bilEmalAddr,
bilOdrFnm,
bilOdrLnm,
bilStatNm,
bilStatPvc,
bilZpcd,
chgDt,
cntryCd,
cntryNm,
mbrNo,
regDt,
useFlag,
},
index
) => {
return (
<BillingAddressItem
key={index}
bilOdrFnm={bilOdrFnm}
bilOdrLnm={bilOdrLnm}
bilZpcd={bilZpcd}
bilStatNm={bilStatNm}
bilCityNm={bilCityNm}
bilDtlAddr={bilDtlAddr}
bilCtpt={bilCtpt}
bilEmalAddr={bilEmalAddr}
/>
);
}
)}
{/* <MyInfoNoResults type="BILLING ADDRESS" /> */}
</div>
<CheckOutContainer className={css.shippingBox}>
<TScroller
className={css.shipping}
direction="horizontal"
noScrollByWheel
>
<ul>
{list &&
list.map(
(
{
bilAddrSno,
bilCityNm,
bilCtpt,
bilDtlAddr,
bilEmalAddr,
bilOdrFnm,
bilOdrLnm,
bilStatNm,
bilStatPvc,
bilZpcd,
chgDt,
cntryCd,
cntryNm,
mbrNo,
regDt,
useFlag,
},
index
) => {
return (
<ItemContainer
className={css.itemContainer}
key={index}
{...rest}
>
<TCheckBox
className={css.checkBox}
selected={selected === bilAddrSno}
onToggle={() => handleSelectAddress(bilAddrSno)}
/>
<h2 className={css.title}>
{bilOdrFnm} {bilOdrLnm}
</h2>
<div className={css.addressWrap}>
<p>
{bilZpcd} {bilStatNm}, <br />
{bilCityNm} <br />
{bilDtlAddr}
</p>
</div>
<div className={css.cardFooter}>
<p className={css.callNumber}>
<span />
{bilCtpt.replace(
/^(\d{2,3})(\d{3,4})(\d{4})$/,
`$1-$2-$3`
)}
</p>
<p className={css.email}>
<span />
{bilEmalAddr}
</p>
</div>
</ItemContainer>
);
}
)}
</ul>
</TScroller>
</CheckOutContainer>
);
}
});

View File

@@ -1,6 +1,88 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.billingBox {
.shippingBox {
position: relative;
.size(@w: 100%, @h:100%);
min-height: 383px;
> div:nth-child(1) {
.size(@w: inherit, @h: inherit);
}
ul {
display: flex;
align-items: center;
height: inherit;
.itemContainer {
.size(@w: 444px, @h: 348px);
background-color: @BG_COLOR_05;
border-radius: 12px;
padding: 32px 30px;
position: relative;
margin-right: 12px;
.checkBox {
position: absolute;
right: 20px;
top: 20px;
}
.title {
color: @COLOR_GRAY06;
font-weight: bold;
font-size: 36px;
margin-bottom: 19px;
}
.addressWrap {
color: @COLOR_GRAY03;
line-height: 1.33;
margin-bottom: 37px;
font-size: 24px;
}
.cardFooter {
font-weight: bold;
font-size: 24px;
color: @COLOR_GRAY03;
.callNumber {
padding-left: 42px;
margin-bottom: 6px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-call.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
position: absolute;
left: 30px;
}
}
.email {
padding-left: 42px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-mail-36.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
position: absolute;
left: 30px;
}
}
}
&:focus {
&::after {
.focused(@boxShadow: 22px, @borderRadius: 12px);
}
}
}
}
}

View File

@@ -1,18 +1,21 @@
import React from "react";
import React from 'react';
import classNames from "classnames";
import classNames from 'classnames';
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import css from "./CheckOutContainer.module.less";
import css from './CheckOutContainer.module.less';
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
"div"
);
export default function CheckOutContainer({ children }) {
export default function CheckOutContainer({ children, className }) {
return (
<Container className={classNames(css.chekcoutBox)}>{children}</Container>
<Container className={classNames(css.chekcoutBox, className)}>
{children}
</Container>
);
}

View File

@@ -0,0 +1,63 @@
import React, { memo } from 'react';
import Spottable from '@enact/spotlight/Spottable';
import CustomImage from '../../../components/CustomImage/CustomImage';
import { $L } from '../../../utils/helperMethods';
import css from './OrderItemCard.module.less';
const OrderItemContainer = Spottable("div");
export const SIZES = {
itemWidth: 660,
itemHeight: 333,
spacing: 12,
};
export default memo(function OrderItemCard({
imgUrls,
prdtNm,
prodQty,
price2,
price3,
prodtOpt,
patncLogPath,
prdtId,
currSign,
currSignLoc,
shippingCharge,
}) {
return (
<OrderItemContainer className={css.itemContainer}>
<div className={css.itemHeader}>
<CustomImage src={patncLogPath} alt="" className={css.patnrLogo} />
<h2 className={css.prdtId}>ID: {prdtId}</h2>
</div>
<div className={css.itemContents}>
<div className={css.leftContents}>
<CustomImage
src={imgUrls[0].imgUrl}
alt=""
className={css.itemImage}
/>
</div>
<div className={css.rightContents}>
<p className={css.prdtNm}>{prdtNm}</p>
<p className={css.options}>Silver Metal / XL</p>
<p className={css.prodQty}>Qty: {prodQty}</p>
<p className={css.priceWrap}>
<span className={css.itemPrice}>
{currSignLoc === "L"
? `${currSign} ${price3}`
: `${price2 || price3} ${currSign}`}
</span>
{`${$L("S&H")}: `}
{currSignLoc === "L"
? `${currSign} ${shippingCharge || 0.0}`
: `${shippingCharge || 0.0} ${currSign}`}
</p>
</div>
</div>
</OrderItemContainer>
);
});

View File

@@ -0,0 +1,110 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.itemContainer {
.size(@w: 660px, @h: 333px);
position: relative;
border-radius: 12px;
overflow: hidden;
background-color: @COLOR_WHITE;
padding: 20px 0;
.itemHeader {
padding: 0 30px;
display: flex;
align-items: center;
padding-bottom: 20px;
border-bottom: 1px solid #dadada;
.patnrLogo {
.size(@w: 42px, @h: 42px);
border: 1px solid #dadada;
border-radius: 50%;
margin-right: 12px;
}
.prdtId {
.font(@fontFamily: @baseFont, @fontSize: 30px);
color: #767676;
}
}
.itemContents {
display: flex;
padding-top: 20px;
padding-left: 30px;
padding-right: 30px;
.leftContents {
.size(@w: 200px, @h: 200px);
border: 1px solid #f0f0f0;
margin-right: 20px;
.itemImage {
.size(@w: 100%, @h: 100%);
object-fit: cover;
}
}
.rightContents {
.size(@w: calc(100% - 200px), @h: 200px);
.prdtNm {
.font(@fontFamily: @baseFont, @fontSize: 24px);
.elip(@clamp: 2);
line-height: 1.33;
color: #333;
font-weight: bold;
margin-bottom: 16px;
}
.options {
font-size: 24px;
line-height: 1.33;
color: #808080;
}
.prodQty {
font-size: 24px;
line-height: 1.33;
color: #808080;
margin-bottom: 12px;
}
.priceWrap {
display: flex;
align-items: center;
.font(@fontFamily: @baseFont, @fontSize: 24px);
color: @COLOR_GRAY03;
line-height: 1.33;
.itemPrice {
.font(@fontFamily: @baseFont, @fontSize: 30px);
font-weight: bold;
color: @PRIMARY_COLOR_RED;
line-height: 1.27;
padding-right: 16px;
margin-right: 16px;
position: relative;
&:after {
content: "";
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
width: 2px;
height: 18px;
background-color: @COLOR_GRAY03;
}
}
}
}
}
&:focus {
&::after {
.focused(@boxShadow:22px, @borderRadius: 12px);
}
}
}

View File

@@ -1,61 +1,108 @@
import React, { memo } from "react";
import React, {
memo,
useCallback,
useEffect,
useState,
} from 'react';
import Spottable from "@enact/spotlight/Spottable";
import { useDispatch } from 'react-redux';
import TScroller from "../../../components/TScroller/TScroller";
import CheckOutContainer from "./CheckOutContainer";
import css from "./ShippingAddressCard.module.less";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
const ItemContainer = Spottable("div");
import { updateSelectedShippingAddr } from '../../../actions/checkoutActions';
import TCheckBox from '../../../components/TCheckBox/TCheckBox';
import TScroller from '../../../components/TScroller/TScroller';
import CheckOutContainer from './CheckOutContainer';
import css from './ShippingAddressCard.module.less';
const ItemContainer = SpotlightContainerDecorator("li");
export default memo(function ShippingAddressCard({ list, ...rest }) {
const dispatch = useDispatch();
const [selected, setSelected] = useState(null);
useEffect(() => {
if (list) {
setSelected(list[0].dlvrAddrSno);
dispatch(updateSelectedShippingAddr(list[0]?.dlvrAddrSno));
}
}, [dispatch, list]);
const handleSelectAddress = useCallback(
(dlvrAddrSno) => {
if (dlvrAddrSno !== selected) {
setSelected(dlvrAddrSno);
dispatch(updateSelectedShippingAddr(dlvrAddrSno));
}
},
[dispatch, selected]
);
export default memo(function ShippingAddressCard({ list }) {
return (
<CheckOutContainer className={css.shippingBox}>
<TScroller className={css.shipping}>
{list &&
list.map(
(
{
dlvrOdrFnm,
dlvrOdrLnm,
dlvrZpcd,
dlvrStatNm,
dlvrCityNm,
dlvrDtlAddr,
dlvrCtpt,
dlvrEmalAddr,
},
index
) => {
return (
<ItemContainer className={css.itemContainer} key={index}>
<h2 className={css.title}>
{dlvrOdrFnm} {dlvrOdrLnm}
</h2>
<div className={css.addressWrap}>
<p>
{dlvrZpcd} {dlvrStatNm}, <br />
{dlvrCityNm} <br />
{dlvrDtlAddr}
</p>
</div>
<div className={css.cardFooter}>
<p className={css.callNumber}>
<span />
{dlvrCtpt?.replace(
/^(\d{2,3})(\d{3,4})(\d{4})$/,
`$1-$2-$3`
)}
</p>
<p className={css.email}>
<span />
{dlvrEmalAddr}
</p>
</div>
</ItemContainer>
);
}
)}
<TScroller
className={css.shipping}
direction="horizontal"
noScrollByWheel
>
<ul>
{list &&
list.map(
(
{
dlvrOdrFnm,
dlvrOdrLnm,
dlvrZpcd,
dlvrStatNm,
dlvrCityNm,
dlvrDtlAddr,
dlvrCtpt,
dlvrEmalAddr,
dlvrAddrSno,
},
index
) => {
return (
<ItemContainer
className={css.itemContainer}
key={index}
{...rest}
>
<TCheckBox
className={css.checkBox}
selected={selected === dlvrAddrSno}
onToggle={() => handleSelectAddress(dlvrAddrSno)}
/>
<h2 className={css.title}>
{dlvrOdrFnm} {dlvrOdrLnm}
</h2>
<div className={css.addressWrap}>
<p>
{dlvrZpcd} {dlvrStatNm}, <br />
{dlvrCityNm} <br />
{dlvrDtlAddr}
</p>
</div>
<div className={css.cardFooter}>
<p className={css.callNumber}>
<span />
{dlvrCtpt?.replace(
/^(\d{2,3})(\d{3,4})(\d{4})$/,
`$1-$2-$3`
)}
</p>
<p className={css.email}>
<span />
{dlvrEmalAddr}
</p>
</div>
</ItemContainer>
);
}
)}
</ul>
</TScroller>
</CheckOutContainer>
);

View File

@@ -2,68 +2,94 @@
@import "../../../style/utils.module.less";
.shippingBox {
min-height: 383px;
}
.itemContainer {
.size(@w: 444px, @h: 348px);
background-color: @BG_COLOR_05;
border-radius: 12px;
padding: 32px 30px;
position: relative;
.size(@w: 100%, @h:100%);
min-height: 383px;
.title {
color: @COLOR_GRAY06;
font-weight: bold;
font-size: 36px;
margin-bottom: 19px;
> div:nth-child(1) {
.size(@w: inherit, @h: inherit);
}
.addressWrap {
color: @COLOR_GRAY03;
line-height: 1.33;
margin-bottom: 37px;
font-size: 24px;
}
ul {
display: flex;
align-items: center;
height: inherit;
.cardFooter {
font-weight: bold;
font-size: 24px;
color: @COLOR_GRAY03;
.itemContainer {
.size(@w: 444px, @h: 348px);
background-color: @BG_COLOR_05;
border-radius: 12px;
padding: 32px 30px;
position: relative;
flex: none;
margin-right: 12px;
.callNumber {
padding-left: 42px;
margin-bottom: 6px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-call.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
.checkBox {
position: absolute;
left: 30px;
right: 20px;
top: 20px;
&.focus {
&::after {
.focused(@boxShadow:22px, @borderRadius: 12px);
}
}
}
}
.email {
padding-left: 42px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-mail-36.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
position: absolute;
left: 30px;
.title {
color: @COLOR_GRAY06;
font-weight: bold;
font-size: 36px;
margin-bottom: 19px;
}
}
}
&:focus {
&::after {
.focused(@boxShadow: 22px, @borderRadius: 12px);
.addressWrap {
color: @COLOR_GRAY03;
line-height: 1.33;
margin-bottom: 37px;
font-size: 24px;
}
.cardFooter {
font-weight: bold;
font-size: 24px;
color: @COLOR_GRAY03;
.callNumber {
padding-left: 42px;
margin-bottom: 6px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-call.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
position: absolute;
left: 30px;
}
}
.email {
padding-left: 42px;
> span {
background-image: url("../../../../assets/images/icons/ic-gr-mail-36.svg");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
.size(@w: 36px, @h: 36px);
position: absolute;
left: 30px;
}
}
}
&:focus {
&::after {
.focused(@boxShadow: 22px, @borderRadius: 12px);
}
}
}
}
}

View File

@@ -1,56 +1,31 @@
import React, { useEffect, useState } from "react";
import React, {
useEffect,
useState,
} from 'react';
import classNames from "classnames";
import classNames from 'classnames';
import Spottable from "@enact/spotlight/Spottable";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import noCouponImg from "../../../../assets/images/img-checkout-coupon@3x.png";
import TCheckBox from "../../../components/TCheckBox/TCheckBox";
import css from "./FixedSideBar.module.less";
import noCouponImg from '../../../../assets/images/img-checkout-coupon@3x.png';
import TCheckBox from '../../../components/TCheckBox/TCheckBox';
import TIconButton from '../../../components/TIconButton/TIconButton';
import { $L } from '../../../utils/helperMethods';
import css from './FixedSideBar.module.less';
const SpottableComponent = Spottable("div");
const SpottableSmall = Spottable("div");
const SideBarContainer = SpotlightContainerDecorator("div");
export default function FixedSideBar({ list, open }) {
const [display, setDisplay] = useState(open);
useEffect(() => {
if (open) {
setDisplay(true);
} else {
setDisplay(false);
}
}, [open]);
const clickHandler = () => {
setDisplay(false);
};
export default function FixedSideBar({ closeSideBar }) {
return (
<>
<div
className={classNames(
css.fixedBg,
display ? css.displayOn : css.displayOff
)}
></div>
<div
className={classNames(
css.fixedSideBar,
display ? css.displayOn : css.displayOff
)}
>
{/* 기본 */}
{/*
<div className={classNames(css.bgLayer)} onClick={closeSideBar} />
<SideBarContainer className={classNames(css.fixedSideBar)}>
<div className={css.sideMainTitle}>
<span />
ORDER ITEM
<TIconButton className={css.backButton} onClick={closeSideBar} />
<h2 className={css.title}>{$L("OFFERS & PROMOTION")}</h2>
</div>
<div className={css.sideLine} />
*/}
{/* 추가 */}
<div className={css.sideMainTitle} onClick={clickHandler}>
<span />
OFFERS & PROMOTION
</div>
<div className={css.sideLine} />
<div className={css.sideCoupon}>
<div className={css.right}>COUPON</div>
<div className={css.left}>
@@ -58,7 +33,6 @@ export default function FixedSideBar({ list, open }) {
<TCheckBox onToggle={true} />
</div>
</div>
<div className={css.sideLine} />
<div className={classNames(css.sideItemList, css.hasCoupon)}>
<div className={css.sideItemTitle}>ITEM COUPON</div>
@@ -118,7 +92,7 @@ export default function FixedSideBar({ list, open }) {
})}
*/}
</div>
</div>
</SideBarContainer>
</>
);
}

View File

@@ -1,6 +1,7 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.fixedBg {
.bgLayer {
position: fixed;
top: 0;
left: 0;
@@ -8,20 +9,9 @@
height: 100%;
background: rgba(0, 0, 0, 0.3);
z-index: 11;
&.displayOn {
display: block;
}
&.displayOff {
display: none;
}
}
.fixedSideBar {
&.displayOn {
display: block;
}
&.displayOff {
display: none;
}
position: fixed;
right: 0;
top: 0;
@@ -29,32 +19,38 @@
height: 100%;
background: #2c343f;
z-index: 12;
.sideMainTitle {
margin: 60px 13px 30px 19px;
font-size: 42px;
color: #f8f8f8;
height: 48px;
line-height: 48px;
display: flex;
flex-wrap: wrap;
> span {
padding: 60px 30px 18px;
border-bottom: 1px solid #d9d9d9;
.backButton {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-left-nor.svg");
width: 48px;
height: 48px;
background-size: contain;
object-fit: cover;
background-repeat: no-repeat;
&:focus {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-left-foc.svg");
}
}
.title {
.font(@fontFamily: @arialFontBold, @fontSize: 42px);
line-height: 1.14;
color: @BG_COLOR_01;
}
}
.sideLine {
border-bottom: 1px solid #d9d9d9;
}
.sideCoupon {
padding: 30px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
border-bottom: 1px solid #d9d9d9;
.right {
height: 42px;
font-size: 30px;

View File

@@ -1,16 +1,27 @@
import React, { useCallback, useState } from "react";
import React, {
useCallback,
useState,
} from 'react';
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import {
useDispatch,
useSelector,
} from 'react-redux';
import TButton from "../../../components/TButton/TButton";
import { $L } from "../../../utils/helperMethods";
import BillingAddressCard from "../components/BillingAddressCard";
import PaymentCard from "../components/PaymentCard";
import ShippingAddressCard from "../components/ShippingAddressCard";
import Subject from "../components/Subject";
import FixedSideBar from "./FixedSideBar.jsx";
import css from "./InformationContainer.module.less";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import { updateSelectedShippingAddr } from '../../../actions/checkoutActions';
import TButton from '../../../components/TButton/TButton';
import { $L } from '../../../utils/helperMethods';
import BillingAddressCard from '../components/BillingAddressCard';
import PaymentCard from '../components/PaymentCard';
import ShippingAddressCard from '../components/ShippingAddressCard';
import Subject from '../components/Subject';
import FixedSideBar from './FixedSideBar.jsx';
import css from './InformationContainer.module.less';
import OrderItemsSideBar from './OrderItemsSideBar';
const Container = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
@@ -18,25 +29,23 @@ const Container = SpotlightContainerDecorator(
);
const BtnSpot = Spottable("p");
export default function InformationContainer({ checkoutData }) {
console.log("###infomantion", checkoutData);
const [open, setOpen] = useState(false);
const handleClick = () => {
if (open === false) {
setOpen(true);
} else {
setOpen(false);
}
};
export default function InformationContainer({
toggleOfferSideBar,
toggleOrderSideBar,
}) {
const dispatch = useDispatch();
const checkoutData = useSelector((state) => state.checkout?.checkoutData);
return (
<>
<Container className={css.container}>
<Subject title="ORDER ITEMS" />
<div className={css.markBtn}>
<BtnSpot onClick={handleClick}>
<BtnSpot onClick={toggleOrderSideBar}>
{checkoutData.productList?.length} ITEMS
</BtnSpot>
<div className={css.markImg}></div>
</div>
<div className={css.listBox}>
<Subject title="SHIPPING ADDRESS" />
@@ -55,13 +64,11 @@ export default function InformationContainer({ checkoutData }) {
</div>
<div className={css.listBox}>
<Subject title="OFFERS & PROMOTION" />
<BtnSpot className={css.markBtn}>
<p>$12.60</p>
<div className={css.markImg}></div>
</BtnSpot>
<div className={css.markBtn}>
<BtnSpot onClick={toggleOfferSideBar}>$12.60</BtnSpot>
</div>
</div>
</Container>
<FixedSideBar list={checkoutData?.productList} open={open} />
</>
);
}

View File

@@ -12,49 +12,81 @@
right: 60px;
height: 48px;
display: flex;
> p {
font-size: 30px;
font-weight: bold;
color: #c70850;
line-height: 48px;
}
.markImg {
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-nor.svg");
background-size: contain;
}
&:focus {
.markImg {
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-foc.svg");
position: relative;
padding-right: 50px;
&:after {
position: absolute;
content: "";
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-nor.svg");
background-size: contain;
top: 0;
right: 0;
}
&:focus {
&:after {
position: absolute;
content: "";
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-foc.svg");
background-size: contain;
top: 0;
right: 0;
}
}
}
}
.listBox {
position: relative;
.markBtn {
position: absolute;
top: 24px;
right: 60px;
height: 48px;
display: flex;
> p {
font-size: 30px;
font-weight: bold;
color: #c70850;
color: @PRIMARY_COLOR_RED;
line-height: 48px;
}
.markImg {
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-nor.svg");
background-size: contain;
}
&:focus {
.markImg {
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-foc.svg");
position: relative;
padding-right: 50px;
&:after {
position: absolute;
content: "";
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-nor.svg");
background-size: contain;
top: 0;
right: 0;
}
&:focus {
&:after {
position: absolute;
content: "";
width: 48px;
height: 48px;
background-image: url("../../../../assets/images/btn/btn-bk-arrow-right-foc.svg");
background-size: contain;
top: 0;
right: 0;
}
}
}
}

View File

@@ -0,0 +1,73 @@
import React, { useCallback } from 'react';
import { useSelector } from 'react-redux';
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import TIConButton from '../../../components/TIconButton/TIconButton';
import TVirtualGridList
from '../../../components/TVirtualGridList/TVirtualGridList';
import { $L } from '../../../utils/helperMethods';
import OrderItemCard, { SIZES } from '../components/OrderItemCard';
import css from './OrderItemsSideBar.module.less';
const SideBarContainer = SpotlightContainerDecorator("div");
export default function OrderItemsSideBar({ closeSideBar }) {
const orderItemList = useSelector(
(state) => state.checkout?.checkoutData?.productList
);
console.log("chw", orderItemList);
const renderItem = useCallback(
({ index, ...rest }) => {
const item = orderItemList[index];
return (
<OrderItemCard
{...rest}
key={index}
imgUrls={item.imgUrls}
prdtNm={item.prdtNm}
prodQty={item.prodQty}
price2={item.price2}
price3={item.price3}
prodOpt={item.prodOpt}
patncLogPath={item.patncLogPath}
prdtId={item.prdtId}
currSign={item.currSign}
currSignLoc={item.currSignLoc}
shippingCharge={item.shippingCharge}
/>
);
},
[orderItemList]
);
return (
<>
<div className={css.bgLayer} onClick={closeSideBar} />
<SideBarContainer className={css.container}>
<div className={css.titleBox}>
<TIConButton className={css.backButton} onClick={closeSideBar} />
<h2 className={css.title}>{$L("ORDER ITEMS")}</h2>
</div>
<div className={css.orderItemListsWrap}>
{orderItemList && orderItemList.length > 0 ? (
<TVirtualGridList
className={css.grid}
itemRenderer={renderItem}
dataSize={orderItemList.length}
itemWidth={SIZES.itemWidth}
itemHeight={SIZES.itemHeight}
spacing={SIZES.spacing}
/>
) : null}
</div>
</SideBarContainer>
;
</>
);
}

View File

@@ -0,0 +1,55 @@
@import "../../../style/CommonStyle.module.less";
@import "../../../style/utils.module.less";
.bgLayer {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.3);
z-index: 11;
}
.container {
position: fixed;
right: 0;
top: 0;
width: 720px;
height: 100%;
background-color: #2c343f;
z-index: 12;
.titleBox {
display: flex;
padding: 60px 30px 18px;
border-bottom: 1px solid #d9d9d9;
.backButton {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-left-nor.svg");
width: 48px;
height: 48px;
object-fit: cover;
background-repeat: no-repeat;
&:focus {
background-image: url("../../../../assets/images/btn/btn-wh-arrow-left-foc.svg");
}
}
.title {
.font(@fontFamily: @arialFontBold, @fontSize: 42px);
line-height: 1.14;
color: @BG_COLOR_01;
}
}
.orderItemListsWrap {
.size(@w: 100%, @h: 100%);
margin-top: 30px;
.grid {
padding: 0 30px;
}
}
}

View File

@@ -5,7 +5,6 @@
.size(@w: 315px, @h: 265px);
position: relative;
border-radius: 12px;
position: relative;
overflow: hidden;
.itemContainer {

View File

@@ -1,8 +1,8 @@
import React, { memo } from "react";
import React, { memo } from 'react';
import Spottable from "@enact/spotlight/Spottable";
import Spottable from '@enact/spotlight/Spottable';
import css from "./BillingAddressItem.module.less";
import css from './BillingAddressItem.module.less';
const ItemContainer = Spottable("div");
@@ -15,9 +15,10 @@ export default memo(function BillingAddressItem({
bilDtlAddr,
bilCtpt,
bilEmalAddr,
...rest
}) {
return (
<ItemContainer className={css.itemContainer}>
<ItemContainer className={css.itemContainer} {...rest}>
<h2 className={css.title}>
{bilOdrFnm} {bilOdrLnm}
</h2>

View File

@@ -1,13 +1,14 @@
import React, { useCallback } from "react";
import React, { useCallback } from 'react';
import { useSelector } from "react-redux";
import { useSelector } from 'react-redux';
import TVirtualGridList from "../../../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../../../hooks/useScrollTo";
import MyInfoNoResults from "../MyInfoNoResults/MyInfoNoResults";
import MyInfoTabContentsContainer from "../MyInfoTabContentsContainer";
import BillingAddressItem from "./BillingAddressItem";
import css from "./BillingAddressTab.module.less";
import TVirtualGridList
from '../../../../../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../../../../../hooks/useScrollTo';
import MyInfoNoResults from '../MyInfoNoResults/MyInfoNoResults';
import MyInfoTabContentsContainer from '../MyInfoTabContentsContainer';
import BillingAddressItem from './BillingAddressItem';
import css from './BillingAddressTab.module.less';
export const ITEM_SIZE = {
itemWidth: 444,
@@ -44,6 +45,7 @@ export default function BillingAddressTab() {
return (
<BillingAddressItem
{...rest}
key={index}
bilOdrFnm={bilOdrFnm}
bilOdrLnm={bilOdrLnm}

View File

@@ -1,8 +1,8 @@
import React, { memo } from "react";
import React, { memo } from 'react';
import Spottable from "@enact/spotlight/Spottable";
import Spottable from '@enact/spotlight/Spottable';
import css from "./ShippingAddressItem.module.less";
import css from './ShippingAddressItem.module.less';
const ItemContainer = Spottable("div");
@@ -15,9 +15,10 @@ export default memo(function ShippingAddressItem({
dlvrDtlAddr,
dlvrCtpt,
dlvrEmalAddr,
...rest
}) {
return (
<ItemContainer className={css.itemContainer}>
<ItemContainer className={css.itemContainer} {...rest}>
<h2 className={css.title}>
{dlvrOdrFnm} {dlvrOdrLnm}
</h2>

View File

@@ -1,14 +1,15 @@
import React, { useCallback } from "react";
import React, { useCallback } from 'react';
import { useSelector } from "react-redux";
import { useSelector } from 'react-redux';
import TVirtualGridList from "../../../../../../components/TVirtualGridList/TVirtualGridList";
import useScrollTo from "../../../../../../hooks/useScrollTo";
import { ITEM_SIZE } from "../BillingAddressTab/BillingAddressTab";
import MyInfoNoResults from "../MyInfoNoResults/MyInfoNoResults";
import MyInfoTabContentsContainer from "../MyInfoTabContentsContainer";
import ShippingAddressItem from "./ShippingAddressItem";
import css from "./ShippingAddressTab.module.less";
import TVirtualGridList
from '../../../../../../components/TVirtualGridList/TVirtualGridList';
import useScrollTo from '../../../../../../hooks/useScrollTo';
import { ITEM_SIZE } from '../BillingAddressTab/BillingAddressTab';
import MyInfoNoResults from '../MyInfoNoResults/MyInfoNoResults';
import MyInfoTabContentsContainer from '../MyInfoTabContentsContainer';
import ShippingAddressItem from './ShippingAddressItem';
import css from './ShippingAddressTab.module.less';
export default function ShippingAddressTab() {
const shippingDatas = useSelector((state) => state.shipping.shippingData);
@@ -39,6 +40,7 @@ export default function ShippingAddressTab() {
return (
<ShippingAddressItem
{...rest}
key={index}
dlvrOdrFnm={dlvrOdrFnm}
dlvrOdrLnm={dlvrOdrLnm}