[checkout] confirmPanel 추가, checkoutOrderAmtDummy api 추가, qr코드 주소 변경, 결제 에러케이스 추가 및 결제 완료 시 confirmPanel 이동

This commit is contained in:
hyunwoo93.cha
2024-05-30 21:26:58 +09:00
parent b16789887c
commit bfb6bf14dc
20 changed files with 519 additions and 285 deletions

View File

@@ -135,6 +135,8 @@ export const types = {
GET_TAX_INFOS: "GET_TAX_INFOS",
UPDATE_SELECTED_SHIPPING_ADDR: "UPDATE_SELECTED_SHIPPING_ADDR",
UPDATE_SELECTED_BILLING_ADDR: "UPDATE_SELECTED_BILLING_ADDR",
UPDATE_SELECTED_COUPON: "UPDATE_SELECTED_COUPON",
GET_CHECKOUT_TOTAL_AMT_DUMMY: "GET_CHECKOUT_TOTAL_AMT_DUMMY", // 추후 실 데이터 api로 교체 필요
CHECKOUT_DATA_RESET: "CHECKOUT_DATA_RESET",
// order actions

View File

@@ -33,35 +33,38 @@ export const getMyInfoCheckoutInfo = (props) => (dispatch, getState) => {
};
// 회원 CheckOut 상품 주문 IF-LGSP-346
export const insertMyInfoCheckoutOrder = (props) => (dispatch, getState) => {
const { mbrNo, bilAddrSno, dlvrAddrSno, pinCd, orderProductCoup, ontUse } =
props;
export const insertMyInfoCheckoutOrder =
(props, callback) => (dispatch, getState) => {
const { mbrNo, bilAddrSno, dlvrAddrSno, pinCd, orderProductCoupontUse } =
props;
const onSuccess = (response) => {
console.log("insertMyInfoCheckoutOrder onSuccess: ", response.data);
const onSuccess = (response) => {
console.log("insertMyInfoCheckoutOrder onSuccess: ", response.data);
dispatch({
type: types.INSERT_MY_INFO_CHECKOUT_ORDER,
payload: response.data.data,
});
dispatch({
type: types.INSERT_MY_INFO_CHECKOUT_ORDER,
payload: response.data.data,
});
if (callback) callback(response);
};
const onFail = (error) => {
console.error("insertMyInfoCheckoutOrder onFail: ", error);
};
TAxios(
dispatch,
getState,
"post",
URLS.INSERT_MY_INFO_CHECKOUT_ORDER,
{},
{ mbrNo, bilAddrSno, dlvrAddrSno, pinCd, orderProductCoupontUse },
onSuccess,
onFail
);
};
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 {
@@ -117,6 +120,45 @@ export const getTaxInfos = (props) => (dispatch, getState) => {
);
};
export const getCheckoutTotalAmtDummy = (params) => (dispatch, getState) => {
const {
mbrNo,
dirPurcSelYn,
bilAddrSno,
dlvrAddrSno,
orderProductCoupontUse,
} = params;
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } }));
const onSuccess = (response) => {
console.log("getCheckoutTotalAmtDummy onSuccess: ", response.data);
dispatch({
type: types.GET_CHECKOUT_TOTAL_AMT_DUMMY,
payload: response.data.data,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
console.error("getCheckoutTotalAmtDummy onFail: ", error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
"post",
URLS.GET_CHECKOUT_TOTAL_AMT_DUMMY,
{},
{ mbrNo, dirPurcSelYn, bilAddrSno, dlvrAddrSno, orderProductCoupontUse },
onSuccess,
onFail
);
};
export const updateSelectedShippingAddr = (dlvrAddrSno) => ({
type: types.UPDATE_SELECTED_SHIPPING_ADDR,
payload: dlvrAddrSno,
@@ -127,6 +169,11 @@ export const updateSelectedBillingAddr = (bilAddrSno) => ({
payload: bilAddrSno,
});
export const updateSelectedCoupon = (productId, coupon) => ({
type: types.UPDATE_SELECTED_COUPON,
payload: { productId, coupon },
});
export const resetCheckoutData = () => ({
type: types.CHECKOUT_DATA_RESET,
});

View File

@@ -4,35 +4,42 @@ import { types } from "./actionTypes";
import { changeAppStatus } from "./commonActions";
// 회원 등록카드 PIN CODE 입력 체크 IF-LGSP-336
export const getMyInfoCardPincodeCheck = (params) => (dispatch, getState) => {
const { mbrNo, pinCd } = params;
export const getMyInfoCardPincodeCheck =
(params, callback) => (dispatch, getState) => {
const { mbrNo, pinCd } = params;
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } }));
dispatch(
changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } })
);
const onSuccess = (response) => {
console.log("getMyInfoCardPincodeCheck onSuccess ", response);
const onSuccess = (response) => {
console.log("getMyInfoCardPincodeCheck onSuccess ", response);
dispatch({
type: types.GET_MY_INFO_CARD_PINCODE_CHECK,
payload: response.data,
});
dispatch({
type: types.GET_MY_INFO_CARD_PINCODE_CHECK,
payload: response.data,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
if (callback) {
callback(response.data);
}
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
console.error("getMyInfoCardPincodeCheck onFail ", error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_MY_INFO_CARD_PINCODE_CHECK,
{ mbrNo, pinCd },
{},
onSuccess,
onFail
);
};
const onFail = (error) => {
console.error("getMyInfoCardPincodeCheck onFail ", error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
"get",
URLS.GET_MY_INFO_CARD_PINCODE_CHECK,
{ mbrNo, pinCd },
{},
onSuccess,
onFail
);
};

View File

@@ -100,8 +100,9 @@ export const URLS = {
//checkout controller
GET_CHECKOUT_INFO: "/lgsp/v1/myinfo/checkout/info.lge",
INSERT_MY_INFO_CHECKIOUT_ORDER: "/lgsp/v1/myinfo/checkout/order.lge",
INSERT_MY_INFO_CHECKOUT_ORDER: "/lgsp/v1/myinfo/checkout/order.lge",
GET_TAX_INFOS: "/lgsp/v1/myinfo/checkout/taxInfo.lge",
GET_CHECKOUT_TOTAL_AMT_DUMMY: "/lgsp/v1/myinfo/checkout/totalAmtDummy.lge",
// app controller
SEND_SMS: "/lgsp/v1/app/sms.lge",

View File

@@ -5,6 +5,9 @@ const initialState = {
taxInfosData: {},
infoForCheckoutData: {},
taxAndSHData: {},
checkoutTotalData: {},
selectedCoupons: {},
checkoutOrderData: {},
};
export const checkoutReducer = (state = initialState, action) => {
@@ -21,6 +24,18 @@ export const checkoutReducer = (state = initialState, action) => {
taxInfosData: action.payload,
};
case types.GET_CHECKOUT_TOTAL_AMT_DUMMY:
return {
...state,
checkoutTotalData: action.payload,
};
case types.INSERT_MY_INFO_CHECKOUT_ORDER:
return {
...state,
checkoutOrderData: action.payload,
};
case types.UPDATE_SELECTED_SHIPPING_ADDR:
return {
...state,
@@ -39,6 +54,15 @@ export const checkoutReducer = (state = initialState, action) => {
},
};
case types.UPDATE_SELECTED_COUPON:
return {
...state,
selectedCoupons: {
...state.selectedCoupons,
[action.payload.productId]: action.payload.coupon,
},
};
case types.CHECKOUT_DATA_RESET:
return initialState;

View File

@@ -20,6 +20,7 @@ export const panel_names = {
CART_PANEL: "cartpanel",
FEATURED_BRANDS_PANEL: "featuredbrandspanel",
WELCOME_EVENT_PANEL: "welcomeeventpanel",
CONFIRM_PANEL: "confirmpanel",
DETAIL_PANEL: "detailpanel",
PLAYER_PANEL: "playerpanel",
CHECKOUT_PANEL: "checkoutpanel",

View File

@@ -6,6 +6,7 @@ import { Job } from "@enact/core/util";
import Spotlight from "@enact/spotlight";
import {
getCheckoutTotalAmtDummy,
getMyInfoCheckoutInfo,
getTaxInfos,
resetCheckoutData,
@@ -45,18 +46,37 @@ export default function CheckOutPanel() {
const infoForCheckoutData = useSelector(
(state) => state.checkout?.infoForCheckoutData
);
const cardInfo = useSelector(
(state) => state.checkout?.checkoutData.cardInfo
const selectedCoupons = useSelector(
(state) => state.checkout.selectedCoupons
);
const taxInfosData = useSelector((state) => state.checkout?.taxInfosData);
const [orderSideBarOpen, setOrderSideBarOpen] = useState(false);
const [offerSideBarOpen, setOfferSideBarOpen] = useState(false);
const [placeOrderPopup, setPlaceOrderPopup] = useState(false);
const [loading, setLoading] = useState(true);
const isMounted = useRef(true);
const { getScrollTo: getScrollToBody, scrollTop: scrollTopBody } =
useScrollTo();
const spotJob = useRef(new Job((func) => func(), 1000));
const spotJob = useRef(new Job((func) => func(), 0));
useEffect(() => {
isMounted.current = true;
dispatch(
changeAppStatus({ showLoadingPanel: { show: true, type: "wait" } })
);
if (!loading) {
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
}
return () => {
isMounted.current = false;
};
}, [loading, dispatch]);
useEffect(() => {
dispatch(getShoptimeTerms());
@@ -77,36 +97,64 @@ export default function CheckOutPanel() {
);
}, [dispatch, userNumber, checkoutPanelInfo]);
useEffect(() => {
if (infoForCheckoutData && productData) {
setLoading(false);
const orderProductCoupontUse = Object.keys(selectedCoupons).map(
(productId) => {
const {
cpnAplyMaxDcAmt,
cpnAplyMinProdAmt,
cpnAplyMinPurcAmt,
cpnCdCval,
cpnCdSeq,
cpnDctrt,
cpnSno,
dcAmt,
mbrNo,
prdtId,
shptmCpnTpCd,
shptmCpnTrgtCd,
shptmDcTpCd,
} = selectedCoupons[productId];
return {
cpnAplyMaxDcAmt,
cpnAplyMinProdAmt,
cpnAplyMinPurcAmt,
cpnCdCval,
cpnCdSeq,
cpnDctrt,
cpnSno,
dcAmt,
mbrNo,
prdtId,
shptmCpnTpCd,
shptmCpnTrgtCd,
shptmDcTpCd,
};
}
);
dispatch(
getCheckoutTotalAmtDummy({
mbrNo: userNumber,
dirPurcSelYn: "Y",
bilAddrSno: infoForCheckoutData?.bilAddrSno,
dlvrAddrSno: infoForCheckoutData?.dlvrAddrSno,
orderProductCoupontUse,
})
);
}
}, [dispatch, infoForCheckoutData, productData, userNumber, selectedCoupons]);
useEffect(() => {
return () => {
dispatch(resetCheckoutData());
};
}, [dispatch]);
useEffect(() => {
if (infoForCheckoutData && productData) {
dispatch(
getTaxInfos({
mbrNo: userNumber,
bilAddrSno: infoForCheckoutData?.bilAddrSno,
dlvrAddrSno: infoForCheckoutData?.dlvrAddrSno,
reqCheckoutTaxInfoItemList: [
{
cpnSno: null,
dcAmt: null,
frgtTaxCd: productData?.[0].frgtTaxCd,
patnrId: productData?.[0].patnrId,
prdtId: productData?.[0].prdtId,
prodPrc: productData?.[0].price3,
prodQty: productData?.[0].prodQty,
taxCd: productData?.[0].taxCd,
},
],
})
);
}
}, [dispatch, infoForCheckoutData, productData, userNumber]);
useEffect(() => {
spotJob.current.start(() => {
Spotlight.focus("spotlightId_placeOrderBtn");

View File

@@ -10,8 +10,6 @@
z-index: 10;
}
.Wrap {
// margin-top: 90px;
// height: 2050px;
display: flex;
}
}

View File

@@ -1,13 +1,14 @@
import React, { memo, useCallback, useEffect, useState } from "react";
import classNames from "classnames";
import { useDispatch } from "react-redux";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { updateSelectedCoupon } from "../../../actions/checkoutActions";
import CustomImage from "../../../components/CustomImage/CustomImage";
import TDropDown from "../../../components/TDropDown/TDropDown";
import { $L, scaleW } from "../../../utils/helperMethods";
import { $L } from "../../../utils/helperMethods";
import css from "./OffersPromotionItemCard.module.less";
const OffersPromotionItemContainer = SpotlightContainerDecorator("div");
@@ -34,52 +35,15 @@ export default memo(function OffersPromotionItemCard({
prdtCoupon,
...rest
}) {
const [selectedCoupon, setSelectedCoupon] = useState(null);
const [dcPrice, setDcPrice] = useState(price3 || price2);
const dispatch = useDispatch();
const [selectedIndex, setSelectedIndex] = useState(-1);
const [openChk, setOpenChk] = useState(false);
const couponSetting = useCallback(
(info, param) => {
const itemPrice = price3 ? Number(price3) : Number(price2);
let couponName = "";
let dcAmt = 0;
switch (param.shptmDcTpCd) {
case "CPN00402":
couponName = "MAX: " + param.cpnDctrt + "%";
dcAmt = (param.cpnDctrt * itemPrice) / 100;
break;
case "CPN00401":
couponName = "MAX: " + param.cpnAplyMaxDcAmt.toFixed(2);
dcAmt = Number(param.dcAmt);
break;
}
if (itemPrice < param.cpnAplyMinProdAmt) {
console.error("쿠폰적용 최소[상품]금액 미달");
return;
} else if (dcAmt > param.cpnAplyMaxDcAmt) {
dcAmt = param.cpnAplyMaxDcAmt;
}
let discountedPrice = itemPrice - dcAmt;
setDcPrice(discountedPrice);
setSelectedCoupon(param);
console.log("chw", couponName);
},
[price2, price3]
);
const handleCouponSelect = (e) => {
const selectedIdx = e.selected;
const selected = prdtCoupon[selectedIdx];
setSelectedIndex(selectedIdx);
couponSetting({ price2, price3, currSignLoc }, selected);
console.log("chw", selectedCoupon, dcPrice);
dispatch(updateSelectedCoupon(prdtId, selected));
};
const dropDownOpen = useCallback(() => {
@@ -91,12 +55,12 @@ export default memo(function OffersPromotionItemCard({
useEffect(() => {
const c = document.getElementById("floatLayer");
c.classList.add("checkout_dropdown");
return () => {
c.classList.remove("checkout_dropdown");
};
}, []);
return (
<OffersPromotionItemContainer className={css.itemContainer}>
<div className={css.itemHeader}>

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import classNames from "classnames";
import { useDispatch, useSelector } from "react-redux";
@@ -7,7 +7,13 @@ import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { insertMyInfoCheckoutOrder } from "../../../actions/checkoutActions";
import { setHidePopup, setShowPopup } from "../../../actions/commonActions";
import {
popPanel,
pushPanel,
resetPanels,
} from "../../../actions/panelActions";
import { getMyInfoCardPincodeCheck } from "../../../actions/pinCodeActions";
import TButton from "../../../components/TButton/TButton";
import TPopUp from "../../../components/TPopUp/TPopUp";
@@ -32,13 +38,24 @@ export default function PinCodeInput({ setPlaceOrderPopup }) {
const userNumber = useSelector(
(state) => state.common.appStatus.loginUserData.userNumber
);
const addrInfo = useSelector((state) => state.checkout?.infoForCheckoutData);
const selectedCoupons = useSelector(
(state) => state.checkout?.selectedCoupons
);
const productList = useSelector(
(state) => state.checkout?.checkoutData?.productList?.[0]
);
const [pin, setPin] = useState(["", "", "", ""]);
const [errorMsg, setErrorMsg] = useState("");
const [okClicked, setOkClicked] = useState(false);
const isMounted = useRef(true);
useEffect(() => {
if (pinCodeDatas && pinCodeDatas.retCode !== 0 && okClicked) {
if (!isMounted.current) return;
if (pinCodeDatas.data.pwdErrorCnt >= 3) {
dispatch(setShowPopup(Config.ACTIVE_POPUP.setPinCodePopup));
} else {
@@ -49,6 +66,11 @@ export default function PinCodeInput({ setPlaceOrderPopup }) {
useEffect(() => {
setOkClicked(false);
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
@@ -88,14 +110,6 @@ export default function PinCodeInput({ setPlaceOrderPopup }) {
setPlaceOrderPopup(false);
}, [setPlaceOrderPopup]);
// const pincodeCallback = (ret) =>{
// if(ret === error ){
// //
// }else{
// //success
// }
// }
const onClickConfirm = useCallback(() => {
if (pin.includes("")) {
setErrorMsg($L("Please enter a PIN CODE."));
@@ -107,14 +121,96 @@ export default function PinCodeInput({ setPlaceOrderPopup }) {
const pinString = pin.join("");
const encryptedPin = sha256(pinString);
const handlePinCodeCheck = (response) => {
if (response.retCode === 0) {
const orderProductCoupontUse = Object.keys(selectedCoupons).map(
(productId) => {
const {
cpnAplyMaxDcAmt,
cpnAplyMinProdAmt,
cpnAplyMinPurcAmt,
cpnCdCval,
cpnCdSeq,
cpnDctrt,
cpnSno,
dcAmt,
mbrNo,
prdtId,
shptmCpnTpCd,
shptmCpnTrgtCd,
shptmDcTpCd,
} = selectedCoupons[productId];
return {
cpnAplyMaxDcAmt,
cpnAplyMinProdAmt,
cpnAplyMinPurcAmt,
cpnCdCval,
cpnCdSeq,
cpnDctrt,
cpnSno,
dcAmt,
mbrNo,
prdtId,
shptmCpnTpCd,
shptmCpnTrgtCd,
shptmDcTpCd,
};
}
);
dispatch(
insertMyInfoCheckoutOrder(
{
mbrNo: userNumber,
bilAddrSno: addrInfo?.bilAddrSno,
dlvrAddrSno: addrInfo?.dlvrAddrSno,
pinCd: encryptedPin,
orderProductCoupontUse:
orderProductCoupontUse.length > 0 ? orderProductCoupontUse : [],
},
(response) => {
if (!isMounted.current) return;
if (response.data.retCode === 0) {
dispatch(resetPanels());
setPlaceOrderPopup(false);
dispatch(
pushPanel({
name: Config.panel_names.CONFIRM_PANEL,
panelInfos: response.data.data,
})
);
} else {
// TODO: 에러 처리 필요 chw
}
}
)
);
} else {
if (!isMounted.current) return;
setErrorMsg($L("Your entries did not match. Please try again.")); // 핀코드 검증 단계 후 에러처리
}
};
dispatch(
getMyInfoCardPincodeCheck({
mbrNo: userNumber,
pinCd: encryptedPin,
// callback: pincodeCallback,
})
getMyInfoCardPincodeCheck(
{
mbrNo: userNumber,
pinCd: encryptedPin,
},
handlePinCodeCheck
)
);
}, [pin, userNumber, dispatch]);
}, [
pin,
userNumber,
addrInfo,
selectedCoupons,
productList,
isMounted,
dispatch,
]);
const handleClickSetPinCode = useCallback(() => {
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));

View File

@@ -1,26 +1,18 @@
import React, {
useCallback,
useEffect,
useState,
} from 'react';
import React, { useCallback } from "react";
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import classNames from "classnames";
import { useSelector } from "react-redux";
import SpotlightContainerDecorator
from '@enact/spotlight/SpotlightContainerDecorator';
import Spottable from '@enact/spotlight/Spottable';
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import noCouponImg from '../../../../assets/images/img-checkout-coupon@3x.png';
import TCheckBox from '../../../components/TCheckBox/TCheckBox';
import TIconButton from '../../../components/TIconButton/TIconButton';
import TVirtualGridList
from '../../../components/TVirtualGridList/TVirtualGridList';
import { $L } from '../../../utils/helperMethods';
import noCouponImg from "../../../../assets/images/img-checkout-coupon@3x.png";
import TIconButton from "../../../components/TIconButton/TIconButton";
import TVirtualGridList from "../../../components/TVirtualGridList/TVirtualGridList";
import { $L } from "../../../utils/helperMethods";
import OffersPromotionItemCard, {
SIZES,
} from '../components/OffersPromotionItemCard';
import css from './FixedSideBar.module.less';
} from "../components/OffersPromotionItemCard";
import css from "./FixedSideBar.module.less";
const SideBarContainer = SpotlightContainerDecorator("div");
@@ -28,6 +20,9 @@ export default function FixedSideBar({ closeSideBar }) {
const orderItemList = useSelector(
(state) => state.checkout?.checkoutData?.productList
);
const selectedCoupons = useSelector(
(state) => state.checkout.selectedCoupons
); // 선택된 쿠폰 정보
const renderItem = useCallback(
({ index, ...rest }) => {

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useRef } from "react";
import React, { useCallback, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
@@ -6,9 +6,13 @@ import Spotlight from "@enact/spotlight";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import Spottable from "@enact/spotlight/Spottable";
import { setHidePopup, setShowPopup } from "../../../actions/commonActions";
import TButton from "../../../components/TButton/TButton";
import TPopUp from "../../../components/TPopUp/TPopUp";
import TQRCode from "../../../components/TQRCode/TQRCode";
import useScrollTo from "../../../hooks/useScrollTo";
import useScrollTopByDistance from "../../../hooks/useScrollTopByDistance";
import * as Config from "../../../utils/Config";
import { $L } from "../../../utils/helperMethods";
import BillingAddressCard from "../components/BillingAddressCard";
import PaymentCard from "../components/PaymentCard";
@@ -28,12 +32,40 @@ export default function InformationContainer({
toggleOrderSideBar,
scrollTopBody,
}) {
const dispatch = useDispatch();
const checkoutData = useSelector((state) => state.checkout?.checkoutData);
const { cursorVisible } = useSelector((state) => state.common.appStatus);
const { popupVisible, activePopup } = useSelector(
(state) => state.common.popup
);
const cardInfo = useSelector(
(state) => state.checkout?.checkoutData?.cardInfo?.easyPmtSeq
);
const bilAddrSno = useSelector(
(state) => state.checkout?.infoForCheckoutData?.bilAddrSno
);
const auctProdYn = useSelector(
(state) => state.checkout?.checkoutData?.productList?.[0].auctProdYn
);
const totDcAmt = useSelector(
(state) => state.checkout?.checkoutTotalData?.orderTotalAmtInfo?.totDcAmt
);
const [tab, setTab] = useState(0);
const { getScrollTo } = useScrollTo();
const { scrollTopByDistance } = useScrollTopByDistance();
const qrCodeUrls = useMemo(
() => ({
0: "https://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=SHIPPING",
1: "https://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=BILLING",
2: `https://qt3-m.shoptime.lgappstv.com/card_list.jsp?caller=checkout&mode=&selected=${cardInfo}&bilAddrSno=${bilAddrSno}`,
}),
[cardInfo, bilAddrSno]
);
const handleFocus = useCallback(() => {
const c = Spotlight.getCurrent();
const target = c.getAttribute("data-spotlight-id");
@@ -50,6 +82,18 @@ export default function InformationContainer({
);
}, []);
const handleButtonClick = useCallback(
(index) => {
setTab(index);
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
},
[dispatch]
);
const handleCancel = useCallback(() => {
dispatch(setHidePopup());
}, [dispatch]);
return (
<>
<Container className={css.container}>
@@ -71,6 +115,7 @@ export default function InformationContainer({
className={css.editBtn}
spotlightId="shipping-add-btn"
onFocus={handleFocus}
onClick={() => handleButtonClick(0)}
>
ADD/EDIT
</TButton>
@@ -85,6 +130,7 @@ export default function InformationContainer({
className={css.editBtn}
spotlightId="billing-add-btn"
onFocus={handleFocus}
onClick={() => handleButtonClick(1)}
>
ADD/EDIT
</TButton>
@@ -99,6 +145,7 @@ export default function InformationContainer({
className={css.editBtn}
spotlightId="payment-add-btn"
onFocus={handleFocus}
onClick={() => handleButtonClick(2)}
>
ADD/EDIT
</TButton>
@@ -113,11 +160,38 @@ export default function InformationContainer({
spotlightId="checkout-btn-fifth"
onFocus={handleFocus}
>
$12.60
{auctProdYn === "Y" ? " - " : totDcAmt}
</BtnSpot>
</div>
</div>
</Container>
{/* QR Code */}
{activePopup === Config.ACTIVE_POPUP.qrPopup && (
<TPopUp kind="qrPopup" open={popupVisible} onClose={handleCancel}>
<div className={css.popupContainer}>
<div className={css.header}>
<h3>{$L("QR CODE")}</h3>
</div>
<div className={css.qrcodeContainer}>
{qrCodeUrls[tab] && (
<div className={css.qrcode}>
<TQRCode text={qrCodeUrls[tab]} width="360" height="360" />
</div>
)}
<h3>
{$L(
"If you want to add or edit your address and payment information, please scan the QR CODE."
)}
</h3>
<TButton className={css.popupBtn} onClick={handleCancel}>
{$L("CLOSE")}
</TButton>
</div>
</div>
</TPopUp>
)}
</>
);
}

View File

@@ -116,3 +116,44 @@
height: 384px;
}
}
.popupContainer {
.header {
.size(@w: 780px , @h: 102px);
.flex(@display: flex, @justifyCenter: center, @alignCenter: center, @direction: row);
background-color: #e7ebef;
> h3 {
font-size: 36px;
color: #222222;
font-weight: bold;
}
}
.qrcodeContainer {
padding: 30px 0;
display: flex;
flex-direction: column;
align-items: center;
.qrcode {
.size(@w: 360px , @h: 360px);
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 0 0 1px #dadada inset;
margin-bottom: 41px;
}
> h3 {
display: flex;
text-align: center;
word-break: break-word;
line-height: 1.27;
}
.popupBtn {
.size(@w: 300px , @h: 78px);
margin-top: 38px;
}
}
}

View File

@@ -4,6 +4,9 @@
.container {
.size(@w: 600px , @h: inherit);
background-color: #2c343f;
position: sticky;
top: 90px;
height: calc(100vh - 90px);
}
.order {

View File

@@ -1,10 +1,9 @@
import React, { useCallback, useEffect, useState } from "react";
import React, { useCallback, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import SpotlightContainerDecorator from "@enact/spotlight/SpotlightContainerDecorator";
import { changeAppStatus } from "../../../actions/commonActions";
import TButton from "../../../components/TButton/TButton";
import css from "./SummaryContainer.module.less";
@@ -13,92 +12,23 @@ const Container = SpotlightContainerDecorator(
"div"
);
const DEFAULT_SUMMARY = {
itemPrice: "-",
shPrice: "-",
couponPrice: "-",
itemsTotal: "-",
taxTotal: "-",
itemsTaxTotal: "-",
estimatedTotal: "-",
currSign: "",
currSignLoc: "",
};
export default function SummaryContainer({ setPlaceOrderPopup }) {
const dispatch = useDispatch();
const { productList } = useSelector((state) => state.checkout.checkoutData);
const taxInfosData = useSelector((state) => state.checkout?.taxInfosData);
const [summary, setSummary] = useState(DEFAULT_SUMMARY);
const [auctProdYn, setAuctProdYn] = useState(false);
const itemSetting = useCallback(
(productList) => {
if (!productList?.length) return;
let itemCnt = 0; // 상품 합계
let shCnt = 0; // 배송비 합계
let couponCnt = 0; // 쿠폰 할인 합계
let itemsTotal = 0; // 상품 + 배송비
let taxCnt = 0; // 세금 합계
let itemsTaxTotal = 0; // itemsTotal + tax
let estimatedTotal = 0; // itemsTotal + tax - couponCnt
productList.forEach((info) => {
const itemPrice = info.price3 || info.price2;
itemCnt += itemPrice * parseInt(info.prodQty, 10);
shCnt += parseFloat(info.shippingCharge || 0);
if (info.prdtCoupon?.length) {
couponCnt += info.prdtCoupon.reduce(
(acc, coupon) => acc + parseFloat(coupon.discountAmount || 0),
0
);
}
if (info.auctProdYn === "Y") {
setAuctProdYn(true);
}
});
shCnt = taxInfosData.totDlvrAmt ?? shCnt;
taxCnt = taxInfosData.billing?.taxTotAmt ?? taxCnt;
itemsTotal = itemCnt + shCnt;
itemsTaxTotal = itemsTotal + taxCnt;
estimatedTotal = itemsTaxTotal - couponCnt;
setSummary((prevSummary) => ({
...prevSummary,
itemPrice: itemCnt.toFixed(2),
shPrice: shCnt.toFixed(2),
couponPrice: couponCnt.toFixed(2),
itemsTotal: itemsTotal.toFixed(2),
taxTotal: taxCnt.toFixed(2),
itemsTaxTotal: itemsTaxTotal.toFixed(2),
estimatedTotal: auctProdYn ? "-" : estimatedTotal.toFixed(2),
currSign: productList[0]?.currSign || "",
currSignLoc: productList[0]?.currSignLoc || "",
}));
},
[taxInfosData, auctProdYn, dispatch]
const priceTotalData = useSelector(
(state) => state.checkout?.checkoutTotalData?.orderTotalAmtInfo
);
useEffect(() => {
if (productList?.length) {
itemSetting(productList);
}
}, [productList, itemSetting]);
const items = [
{ name: "Items", value: summary.itemPrice },
{ name: "Options Total", value: "0.00" },
{ name: "Shipping & Handling", value: summary.shPrice },
{ name: "TOTAL (before Tax)", value: summary.itemsTotal },
{ name: "Estimated Sales Tax", value: summary.taxTotal },
{ name: "Your Coupon Savings", value: summary.couponPrice },
{ name: "Items", value: priceTotalData?.totProdPrc || "-" },
{ name: "Options Total", value: priceTotalData?.totOptPrc || "-" },
{ name: "Shipping & Handling", value: priceTotalData?.totDlvrAmt || "-" },
{ name: "TOTAL (before Tax)", value: priceTotalData?.totProdPrc || "-" },
{ name: "Estimated Sales Tax", value: priceTotalData?.ordTotTaxAmt || "-" },
{
name: "Your Coupon Savings",
value: priceTotalData?.cpnAplyMaxDcAmt || "-",
},
];
const handleClickOrder = useCallback(() => {
@@ -111,45 +41,17 @@ export default function SummaryContainer({ setPlaceOrderPopup }) {
<h3>ORDER SUMMARY</h3>
<span className={css.line} />
<div className={css.itemWrap}>
{!auctProdYn ? (
<>
{items.map((item, index) => (
<div key={index} className={css.item}>
<span className={css.summaryDesc}>{item.name}</span>
<span className={css.summaryCnt}>
{index === items.length - 1
? summary.currSignLoc === "L"
? `- ${summary.currSign} ${item.value}`
: `- ${item.value} ${summary.currSign}`
: summary.currSignLoc === "L"
? `${summary.currSign} ${item.value}`
: `${item.value} ${summary.currSign}`}
</span>
</div>
))}
</>
) : (
<div className={css.noticeBox}>
<span className={css.noticeBoxText}>
{"Purchased products will be paid at the final price."}
</span>
{items.map((item, index) => (
<div key={index} className={css.item}>
<span className={css.summaryDesc}>{item.name}</span>
<span className={css.summaryCnt}>{item.value}</span>
</div>
)}
))}
</div>
</div>
<div className={css.total}>
<div>Estimated Total</div>
<div className={css.price}>
{!auctProdYn ? (
<>
{summary.currSignLoc === "L"
? `${summary.currSign} ${summary.estimatedTotal}`
: `${summary.estimatedTotal} ${summary.currSign}`}
</>
) : (
summary.estimatedTotal
)}
</div>
<div className={css.price}>{priceTotalData?.ordPmtReqAmt || "-"}</div>
</div>
<div className={css.bottom}>
<TButton

View File

@@ -0,0 +1,12 @@
import React from "react";
import TPanel from "../../components/TPanel/TPanel";
import css from "./ConfirmPanel.module.less";
export default function ConfirmPanel() {
return (
<TPanel className={css.confirmPanel} isTabActivated={false}>
OK!
</TPanel>
);
}

View File

@@ -0,0 +1,6 @@
{
"main": "ConfirmPanel.jsx",
"styles": [
"ConfirmPanel.module.less"
]
}

View File

@@ -18,12 +18,14 @@ import PreloadImage from "../../components/PreloadImage/PreloadImage";
import TabLayout from "../../components/TabLayout/TabLayout";
import TPopUp from "../../components/TPopUp/TPopUp";
import TToast from "../../components/TToast/TToast";
import useLogService from "../../hooks/useLogService";
import * as Config from "../../utils/Config";
import { panel_names } from "../../utils/Config";
import { $L, getSpottableDescendants } from "../../utils/helperMethods";
import CartPanel from "../CartPanel/CartPanel";
import CategoryPanel from "../CategoryPanel/CategoryPanel";
import CheckOutPanel from "../CheckOutPanel/CheckOutPanel";
import ConfirmPanel from "../ConfirmPanel/ConfirmPanel";
import DebugPanel from "../DebugPanel/DebugPanel";
import DetailPanel from "../DetailPanel/DetailPanel";
import ErrorPanel from "../ErrorPanel/ErrorPanel";
@@ -37,12 +39,11 @@ import MyPagePanel from "../MyPagePanel/MyPagePanel";
import OnSalePanel from "../OnSalePanel/OnSalePanel";
import PlayerPanel from "../PlayerPanel/PlayerPanel";
import SearchPanel from "../SearchPanel/SearchPanel";
import ServiceUnavailableNoticePanel from "../ServiceUnavailableNoticePanel/ServiceUnavailableNoticePanel";
import ThemeCurationPanel from "../ThemeCurationPanel/ThemeCurationPanel";
import TrendingNowPanel from "../TrendingNowPanel/TrendingNowPanel";
import WelcomeEventPanel from "../WelcomeEventPanel/WelcomeEventPanel";
import css from "./MainView.module.less";
import ServiceUnavailableNoticePanel from "../ServiceUnavailableNoticePanel/ServiceUnavailableNoticePanel";
import useLogService from "../../hooks/useLogService";
const preloadImages = [testImage];
@@ -66,6 +67,7 @@ const panelMap = {
[Config.panel_names.THEME_CURATION_PANEL]: ThemeCurationPanel,
[Config.panel_names.IMAGE_PANEL]: ImagePanel,
[Config.panel_names.SERVICE_UNAVAILABLE]: ServiceUnavailableNoticePanel,
[Config.panel_names.CONFIRM_PANEL]: ConfirmPanel,
};
export default function MainView({ className }) {

View File

@@ -1,4 +1,10 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useDispatch, useSelector } from "react-redux";
@@ -25,6 +31,7 @@ import TButtonTab, {
import THeader from "../../../../components/THeader/THeader";
import TPopUp from "../../../../components/TPopUp/TPopUp";
import TQRCode from "../../../../components/TQRCode/TQRCode";
import useLogService from "../../../../hooks/useLogService";
import { getLoginUserData } from "../../../../lunaSend";
import * as Config from "../../../../utils/Config";
import { $L } from "../../../../utils/helperMethods";
@@ -33,7 +40,6 @@ import BillingAddressTab from "./MyInfoTabContents/BillingAddressTab/BillingAddr
import CouponTab from "./MyInfoTabContents/CouponTab/CouponTab";
import PaymentTab from "./MyInfoTabContents/PaymentTab/PaymentTab";
import ShippingAddressTab from "./MyInfoTabContents/ShippingAddressTab/ShippingAddressTab";
import useLogService from "../../../../hooks/useLogService";
const TabContainer = SpotlightContainerDecorator(
{ enterTo: "last-focused" },
@@ -77,6 +83,8 @@ export default function MyInfo({ title, cbScrollTo }) {
const panelInfos = useSelector(
(state) => state.panels.panels[panels?.length - 1]
);
const cardInfo = useSelector((state) => state.card.cardData.easyPmtSeq);
const [tab, setTab] = useState(
panelInfos.panelInfo.menuOrd ? panelInfos.panelInfo.menuOrd : 0
);
@@ -91,11 +99,14 @@ export default function MyInfo({ title, cbScrollTo }) {
3: CouponTab,
};
const qrCodeUrls = {
0: "https://www.google.com",
1: "http://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=BILLING&caller=mypage&addrData=&mode=",
2: "http://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=SHIPPING&caller=mypage&addrData=&mode=",
};
const qrCodeUrls = useMemo(
() => ({
0: `https://qt3-m.shoptime.lgappstv.com/card_list.jsp?caller=checkout&mode=&selected=${cardInfo}`,
1: "https://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=BILLING",
2: "https://qt3-m.shoptime.lgappstv.com/add_address.jsp?from=SHIPPING",
}),
[cardInfo]
);
const SelectedComponent = myInfoTabContents[tab];