const ProductCartHeader = (props) => {
const {
config, barcode, setBarcode, setBaskets, baskets, carts, setCarts,
userInfo, shift, setShift, member, setMember, setSaleNumber, isMobile,
isCashier = true, setShowCart, returnNo, setReturnNo,
transactionReturn, setTransactionReturn
} = props;
const { Row, Col, Badge, Button } = ReactBootstrap;
// --- States ---
const [code, setCode] = useState('');
const [loading, setLoading] = useState(false);
const [custCode, setCustCode] = useState();
const [showModalMember, setShowModalMember] = useState(false);
const [showModalPONumber, setShowModalPONumber] = useState(false);
const [showModalTransaction, setShowModalTransaction] = useState(false);
const [showModalSelectBasket, setShowModalSelectBasket] = useState(false);
const [showModalReturn, setShowModalReturn] = useState(false);
const [showModalConfirmReturn, setShowModalConfirmReturn] = useState(false);
const [showShiftModal, setShowShiftModal] = useState(false);
const [showModalScanBarcode, setShowModalScanBarcode] = useState(false);
const [poNumber, setPoNumber] = useState(baskets?.basket?.po_number || '');
// --- Refs ---
const inputCodeRef = useRef(null);
const btnSaveDraftRef = useRef(null);
const btnOpenDraftRef = useRef(null);
const btnCheckoutRef = useRef(null);
// --- Memoized Values ---
const displayHotKey = useMemo(() => (isMobile ? "none" : ""), [isMobile]);
// Tối ưu việc lấy dữ liệu nháp từ Cookie
const jTmpBasket = useMemo(() => {
const strTmpBastket = commonFunc.getCookies('tmp_baskets');
if (strTmpBastket) {
try { return JSON.parse(strTmpBastket); } catch (e) { return []; }
}
return [];
}, [baskets]); // Re-calculate khi baskets thay đổi để đồng bộ logic hiển thị nút
// --- Callbacks (Tối ưu hiệu năng) ---
const handleSearch = useCallback(() => {
if (!code) return;
let codeTmp = `${code.trim()}_${new Date().getTime()}`;
setBarcode(codeTmp);
setCode('');
}, [code, setBarcode]);
const handleChangeCustCode = useCallback(() => {
if (!custCode || !custCode.trim()) return;
fetchMdcMember(custCode);
setShowModalMember(false);
}, [custCode]);
const handleAddMember = useCallback((e) => {
if (e) e.preventDefault();
setShowModalMember(true);
}, []);
const handleNewBasket = useCallback(() => {
commonFunc.setCookies('baskets', {}, -1);
setMember(null);
setBaskets(prev => ({ ...prev, items: [], basket: {} }));
if (!isCashier) {
window.location.href = '/';
} else {
inputCodeRef.current?.focus();
}
}, [isCashier, setBaskets, setMember]);
const handleSaveDraft = useCallback(() => {
if (!baskets || (baskets?.items?.length || 0) === 0) return;
// 1. Hàm lọc bỏ product_info cho một giỏ hàng bất kỳ
const cleanBasket = (basketObj) => ({
...basketObj,
items: basketObj.items?.map(item => {
const { product_info, ...rest } = item; // Loại bỏ product_info
return rest;
}) || []
});
// 2. Làm sạch giỏ hàng hiện tại
const cleanedCurrentBasket = cleanBasket(baskets);
// 3. Đảm bảo jTmpBasket cũng sạch (đề phòng dữ liệu cũ từ Cookie lên bị lỗi)
const cleanedOldDrafts = jTmpBasket.map(draft => cleanBasket(draft));
// 4. Kết hợp lại
const newDrafts = [...cleanedOldDrafts, cleanedCurrentBasket];
// 5. Lưu Cookie
commonFunc.setCookies('tmp_baskets', JSON.stringify(newDrafts), undefined);
// ... các lệnh reset state bên dưới
commonFunc.setCookies('baskets', {}, -1);
setBaskets({ items: [], basket: {} });
setMember(null);
setShowCart(false);
}, [baskets, jTmpBasket, setBaskets, setMember, setShowCart]);
const handleReturn = useCallback((rNo) => {
if (rNo) {
setReturnNo(rNo);
setShowModalConfirmReturn(true);
} else {
setShowModalReturn(true);
}
}, [setReturnNo]);
const handleCheckOut = useCallback(() => {
if (returnNo > 0) {
setShowModalConfirmReturn(true);
} else {
setShowModalTransaction(true);
}
}, [returnNo]);
const handleOpenShift = useCallback(() => setShowShiftModal(true), []);
const handleScanBarcode = useCallback(() => setShowModalScanBarcode(true), []);
const handleShowModalSelectBasket = useCallback(() => {
if ((baskets?.items?.length || 0) > 0) return;
setShowModalSelectBasket(true);
}, [baskets]);
// --- API Functions ---
const fetchMdcMember = async (cCode) => {
try {
setLoading(true);
const url = `/api/get-mdc-member/${cCode}`;
const resultObj = await commonApi.APIGETByURL(url, config);
if (resultObj?.status === 200) {
const dataTmp = resultObj.data;
setMember(dataTmp);
const currentCustCode = baskets?.basket?.cust_code;
if (currentCustCode !== dataTmp?.sign) {
const basketTmp = {
...baskets,
basket: { ...baskets?.basket, cust_code: dataTmp?.sign },
items: baskets?.items || []
};
setBaskets(basketTmp);
commonFunc.setCookies('baskets', JSON.stringify(basketTmp));
}
}
} catch (e) {
console.error("ERROR get-mdc-member", e);
} finally {
setLoading(false);
}
};
const fetchMdcStoreShift = async () => {
if (!userInfo?.branch_no || userInfo.branch_no === 0) return;
try {
setLoading(true);
const url = `/api/get-mdc-current-store-shift/${userInfo.branch_no}`;
const resultObj = await commonApi.APIGETByURL(url, config);
if (resultObj?.status === 200) {
setShift(resultObj.data);
}
} catch (e) {
console.error("ERROR get-mdc-current-store-shift", e);
} finally {
setLoading(false);
}
};
// --- Effects ---
// Check URL params for return_no
useEffect(() => {
const queryParams = new URLSearchParams(window.location.search);
const r_no = queryParams.get('return_no');
if (r_no) handleReturn(r_no);
}, [handleReturn]);
// Sync PO Number and Member
useEffect(() => {
const cust_code = baskets?.basket?.cust_code;
if (cust_code && !member) fetchMdcMember(cust_code);
setPoNumber(baskets?.basket?.po_number || '');
}, [baskets]);
// Fetch shift info
useEffect(() => {
if (userInfo && !shift) fetchMdcStoreShift();
}, [userInfo]);
// Keyboard Shortcuts
useEffect(() => {
const handleKeyDown = (e) => {
if (showModalMember) return;
if (e.key === 'F9') {
e.preventDefault(); handleAddMember();
} else if (e.key === 'F2') {
e.preventDefault();
(inputCodeRef.current || document.getElementById('txtBarcode'))?.focus();
} else if (e.key === 'F8') {
e.preventDefault(); btnCheckoutRef.current?.click();
} else if (e.key === 'F4') {
e.preventDefault();
document.querySelector('button[name="btn_add_to_basket"]')?.click();
} else if (e.key === 's' && e.altKey) {
e.preventDefault(); btnSaveDraftRef.current?.click();
} else if (e.key === 'r' && e.altKey) {
e.preventDefault();
btnOpenDraftRef.current ? btnOpenDraftRef.current.click() : handleShowModalSelectBasket();
} else if (e.key === 'q' && e.altKey) {
e.preventDefault(); handleNewBasket();
} else if (e.key === 't' && e.altKey && isCashier) {
e.preventDefault(); handleReturn();
} else if (e.key === 'F5' && e.altKey) {
e.preventDefault(); handleOpenShift();
} else if (e.key === 'p' && e.altKey && isCashier) {
e.preventDefault(); setShowModalPONumber(true);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [showModalMember, isCashier, handleAddMember, handleNewBasket, handleReturn, handleOpenShift, handleShowModalSelectBasket]);
return (
<>
{isCashier && (
setCode(e.target.value)}
onEnter={handleSearch}
autoFocus={true}
inputRef={inputCodeRef}
size="sm"
style={{ width: '100px', float: 'left' }}
/>
{isMobile && (
)}
{shift?.name || 'No Shift'}
ALT-F5
}
onClick={handleOpenShift}
variant='warning'
style={{ marginLeft: 5, padding: 1, float: 'left' }}
/>
{member ? (config?.lang === 'vi' ? `${member.last_name} ${member.first_name}` : `${member.first_name} ${member.last_name}`) : "CRM"}
F9
}
onClick={handleAddMember}
variant='info'
style={{ marginLeft: 5, padding: 1, float: 'right' }}
/>
)}
{(baskets?.items?.length > 0) && (
{returnNo > 0 ? (config?.lang === 'vi' ? "Trả" : "Return") : (config?.lang === 'vi' ? "Tính" : "Pay")}
F8
}
onClick={handleCheckOut}
variant='success'
style={{ marginLeft: 10, padding: 2, float: 'right' }}
refCtl={btnCheckoutRef}
/>
)}
{isCashier && (baskets?.items?.length > 0) && (
{baskets?.basket?.po_number ? `PO: ${baskets.basket.po_number}` : "PO Number"}
ALT-P
>
}
onClick={() => setShowModalPONumber(true)}
variant={baskets?.basket?.po_number ? 'warning' : 'info'}
style={{ marginLeft: 10, padding: 2, float: 'right' }}
/>
)}
{(baskets?.items?.length > 0) && (
{config?.lang === 'vi' ? "Tạm" : "Draft"}
ALT-S
}
onClick={handleSaveDraft}
variant='secondary'
style={{ marginLeft: 10, padding: 2, float: 'right' }}
refCtl={btnSaveDraftRef}
/>
)}
{config?.lang === 'vi' ? "Mở đơn nháp" : "Retrieve Draft"}
ALT-R
}
onClick={handleShowModalSelectBasket}
variant='info'
style={{ marginLeft: 10, padding: 2, display: ((jTmpBasket?.length > 0 && (baskets?.items?.length || 0) === 0) ? "" : "none") }}
refCtl={btnOpenDraftRef}
/>
{isCashier && (!baskets?.items || baskets.items.length === 0 || !baskets.items.some(item => item.line_type === 'R')) && (
{config?.lang === 'vi' ? "Trả hàng" : "Return"}
ALT-T
}
onClick={() => handleReturn(null)}
variant='danger'
style={{ padding: 2, marginLeft: 10, float: 'right' }}
/>
)}
{(baskets?.items?.length > 0) && (
{config?.lang === 'vi' ? "Đơn Mới" : "New"}
ALT-Q
}
onClick={handleNewBasket}
variant='outline-primary'
style={{ padding: 2, marginLeft: 10, float: 'right' }}
/>
)}
{/* Modals - Giữ nguyên toàn bộ cấu trúc props */}
setShowModalMember(false)}
handleChangeCustCode={handleChangeCustCode} setShowForm={setShowModalMember}
setShow={setShowModalMember} isMobile={isMobile}
/>
setShowModalPONumber(false)}
setBaskets={setBaskets} poNumber={poNumber} setPoNumber={setPoNumber}
/>
{config?.lang === 'vi' ? "Đơn" : "Transaction"}}
childComponent={POSTransaction} show={showModalTransaction}
carts={carts} fullscreen={true} backdrop="static"
setShowForm={setShowModalTransaction} setShow={setShowModalTransaction}
handleClose={() => setShowModalTransaction(false)}
baskets={baskets} setBaskets={setBaskets} classNameCustom="modal-custom"
config={config} userInfo={userInfo} shift_id={shift?.id}
member={member} setMember={setMember} setSaleNumber={setSaleNumber}
/>
Select Basket}
childComponent={ManageBasketDraft} show={showModalSelectBasket}
carts={carts} fullscreen={true} backdrop="static" config={config}
setShowForm={setShowModalSelectBasket} setShow={setShowModalSelectBasket}
handleClose={() => setShowModalSelectBasket(false)}
getBasketsDraft={() => jTmpBasket} // Sử dụng dữ liệu đã memo
setBaskets={setBaskets} showCloseButton={false} classNameCustom="modal-custom"
/>
Manage Return}
childComponent={ManageReturns} show={showModalReturn}
fullscreen={true} backdrop="static" config={config}
setShowForm={setShowModalReturn} setShow={setShowModalReturn}
handleClose={() => setShowModalReturn(false)}
setBaskets={setBaskets} showCloseButton={false} returnNo={returnNo} classNameCustom="modal-custom"
isCashier={isCashier}
/>
Confirm Return}
childComponent={ConfirmReturns} show={showModalConfirmReturn}
fullscreen={true} backdrop="static" config={config}
setShowForm={setShowModalConfirmReturn} setShow={setShowModalConfirmReturn}
handleClose={() => setShowModalConfirmReturn(false)}
setBaskets={setBaskets} carts={carts} setCarts={setCarts}
transactionReturn={transactionReturn} setTransactionReturn={setTransactionReturn}
isCashier={isCashier} showCloseButton={false} returnNo={returnNo} classNameCustom="modal-custom"
/>
Scan barcode}
childComponent={ScanBarcodeOrQRCode} show={showModalScanBarcode}
fullscreen={true} backdrop="static" config={config} isMobile={isMobile}
setShowForm={setShowModalScanBarcode} setShow={setShowModalScanBarcode}
handleClose={() => setShowModalScanBarcode(false)}
showCloseButton={false} classNameCustom="modal-custom"
barcode={barcode} setBarcode={setBarcode}
/>
{config?.lang === 'vi' ? "Quản lý ca" : "Shift Management"}}
childComponent={ManageShiftClosing} show={showShiftModal}
fullscreen={true} backdrop="static" config={config} shift={shift} userInfo={userInfo}
setShowForm={setShowShiftModal} setShow={setShowShiftModal}
handleClose={() => setShowShiftModal(false)}
showCloseButton={false} classNameCustom="modal-custom"
/>
>
);
};