Merge pull request 'mobile-apk-and-web-qrscan' (#159) from mobile-apk-and-web-qrscan into main
Reviewed-on: Pozomind/pozo-retail-app#159
This commit is contained in:
commit
8d19d901b9
|
|
@ -81,6 +81,7 @@ import { Global_PromoCodeOffers } from '../../../../Features/Offer/Offer.js';
|
|||
import { validateOffers } from '../BSItemCards/ValidateOffer.jsx';
|
||||
import { useSaleswiseOfferWatcher } from '../BSItemCards/SalesWiseOffer.jsx';
|
||||
import ExpireConfirmModal from '../BookingFunctionality/ExpireConfirmModal.jsx';
|
||||
import BarCodeScan from '../../../../Services/BarCodeScan.jsx';
|
||||
|
||||
export default function BSC1Search(props) {
|
||||
const dispatch = useDispatch();
|
||||
|
|
@ -4567,10 +4568,15 @@ export default function BSC1Search(props) {
|
|||
</Form.Item>
|
||||
</Form>
|
||||
{isMobile && screenWidth <= 768 && (
|
||||
<BiBarcodeReader
|
||||
className="BSC1Search-barcodeicon"
|
||||
onClick={() => handleQrcode()}
|
||||
// onClick={() => OnfocusTrue()}
|
||||
// <BiBarcodeReader
|
||||
// className="BSC1Search-barcodeicon"
|
||||
// onClick={() => handleQrcode()}
|
||||
// // onClick={() => OnfocusTrue()}
|
||||
// />
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
ProductNameOnChange(value, true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
|
@ -4821,7 +4827,7 @@ export default function BSC1Search(props) {
|
|||
</div>
|
||||
</div>
|
||||
</DefaultModal>
|
||||
{isMobile && screenWidth <= 768 && (
|
||||
{/* {isMobile && screenWidth <= 768 && (
|
||||
<DefaultModal
|
||||
title="Barcode Reader"
|
||||
width={800}
|
||||
|
|
@ -4839,7 +4845,7 @@ export default function BSC1Search(props) {
|
|||
/>
|
||||
)}
|
||||
</DefaultModal>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import './Verfiedproduct.scss';
|
|||
import { useSelector } from 'react-redux';
|
||||
import { ApplicationPreferences } from '../../../../../Features/BrachLogin/BranchLogin.js';
|
||||
import { changeOfferAppliedProductsForMembership } from '../../../../../Features/Offer/Offernew/BookingOffernew.js';
|
||||
import BarCodeScan from '../../../../../Services/BarCodeScan.jsx';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
|
||||
|
||||
const VerfiedProducts = ({ drawerOpen = false, setDrawerOpen = () => {} }) => {
|
||||
|
|
@ -303,10 +305,16 @@ const VerfiedProducts = ({ drawerOpen = false, setDrawerOpen = () => {} }) => {
|
|||
>
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<button onClick={() => setShowScanner((s) => !s)}>
|
||||
{!isMobile ?
|
||||
(<button onClick={() => setShowScanner((s) => !s)}>
|
||||
{showScanner ? 'Stop Camera' : 'Scan with Camera'}
|
||||
</button>
|
||||
</button>)
|
||||
: (<BarCodeScan
|
||||
onScan={(value) => {
|
||||
setScanValue(value);
|
||||
fetchProductsByOrderId(value);
|
||||
}}
|
||||
/>)}
|
||||
</div>
|
||||
|
||||
{message && <div className="SelfBooking-message">{message}</div>}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import BarcodeScanner from './BarcodeScanner';
|
|||
import { isMobile } from 'react-device-detect';
|
||||
import { BiBarcodeReader } from 'react-icons/bi';
|
||||
import TooltipWrapper from '../../../../Components/Tooltip/Tooltip';
|
||||
import BarCodeScan from '../../../../Services/BarCodeScan.jsx';
|
||||
|
||||
const BSNavBarComboSearch = (props) => {
|
||||
|
||||
|
|
@ -1197,28 +1198,28 @@ const BSNavBarComboSearch = (props) => {
|
|||
: [];
|
||||
// });
|
||||
};
|
||||
const stopScannerFromParent = () => {
|
||||
if (scannerRef.current) {
|
||||
scannerRef.current.stopScanner(); // Call stopScanner from the child component
|
||||
}
|
||||
};
|
||||
// const stopScannerFromParent = () => {
|
||||
// if (scannerRef.current) {
|
||||
// scannerRef.current.stopScanner(); // Call stopScanner from the child component
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleQrcode = () => {
|
||||
setOpenScanner((prev) => !prev);
|
||||
if (!openScanner && scannerRef.current) {
|
||||
scannerRef.current.reinitializeCamera(); // Call the function to reinitialize the camera
|
||||
}
|
||||
};
|
||||
// const handleQrcode = () => {
|
||||
// setOpenScanner((prev) => !prev);
|
||||
// if (!openScanner && scannerRef.current) {
|
||||
// scannerRef.current.reinitializeCamera(); // Call the function to reinitialize the camera
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleQrData = (decodedText, err) => {
|
||||
onChangeprodFunction(decodedText);
|
||||
setOpenScanner(false); // Close the modal after scanning
|
||||
stopScannerFromParent();
|
||||
if (err && err?.error) {
|
||||
setMessageData(err?.error);
|
||||
setMessageType('error');
|
||||
}
|
||||
};
|
||||
// const handleQrData = (decodedText, err) => {
|
||||
// onChangeprodFunction(decodedText);
|
||||
// setOpenScanner(false); // Close the modal after scanning
|
||||
// stopScannerFromParent();
|
||||
// if (err && err?.error) {
|
||||
// setMessageData(err?.error);
|
||||
// setMessageType('error');
|
||||
// }
|
||||
// };
|
||||
return (
|
||||
<div className="L">
|
||||
<Messages
|
||||
|
|
@ -1289,10 +1290,14 @@ const BSNavBarComboSearch = (props) => {
|
|||
/>
|
||||
</div>
|
||||
{isMobile && screenWidth <= 768 ? (
|
||||
<BiBarcodeReader
|
||||
className="search-icon"
|
||||
onClick={() => handleQrcode()}
|
||||
/>
|
||||
// <BiBarcodeReader
|
||||
// className="search-icon"
|
||||
// onClick={() => handleQrcode()}
|
||||
// />
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
onChangeprodFunction(value);
|
||||
}} />
|
||||
) : (
|
||||
<IoSearchOutline className="search-icon" />
|
||||
)}
|
||||
|
|
@ -1395,7 +1400,7 @@ const BSNavBarComboSearch = (props) => {
|
|||
</div>
|
||||
</Modal>
|
||||
)}
|
||||
{isMobile && screenWidth <= 768 && (
|
||||
{/* {isMobile && screenWidth <= 768 && (
|
||||
<DefaultModal
|
||||
title="Barcode Reader"
|
||||
width={800}
|
||||
|
|
@ -1413,7 +1418,7 @@ const BSNavBarComboSearch = (props) => {
|
|||
/>
|
||||
)}
|
||||
</DefaultModal>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import { BiBarcodeReader } from 'react-icons/bi';
|
|||
import { ApplicationPreferences } from '../../../../Features/BrachLogin/BranchLogin.js';
|
||||
import { useDateStore } from '../../../../Features/BookingScreen/BookingData/DateStore.js';
|
||||
import { Messages } from '../../../../../ownLib/my-ui-lib.js';
|
||||
import BarCodeScan from '../../../../Services/BarCodeScan.jsx';
|
||||
|
||||
const BSNavBarSearch = (props) => {
|
||||
const searchInputRef = useRef(null);
|
||||
|
|
@ -362,28 +363,28 @@ const BSNavBarSearch = (props) => {
|
|||
|
||||
await dispatch(getCardDataWithoutSub(data));
|
||||
};
|
||||
const stopScannerFromParent = () => {
|
||||
if (scannerRef?.current) {
|
||||
scannerRef?.current?.stopScanner(); // Call stopScanner from the child component
|
||||
}
|
||||
};
|
||||
// const stopScannerFromParent = () => {
|
||||
// if (scannerRef?.current) {
|
||||
// scannerRef?.current?.stopScanner(); // Call stopScanner from the child component
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleQrcode = () => {
|
||||
setOpenScanner((prev) => !prev);
|
||||
if (!openScanner && scannerRef?.current) {
|
||||
scannerRef?.current?.reinitializeCamera(); // Call the function to reinitialize the camera
|
||||
}
|
||||
};
|
||||
// const handleQrcode = () => {
|
||||
// setOpenScanner((prev) => !prev);
|
||||
// if (!openScanner && scannerRef?.current) {
|
||||
// scannerRef?.current?.reinitializeCamera(); // Call the function to reinitialize the camera
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleQrData = async (value, err) => {
|
||||
if (err && err?.error) {
|
||||
setMessageData(err?.error);
|
||||
setMessageType('error');
|
||||
}
|
||||
await searchdata(value);
|
||||
setOpenScanner(false);
|
||||
stopScannerFromParent();
|
||||
};
|
||||
// const handleQrData = async (value, err) => {
|
||||
// if (err && err?.error) {
|
||||
// setMessageData(err?.error);
|
||||
// setMessageType('error');
|
||||
// }
|
||||
// await searchdata(value);
|
||||
// setOpenScanner(false);
|
||||
// stopScannerFromParent();
|
||||
// };
|
||||
|
||||
const onComplete = useCallback(() => {
|
||||
setMessageData(null);
|
||||
|
|
@ -421,9 +422,14 @@ const BSNavBarSearch = (props) => {
|
|||
disabled={props.OrderType === 'Failed' ? true : false}
|
||||
/>
|
||||
{isMobile && screenWidth <= 768 ? (
|
||||
<BiBarcodeReader
|
||||
className="search-icon"
|
||||
onClick={() => handleQrcode()}
|
||||
// <BiBarcodeReader
|
||||
// className="search-icon"
|
||||
// onClick={() => handleQrcode()}
|
||||
// />
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
searchdata(value);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<IoSearchOutline className="search-icon" />
|
||||
|
|
@ -435,7 +441,7 @@ const BSNavBarSearch = (props) => {
|
|||
</div>} */}
|
||||
</div>
|
||||
</div>
|
||||
{isMobile && screenWidth <= 768 && (
|
||||
{/* {isMobile && screenWidth <= 768 && (
|
||||
<DefaultModal
|
||||
title="Barcode Reader"
|
||||
width={800}
|
||||
|
|
@ -454,7 +460,7 @@ const BSNavBarSearch = (props) => {
|
|||
/>
|
||||
)}
|
||||
</DefaultModal>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ import Search from "../../../../Components/Forms/Search";
|
|||
import VoiceToTextItemCard from "../../../VoiceToText/VoiceToTextItemCard";
|
||||
import { ApplicationPreferences } from "../../../../Features/BrachLogin/BranchLogin";
|
||||
import { useDateStore } from "../../../../Features/BookingScreen/BookingData/DateStore";
|
||||
import BarCodeScan from "../../../../Services/BarCodeScan";
|
||||
import { isMobile } from "react-device-detect";
|
||||
|
||||
const BSNavbarSearchStandard = ({ inputRef }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
|
@ -147,6 +149,9 @@ const BSNavbarSearchStandard = ({ inputRef }) => {
|
|||
const handleSearch = (e) => {
|
||||
dispatch(changeSearchedData(e?.target?.value));
|
||||
};
|
||||
const handleScan = (e) => {
|
||||
dispatch(changeSearchedData(e));
|
||||
};
|
||||
const HandleVoiceSearch = () => {
|
||||
setVoiceSearch(!voiceSearch);
|
||||
};
|
||||
|
|
@ -159,6 +164,13 @@ const BSNavbarSearchStandard = ({ inputRef }) => {
|
|||
ref={inputRef}
|
||||
value={ProdName}
|
||||
/>
|
||||
{isMobile && screenWidth <= 768 && (
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
handleScan(value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import { PrintStyleFunction } from '../paymentpdfPage/PrintStyleFunction.js';
|
|||
import { PurchaseOrderMobilePrint } from './PurchsasePrintStyle/PurchaseOrderMobilePrint.js';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
import { CiEdit } from 'react-icons/ci';
|
||||
import BarCodeScan from '../../Services/BarCodeScan.jsx';
|
||||
|
||||
const subDirectory = import.meta.env.BASE_URL;
|
||||
const stylesMap = {
|
||||
|
|
@ -1628,6 +1629,7 @@ const PurchaseOrder = () => {
|
|||
}}
|
||||
></AutoComplete>
|
||||
</FloatLabel>
|
||||
{!isMobile && (
|
||||
<Tooltip title={'BarCode/QrCode'} placement={'top'}>
|
||||
<BsUpcScan
|
||||
strokeWidth={0.5}
|
||||
|
|
@ -1641,6 +1643,16 @@ const PurchaseOrder = () => {
|
|||
onClick={handleScanSearch}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isMobile && (
|
||||
<div className='IsmoBileBarcode'>
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
debouncedScannerSearch(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Tooltip
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ import AddAllProductsButton from './AddAllProductGrid.jsx';
|
|||
import SettingsIconWithModal from './SettingsIconWithModal.jsx';
|
||||
import VirtualizedTable from './VirtualizedTable.jsx';
|
||||
import { useDebounce } from './UseDebounce.jsx';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
import BarCodeScan from '../../Services/BarCodeScan.jsx';
|
||||
|
||||
const subDirectory = import.meta.env.BASE_URL;
|
||||
const EditableContext = React.createContext(null);
|
||||
|
|
@ -3813,6 +3815,28 @@ const StockForm = ({ formType }) => {
|
|||
style={{ width: '250px' }}
|
||||
></AutoComplete>
|
||||
</FloatLabel>
|
||||
{/* <Tooltip title={'BarCode/QrCode'} placement={'top'}>
|
||||
<BsUpcScan
|
||||
strokeWidth={0.5}
|
||||
color="#000"
|
||||
style={{
|
||||
fontSize: '16px',
|
||||
color: scanner ? '#52c41a' : '#888',
|
||||
fontWeight: '600',
|
||||
}}
|
||||
className="product-scan-Icon"
|
||||
onClick={handleScanSearch}
|
||||
/>
|
||||
</Tooltip> */}
|
||||
{isMobile ? (
|
||||
<div className="IsmoBileBarcode">
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
debouncedScannerSearch(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Tooltip title={'BarCode/QrCode'} placement={'top'}>
|
||||
<BsUpcScan
|
||||
strokeWidth={0.5}
|
||||
|
|
@ -3826,6 +3850,7 @@ const StockForm = ({ formType }) => {
|
|||
onClick={handleScanSearch}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ import { getCompBranchData } from '../../Features/BrachLogin/BranchLogin.js';
|
|||
import { changeBreadCrumb } from '../../Features/AppPage/CenterPage.js';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { postRequestStock } from '../../Features/RequestStock/RequestStock.js';
|
||||
import BarCodeScan from '../../Services/BarCodeScan.jsx';
|
||||
import { isMobile } from 'react-device-detect';
|
||||
const subDirectory = import.meta.env.BASE_URL;
|
||||
|
||||
function debounce(func, wait) {
|
||||
|
|
@ -516,13 +518,22 @@ const CreateTransfer = () => {
|
|||
disabled={isProcessing}
|
||||
/>
|
||||
</FloatLabel>
|
||||
|
||||
{isMobile ? (
|
||||
<div className="IsmoBileBarcode">
|
||||
<BarCodeScan
|
||||
onScan={(value) => {
|
||||
debouncedScannerSearch(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Tooltip title="BarCode / QR Code">
|
||||
<BsUpcScan
|
||||
className={`product-scan-icon ${scanner ? 'active' : ''}`}
|
||||
onClick={handleScannerToggle}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,419 @@
|
|||
// import React, { useRef, useState, useEffect } from "react";
|
||||
// import { BarcodeScanner } from "@capacitor-mlkit/barcode-scanning";
|
||||
// import { BsUpcScan } from "react-icons/bs";
|
||||
// import { BrowserMultiFormatReader } from "@zxing/library";
|
||||
// import { Capacitor } from "@capacitor/core";
|
||||
// import { DefaultModal } from "../Components/Modal/DefaultModal";
|
||||
|
||||
// export default function BarCodeScan({ onScan }) {
|
||||
// const isMobileApp = Capacitor.getPlatform() !== "web";
|
||||
// const [scanning, setScanning] = useState(false);
|
||||
// const [modalOpen, setModalOpen] = useState(false);
|
||||
// const videoRef = useRef(null);
|
||||
// const codeReaderRef = useRef(null);
|
||||
// const streamRef = useRef(null);
|
||||
|
||||
// const startScan = async () => {
|
||||
// if (scanning) return;
|
||||
// setScanning(true);
|
||||
|
||||
// if (isMobileApp) {
|
||||
// // Mobile: MLKit scanner
|
||||
// try {
|
||||
// const perm = await BarcodeScanner.requestPermissions();
|
||||
// if (perm.camera !== "granted") {
|
||||
// alert("Camera permission not granted");
|
||||
// setScanning(false);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const { barcodes } = await BarcodeScanner.scan();
|
||||
// if (barcodes && barcodes.length > 0) {
|
||||
// onScan(barcodes[0].rawValue);
|
||||
// }
|
||||
// } catch (err) {
|
||||
// alert("Error scanning barcode: " + err.message);
|
||||
// } finally {
|
||||
// setScanning(false);
|
||||
// }
|
||||
// } else {
|
||||
// // Web: open modal and start camera
|
||||
// setModalOpen(true);
|
||||
// setTimeout(() => startWebScan(), 50); // wait for modal DOM
|
||||
// }
|
||||
// };
|
||||
|
||||
// const startWebScan = async () => {
|
||||
// try {
|
||||
// codeReaderRef.current = new BrowserMultiFormatReader();
|
||||
// streamRef.current = await navigator.mediaDevices.getUserMedia({
|
||||
// video: { facingMode: "environment" },
|
||||
// });
|
||||
// if (videoRef.current) {
|
||||
// videoRef.current.srcObject = streamRef.current;
|
||||
// videoRef.current.play();
|
||||
// }
|
||||
|
||||
// codeReaderRef.current.decodeFromVideoDevice(
|
||||
// null,
|
||||
// videoRef.current,
|
||||
// (result) => {
|
||||
// if (result) {
|
||||
// onScan(result.getText());
|
||||
// stopWebScan();
|
||||
// setModalOpen(false);
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// } catch (err) {
|
||||
// alert("Error accessing camera: " + err.message);
|
||||
// setScanning(false);
|
||||
// setModalOpen(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const stopWebScan = () => {
|
||||
// if (streamRef.current) {
|
||||
// streamRef.current.getTracks().forEach((track) => track.stop());
|
||||
// streamRef.current = null;
|
||||
// }
|
||||
// if (codeReaderRef.current) {
|
||||
// codeReaderRef.current.reset();
|
||||
// codeReaderRef.current = null;
|
||||
// }
|
||||
// setScanning(false);
|
||||
// };
|
||||
|
||||
// const closeModal = () => {
|
||||
// stopWebScan();
|
||||
// setModalOpen(false);
|
||||
// };
|
||||
|
||||
// // Stop camera if component unmounts
|
||||
// useEffect(() => () => stopWebScan(), []);
|
||||
|
||||
// return (
|
||||
// <div style={{ display: "flex", justifyContent: "center", padding: 20 }}>
|
||||
// {!scanning && (
|
||||
// <BsUpcScan
|
||||
// onClick={startScan}
|
||||
// style={{ fontSize: 32, color: "#007bff", cursor: "pointer" }}
|
||||
// />
|
||||
// )}
|
||||
|
||||
// <DefaultModal
|
||||
// title="Barcode Reader"
|
||||
// width={500}
|
||||
// open={modalOpen}
|
||||
// footer={false}
|
||||
// handleCancel={() => {
|
||||
// closeModal();
|
||||
// }}
|
||||
// >
|
||||
// <div
|
||||
// style={{
|
||||
// display: "flex",
|
||||
// flexDirection: "column",
|
||||
// alignItems: "center",
|
||||
// justifyContent: "center",
|
||||
// }}
|
||||
// >
|
||||
// <video
|
||||
// ref={videoRef}
|
||||
// style={{
|
||||
// width: "100%",
|
||||
// maxWidth: 400,
|
||||
// borderRadius: 8,
|
||||
// border: "2px solid #000",
|
||||
// }}
|
||||
// />
|
||||
|
||||
// <button
|
||||
// onClick={closeModal}
|
||||
// style={{
|
||||
// marginTop: 20,
|
||||
// padding: "10px 20px",
|
||||
// fontSize: 16,
|
||||
// borderRadius: 8,
|
||||
// border: "none",
|
||||
// cursor: "pointer",
|
||||
// }}
|
||||
// >
|
||||
// Close
|
||||
// </button>
|
||||
// </div>
|
||||
// </DefaultModal>
|
||||
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { BarcodeScanner } from "@capacitor-mlkit/barcode-scanning";
|
||||
import { BsUpcScan } from "react-icons/bs";
|
||||
import { Capacitor } from "@capacitor/core";
|
||||
import { DefaultModal } from "../Components/Modal/DefaultModal";
|
||||
import QrScanner from "qr-scanner/qr-scanner.min.js";
|
||||
import {
|
||||
BrowserMultiFormatReader,
|
||||
BarcodeFormat,
|
||||
} from "@zxing/library";
|
||||
|
||||
QrScanner.WORKER_PATH = new URL(
|
||||
"qr-scanner/qr-scanner-worker.min.js",
|
||||
import.meta.url
|
||||
).toString();
|
||||
|
||||
export default function BarCodeScan({ onScan }) {
|
||||
const isMobileApp = Capacitor.getPlatform() !== "web";
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
|
||||
const videoRef = useRef(null);
|
||||
const qrScannerRef = useRef(null);
|
||||
const barcodeReaderRef = useRef(null);
|
||||
|
||||
// -------------------------
|
||||
// START SCAN
|
||||
// -------------------------
|
||||
const startScan = async () => {
|
||||
if (scanning) return;
|
||||
setScanning(true);
|
||||
|
||||
if (isMobileApp) {
|
||||
try {
|
||||
const perm = await BarcodeScanner.requestPermissions();
|
||||
if (perm.camera !== "granted") {
|
||||
alert("Camera permission not granted");
|
||||
setScanning(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const { barcodes } = await BarcodeScanner.scan();
|
||||
if (barcodes?.length > 0) {
|
||||
onScan(barcodes[0].rawValue);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
setScanning(false);
|
||||
}
|
||||
} else {
|
||||
setModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
// START WEB SCAN (HYBRID)
|
||||
// -------------------------
|
||||
const startWebScan = async () => {
|
||||
if (!videoRef.current) return;
|
||||
|
||||
// Start QR scanner (fast)
|
||||
qrScannerRef.current = new QrScanner(
|
||||
videoRef.current,
|
||||
(result) => {
|
||||
stopWebScan();
|
||||
onScan(result.data);
|
||||
setModalOpen(false);
|
||||
},
|
||||
{
|
||||
maxScansPerSecond: 25,
|
||||
}
|
||||
);
|
||||
|
||||
await qrScannerRef.current.start();
|
||||
|
||||
// Start Barcode scanner (ZXing)
|
||||
barcodeReaderRef.current = new BrowserMultiFormatReader(undefined, {
|
||||
possibleFormats: [
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.UPC_A,
|
||||
BarcodeFormat.UPC_E,
|
||||
],
|
||||
});
|
||||
|
||||
barcodeReaderRef.current.timeBetweenDecodingAttempts = 50;
|
||||
|
||||
barcodeReaderRef.current.decodeFromVideoDevice(
|
||||
null,
|
||||
videoRef.current,
|
||||
(result) => {
|
||||
if (result) {
|
||||
stopWebScan();
|
||||
onScan(result.getText());
|
||||
setModalOpen(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const stopWebScan = () => {
|
||||
if (qrScannerRef.current) {
|
||||
qrScannerRef.current.stop();
|
||||
qrScannerRef.current.destroy();
|
||||
qrScannerRef.current = null;
|
||||
}
|
||||
|
||||
if (barcodeReaderRef.current) {
|
||||
barcodeReaderRef.current.reset();
|
||||
barcodeReaderRef.current = null;
|
||||
}
|
||||
|
||||
setScanning(false);
|
||||
};
|
||||
|
||||
const closeModal = () => {
|
||||
stopWebScan();
|
||||
setModalOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (modalOpen) {
|
||||
startWebScan();
|
||||
}
|
||||
}, [modalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => stopWebScan();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div style={{ display: "flex", justifyContent: "center", padding: 20 }}>
|
||||
{!scanning && (
|
||||
<BsUpcScan
|
||||
onClick={startScan}
|
||||
style={{
|
||||
fontSize: 32,
|
||||
color: "#007bff",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <DefaultModal
|
||||
title="QR / Barcode Reader"
|
||||
width={500}
|
||||
open={modalOpen}
|
||||
footer={false}
|
||||
handleCancel={closeModal}
|
||||
>
|
||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||
<video
|
||||
ref={videoRef}
|
||||
style={{
|
||||
width: "100%",
|
||||
maxWidth: 400,
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
marginTop: 20,
|
||||
padding: "10px 20px",
|
||||
fontSize: 16,
|
||||
borderRadius: 8,
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</DefaultModal> */}
|
||||
<DefaultModal
|
||||
title="QR / Barcode Reader"
|
||||
width={500}
|
||||
open={modalOpen}
|
||||
footer={false}
|
||||
handleCancel={closeModal}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
maxWidth: 400,
|
||||
margin: "auto",
|
||||
}}
|
||||
>
|
||||
{/* VIDEO */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
style={{
|
||||
width: "100%",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* DARK OVERLAY */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
background: "rgba(0,0,0,0.5)",
|
||||
borderRadius: 8,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* SCAN BOX */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
width: "70%",
|
||||
height: "60%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
border: "3px solid #00ff00",
|
||||
borderRadius: 12,
|
||||
boxShadow: "0 0 0 2000px rgba(0,0,0,0.4)",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* SCAN LINE */}
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: "15%",
|
||||
width: "70%",
|
||||
height: 2,
|
||||
background: "red",
|
||||
animation: "scanAnimation 2s infinite linear",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
marginTop: 20,
|
||||
padding: "10px 20px",
|
||||
fontSize: 16,
|
||||
borderRadius: 8,
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
{/* Animation */}
|
||||
<style>
|
||||
{`
|
||||
@keyframes scanAnimation {
|
||||
0% { top: 25%; }
|
||||
50% { top: 70%; }
|
||||
100% { top: 25%; }
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</DefaultModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue