368 lines
12 KiB
JavaScript
368 lines
12 KiB
JavaScript
import React, {
|
|
useCallback,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from 'react';
|
|
|
|
import {
|
|
useDispatch,
|
|
useSelector,
|
|
} from 'react-redux';
|
|
|
|
import Spotlight from '@enact/spotlight';
|
|
import SpotlightContainerDecorator
|
|
from '@enact/spotlight/SpotlightContainerDecorator';
|
|
import Spottable from '@enact/spotlight/Spottable';
|
|
|
|
import {
|
|
setHidePopup,
|
|
setShowPopup,
|
|
} from '../../../actions/commonActions';
|
|
import { sendLogTotalRecommend } from '../../../actions/logActions';
|
|
import { popPanel } from '../../../actions/panelActions';
|
|
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 { BUYNOW_CONFIG } from '../../../utils/BuyNowConfig';
|
|
import * as Config from '../../../utils/Config';
|
|
import {
|
|
$L,
|
|
getQRCodeUrl,
|
|
} from '../../../utils/helperMethods';
|
|
import BillingAddressCard from '../components/BillingAddressCard';
|
|
import PaymentCard from '../components/PaymentCard';
|
|
import ShippingAddressCard from '../components/ShippingAddressCard';
|
|
import Subject from '../components/Subject';
|
|
import css from './InformationContainer.module.less';
|
|
|
|
const Container = SpotlightContainerDecorator({ enterTo: 'last-focused' }, 'div');
|
|
|
|
const BtnSpot = Spottable('p');
|
|
|
|
const getBtnNmByIndex = (index) => {
|
|
switch (index) {
|
|
case 0:
|
|
return 'SHIPPING ADDRESS';
|
|
case 1:
|
|
return 'BILLING ADDRESS';
|
|
case 2:
|
|
return 'PATMENT METHOD';
|
|
default:
|
|
return '';
|
|
}
|
|
};
|
|
|
|
export default function InformationContainer({
|
|
toggleOfferSideBar,
|
|
toggleOrderSideBar,
|
|
scrollTopBody,
|
|
doSendLogMyInfoEdit,
|
|
}) {
|
|
const dispatch = useDispatch();
|
|
|
|
// All useSelector calls must be at the top - before any conditional logic
|
|
const reduxCheckoutData = useSelector((state) => state.checkout?.checkoutData);
|
|
const { cursorVisible } = useSelector((state) => state.common.appStatus);
|
|
const { popupVisible, activePopup } = useSelector((state) => state.common.popup);
|
|
const auctProdYn = useSelector(
|
|
(state) => state.checkout?.checkoutData?.productList?.[0].auctProdYn
|
|
);
|
|
const auctFinalPriceChgDt = useSelector(
|
|
(state) => state.checkout?.checkoutData?.productList?.[0].auctFinalPriceChgDt
|
|
);
|
|
const totDcAmt = useSelector((state) => state.checkout?.checkoutTotalData?.totDcAmt);
|
|
const reduxServerHOST = useSelector((state) => state.common.appStatus.serverHOST);
|
|
const reduxServerType = useSelector((state) => state.localSettings.serverType);
|
|
const reduxNowMenu = useSelector((state) => state.common.menu.nowMenu);
|
|
const reduxEntryMenu = useSelector((state) => state.common.menu.entryMenu);
|
|
const reduxDeviceInfo = useSelector((state) => state.device.deviceInfo);
|
|
|
|
// Mock Mode: Provide fallback values for Redux selectors
|
|
const serverHOST = BUYNOW_CONFIG.isMockMode()
|
|
? reduxServerHOST || 'sdp-us.shoptime.com'
|
|
: reduxServerHOST;
|
|
const serverType = BUYNOW_CONFIG.isMockMode() ? reduxServerType || 'system' : reduxServerType;
|
|
const nowMenu = BUYNOW_CONFIG.isMockMode() ? reduxNowMenu || 'MOCK_NOW_MENU' : reduxNowMenu;
|
|
const entryMenu = BUYNOW_CONFIG.isMockMode()
|
|
? reduxEntryMenu || 'MOCK_ENTRY_MENU'
|
|
: reduxEntryMenu;
|
|
const deviceInfo = BUYNOW_CONFIG.isMockMode()
|
|
? reduxDeviceInfo || { dvcIndex: 1 }
|
|
: reduxDeviceInfo;
|
|
|
|
// Check if reduxCheckoutData has actual data (productList)
|
|
const hasValidCheckoutData =
|
|
reduxCheckoutData?.productList &&
|
|
Array.isArray(reduxCheckoutData.productList) &&
|
|
reduxCheckoutData.productList.length > 0;
|
|
|
|
const checkoutData = hasValidCheckoutData
|
|
? reduxCheckoutData
|
|
: BUYNOW_CONFIG.isMockMode()
|
|
? {
|
|
productList: [
|
|
{
|
|
prdtId: 'MOCK_PRODUCT_1',
|
|
prdtNm: 'Mock Product',
|
|
patnrId: '1',
|
|
prodQty: 1,
|
|
prdtOpt: [{ prodOptCdCval: 'MOCK_OPT_1' }],
|
|
auctProdYn: 'N',
|
|
auctFinalPriceChgDt: null,
|
|
},
|
|
],
|
|
shippingAddressList: [
|
|
{
|
|
dlvrAddrSno: 'MOCK_ADDR_1',
|
|
dlvrOdrFnm: 'Mock',
|
|
dlvrOdrLnm: 'Receiver',
|
|
dlvrZpcd: '12345',
|
|
dlvrStatNm: 'CA',
|
|
dlvrCityNm: 'Los Angeles',
|
|
dlvrDtlAddr: '123 Mock Street, Suite 100',
|
|
dlvrCtpt: '555-1234',
|
|
dlvrEmalAddr: 'mock@example.com',
|
|
},
|
|
],
|
|
billingAddressList: [
|
|
{
|
|
bilAddrSno: 'MOCK_ADDR_2',
|
|
bilOdrFnm: 'Mock',
|
|
bilOdrLnm: 'Payer',
|
|
bilZpcd: '12346',
|
|
bilStatNm: 'CA',
|
|
bilCityNm: 'Los Angeles',
|
|
bilDtlAddr: '456 Mock Avenue, Suite 200',
|
|
bilCtpt: '5555678',
|
|
bilEmalAddr: 'payer@example.com',
|
|
},
|
|
],
|
|
cardInfo: [
|
|
{
|
|
cardSno: 'MOCK_CARD_1',
|
|
cardNm: 'Mock Card',
|
|
cardNo: '****-****-****-1234',
|
|
cardType: 'CREDIT',
|
|
},
|
|
],
|
|
}
|
|
: null;
|
|
|
|
const [_, setTab] = useState(0);
|
|
const [prdtData, setPrdtData] = useState({});
|
|
|
|
const { getScrollTo } = useScrollTo();
|
|
const { scrollTopByDistance } = useScrollTopByDistance();
|
|
|
|
// Use effect to handle missing checkoutData (only in API mode without mock fallback)
|
|
useEffect(() => {
|
|
if (!checkoutData) {
|
|
dispatch(popPanel());
|
|
}
|
|
}, [checkoutData, dispatch]);
|
|
|
|
useEffect(() => {
|
|
if (checkoutData && checkoutData.productList) {
|
|
try {
|
|
const { patnrId, prdtId, prodQty } = checkoutData.productList[0];
|
|
const prdtOpt = checkoutData.productList[0].prdtOpt;
|
|
const prodOptCdCval =
|
|
Array.isArray(prdtOpt) && prdtOpt.length > 0 ? prdtOpt[0].prodOptCdCval : '';
|
|
|
|
const params = {
|
|
patnrId: patnrId,
|
|
prdtId: prdtId,
|
|
prodOptCdCval: prodOptCdCval || '',
|
|
prodQty: prodQty,
|
|
};
|
|
|
|
setPrdtData(params);
|
|
} catch (error) {
|
|
console.error('[CheckOutPanel] InformationContainer prdtData useEffect - ERROR:', error);
|
|
}
|
|
}
|
|
}, [checkoutData]);
|
|
|
|
const checkoutUrlObj = useMemo(() => {
|
|
try {
|
|
// Guard: ensure checkoutData and productList exist
|
|
if (!checkoutData?.productList?.[0]) {
|
|
console.error('[CheckOutPanel] InformationContainer checkoutUrl useMemo - No product data');
|
|
return null;
|
|
}
|
|
|
|
const { patnrId, prdtId } = checkoutData.productList[0];
|
|
const url = getQRCodeUrl({
|
|
serverHOST,
|
|
serverType,
|
|
prdtData,
|
|
entryMenu,
|
|
nowMenu,
|
|
prdtId,
|
|
patnrId,
|
|
index: deviceInfo?.dvcIndex,
|
|
});
|
|
return url;
|
|
} catch (error) {
|
|
console.error('[CheckOutPanel] InformationContainer checkoutUrl useMemo - ERROR:', error);
|
|
return null;
|
|
}
|
|
}, [serverHOST, serverType, entryMenu, nowMenu, deviceInfo]);
|
|
|
|
// Extract the actual URL string from the URL object
|
|
const checkoutUrl = checkoutUrlObj?.checkoutUrl || null;
|
|
|
|
const handleFocus = useCallback(() => {
|
|
const c = Spotlight.getCurrent();
|
|
const target = c.getAttribute('data-spotlight-id');
|
|
|
|
const targetValue = '[data-spotlight-id="' + target + '"]';
|
|
if (cursorVisible) {
|
|
return;
|
|
}
|
|
scrollTopByDistance(`[data-marker="scroll-marker"]`, targetValue, scrollTopBody, 100);
|
|
}, []);
|
|
|
|
const handleButtonClick = useCallback(
|
|
(index) => {
|
|
const btnNm = getBtnNmByIndex(index);
|
|
|
|
if (btnNm && doSendLogMyInfoEdit) {
|
|
doSendLogMyInfoEdit(btnNm);
|
|
}
|
|
|
|
setTab(index);
|
|
dispatch(setShowPopup(Config.ACTIVE_POPUP.qrPopup));
|
|
dispatch(
|
|
sendLogTotalRecommend({
|
|
buttonTitle: `${btnNm} ADD/EDIT`,
|
|
buttonId: `checkout_info_${btnNm.toLowerCase().replace(/\s+/g, '_')}`,
|
|
contextName: Config.LOG_CONTEXT_NAME.CHECKOUT,
|
|
messageId: Config.LOG_MESSAGE_ID.BUTTONCLICK,
|
|
})
|
|
);
|
|
},
|
|
[dispatch, doSendLogMyInfoEdit]
|
|
);
|
|
|
|
const handleCancel = useCallback(() => {
|
|
dispatch(setHidePopup());
|
|
}, [dispatch]);
|
|
|
|
const handleDone = useCallback(() => {
|
|
dispatch(setHidePopup());
|
|
dispatch(popPanel());
|
|
}, [dispatch]);
|
|
|
|
return (
|
|
<>
|
|
<Container className={css.container}>
|
|
<div data-marker="scroll-marker" />
|
|
<Subject title="ORDER ITEMS" />
|
|
<div className={css.markBtn}>
|
|
<BtnSpot
|
|
onClick={toggleOrderSideBar}
|
|
scrollTopBody={scrollTopBody}
|
|
spotlightId="checkout-btn-first"
|
|
onFocus={handleFocus}
|
|
>
|
|
{checkoutData?.productList?.length || 0} ITEMS
|
|
</BtnSpot>
|
|
</div>
|
|
<div className={css.listBox}>
|
|
<Subject title="SHIPPING ADDRESS" />
|
|
<TButton
|
|
className={css.editBtn}
|
|
spotlightId="shipping-add-btn"
|
|
onFocus={handleFocus}
|
|
onClick={() => handleButtonClick(0)}
|
|
>
|
|
ADD/EDIT
|
|
</TButton>
|
|
{checkoutData?.shippingAddressList && (
|
|
<ShippingAddressCard list={checkoutData.shippingAddressList} onFocus={handleFocus} />
|
|
)}
|
|
</div>
|
|
<div className={css.listBox}>
|
|
<Subject title="BILLING ADDRESS" />
|
|
<TButton
|
|
className={css.editBtn}
|
|
spotlightId="billing-add-btn"
|
|
onFocus={handleFocus}
|
|
onClick={() => handleButtonClick(1)}
|
|
>
|
|
ADD/EDIT
|
|
</TButton>
|
|
{/* BillingAddressCard disabled due to infinite render loop in Mock Mode */}
|
|
{/* <div style={{ padding: '10px', textAlign: 'center', color: '#999' }}>
|
|
Mock Billing Address
|
|
</div> */}
|
|
{checkoutData?.billingAddressList && (
|
|
<BillingAddressCard list={checkoutData.billingAddressList} onFocus={handleFocus} />
|
|
)}
|
|
</div>
|
|
<div className={css.listBox}>
|
|
<Subject title="PATMENT METHOD" />
|
|
<TButton
|
|
className={css.editBtn}
|
|
spotlightId="payment-add-btn"
|
|
onFocus={handleFocus}
|
|
onClick={() => handleButtonClick(2)}
|
|
>
|
|
ADD/EDIT
|
|
</TButton>
|
|
{checkoutData?.cardInfo && <PaymentCard list={checkoutData.cardInfo} />}
|
|
</div>
|
|
<div className={css.listBox}>
|
|
<Subject title="OFFERS & PROMOTION" />
|
|
<div className={css.markBtn}>
|
|
<BtnSpot
|
|
onClick={toggleOfferSideBar}
|
|
scrollTopBody={scrollTopBody}
|
|
spotlightId="checkout-btn-fifth"
|
|
onFocus={handleFocus}
|
|
>
|
|
{auctProdYn === 'Y' && !auctFinalPriceChgDt ? ' - ' : totDcAmt ? totDcAmt : '-'}
|
|
</BtnSpot>
|
|
</div>
|
|
</div>
|
|
</Container>
|
|
|
|
{/* QR Code */}
|
|
{activePopup === Config.ACTIVE_POPUP.qrPopup && (
|
|
<TPopUp
|
|
kind="qrPopup"
|
|
open={popupVisible}
|
|
onClose={handleCancel}
|
|
className={css.checkoutQRPopup}
|
|
>
|
|
<div className={css.popupContainer}>
|
|
<div className={css.header}>
|
|
<h3>{$L('QR CODE')}</h3>
|
|
</div>
|
|
|
|
<div className={css.qrcodeContainer}>
|
|
{checkoutUrl && (
|
|
<div className={css.qrcode}>
|
|
<TQRCode text={checkoutUrl} isBillingProductVisible width="360" height="360" />
|
|
</div>
|
|
)}
|
|
<h3>
|
|
{$L(
|
|
'Please update your information and complete the payment on your mobile. By clicking the OK button, you will be redirected to the product details page'
|
|
)}
|
|
</h3>
|
|
<TButton className={css.popupBtn} onClick={handleDone}>
|
|
{$L('OK')}
|
|
</TButton>
|
|
</div>
|
|
</div>
|
|
</TPopUp>
|
|
)}
|
|
</>
|
|
);
|
|
}
|