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" /> ); };