From c89465ae0f812e7d98a6470bdfe684985d2a3585 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Tue, 10 Jun 2025 18:56:02 +0800 Subject: [PATCH] Add Qr Code Scanner --- src/components/PoDetail/PoInputGrid.tsx | 4 +- src/components/PoDetail/PutawayForm.tsx | 28 +++++- src/components/PoDetail/QrModal.tsx | 83 +++++++++++----- .../QrCodeScannerProvider.tsx | 99 +++++++++++++++++++ 4 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index be3498d..ccb565a 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -54,8 +54,7 @@ import QrCodeIcon from "@mui/icons-material/QrCode"; import { downloadFile } from "@/app/utils/commonUtil"; import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { fetchQcResult } from "@/app/api/qc/actions"; -import PoQcStockInModal from "./PoQcStockInModal"; -import { notifyActionSuccess } from "../Toast/Toast"; +import PoQcStockInModal from "./PoQcStockInModal" import DoDisturbIcon from "@mui/icons-material/DoDisturb"; interface ResultWithId { @@ -181,7 +180,6 @@ function PoInputGrid({ ) as StockInLine[] ); setBtnIsLoading(false); - notifyActionSuccess(); // do post directly to test // openStartModal(); }, 200); diff --git a/src/components/PoDetail/PutawayForm.tsx b/src/components/PoDetail/PutawayForm.tsx index 95354e5..2ac8e12 100644 --- a/src/components/PoDetail/PutawayForm.tsx +++ b/src/components/PoDetail/PutawayForm.tsx @@ -44,6 +44,7 @@ import ReactQrCodeScanner, { ScannerConfig, } from "../ReactQrCodeScanner/ReactQrCodeScanner"; import { QrCodeInfo } from "@/app/api/qrcode"; +import { useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; interface Props { itemDetail: StockInLine; @@ -210,6 +211,30 @@ const PutawayForm: React.FC = ({ itemDetail, warehouse, disabled }) => { [onCloseScanner] ); + // QR Code Scanner + const scanner = useQcCodeScanner() + useEffect(() => { + if (isOpenScanner) { + scanner.startScan() + } else if (!isOpenScanner) { + scanner.stopScan() + } + }, [isOpenScanner]) + + useEffect(() => { + if (scanner.values.length > 0) { + console.log(scanner.values[0]) + const data: QrCodeInfo = JSON.parse(scanner.values[0]); + console.log(data); + if (data.warehouseId) { + console.log(data.warehouseId); + setWarehouseId(data.warehouseId); + onCloseScanner(); + } + scanner.resetScan() + } + }, [scanner.values]) + useEffect(() => { setValue("status", "completed"); }, []); @@ -408,7 +433,8 @@ const PutawayForm: React.FC = ({ itemDetail, warehouse, disabled }) => { - + {t("Please scan warehouse qr code.")} + {/* */} diff --git a/src/components/PoDetail/QrModal.tsx b/src/components/PoDetail/QrModal.tsx index 5cfc416..bd72ab8 100644 --- a/src/components/PoDetail/QrModal.tsx +++ b/src/components/PoDetail/QrModal.tsx @@ -19,6 +19,7 @@ import { QrCodeInfo } from "@/app/api/qrcode"; import { Check } from "@mui/icons-material"; import { useTranslation } from "react-i18next"; import { useSearchParams } from "next/navigation"; +import { useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; interface Props extends Omit { warehouse: WarehouseResult[]; @@ -72,6 +73,30 @@ const QrModal: React.FC = ({ open, onClose, warehouse }) => { [] ); + // QR Code Scanner + const scanner = useQcCodeScanner() + useEffect(() => { + if (open && !scanner.isScanning) { + scanner.startScan() + } else if (!open && scanner.isScanning) { + scanner.stopScan() + } + }, [open]) + + useEffect(() => { + if (scanner.values.length > 0 && !Boolean(itemDetail)) { + console.log(scanner.values[0]) + const data: QrCodeInfo = JSON.parse(scanner.values[0]); + console.log(data); + if (data.stockInLineId) { + console.log("still got in"); + console.log(data.stockInLineId); + setStockInLineId(data.stockInLineId); + } + scanner.resetScan() + } + }, [scanner.values]) + const [itemDetail, setItemDetail] = useState(); const [disabledSubmit, setDisabledSubmit] = useState(false); const [unavailableText, setUnavailableText] = useState(undefined) @@ -115,13 +140,13 @@ const QrModal: React.FC = ({ open, onClose, warehouse }) => { //////////////////////// modify this mess later ////////////////////// const args = { - id: itemDetail?.id, - purchaseOrderId: parseInt(params.get("id")!!), - purchaseOrderLineId: itemDetail?.purchaseOrderLineId, - itemId: itemDetail?.itemId, - acceptedQty: data.acceptedQty, - warehouseId: data.warehouseId, - // ...data, + id: itemDetail?.id, + purchaseOrderId: parseInt(params.get("id")!!), + purchaseOrderLineId: itemDetail?.purchaseOrderLineId, + itemId: itemDetail?.itemId, + acceptedQty: data.acceptedQty, + warehouseId: data.warehouseId, + // ...data, // productionDate: productionDate, } as StockInLineEntry & ModalFormInput; ////////////////////////////////////////////////////////////////////// @@ -160,28 +185,32 @@ const QrModal: React.FC = ({ open, onClose, warehouse }) => { > - {itemDetail != undefined ? ( - unavailableText != undefined ? {unavailableText} - : ( - <> - - - - - + { + itemDetail != undefined ? ( + unavailableText != undefined ? {unavailableText} + : ( + <> + + + + + + ) ) - ) : ( - - )} + : ( + // + {t("Will start binding procedure after scanning item qr code.")} + ) + } diff --git a/src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx b/src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx new file mode 100644 index 0000000..8b613c2 --- /dev/null +++ b/src/components/QrCodeScannerProvider/QrCodeScannerProvider.tsx @@ -0,0 +1,99 @@ +"use client"; +import { ReactNode, createContext, useCallback, useContext, useEffect, useState } from "react"; + +interface QcCodeScanner { + values: string[]; + isScanning: boolean; + startScan: () => void; + stopScan: () => void; + resetScan: () => void; +} + +interface QrCodeScannerProviderProps { + children: ReactNode; +} + +export const QcCodeScannerContext = createContext(undefined) + +const QrCodeScannerProvider: React.FC = ({ children }) => { + const [qcCodeScannerValues, setQrCodeScannerValues] = useState([]); + const [isScanning, setIsScanning] = useState(false); + const [keys, setKeys] = useState([]); + const [leftCurlyBraceCount, setLeftCurlyBraceCount] = useState(0); + const [rightCurlyBraceCount, setRightCurlyBraceCount] = useState(0); + const resetQrCodeScanner = useCallback(() => { + setQrCodeScannerValues(() => []) + }, []) + + const startQrCodeScanner = useCallback(() => { + resetQrCodeScanner() + setIsScanning(() => true) + }, []) + + const endQrCodeScanner = useCallback(() => { + setIsScanning(() => false) + }, []) + + + // Check the KeyDown + useEffect(() => { + if (isScanning) { + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key.length === 1) { + setKeys((key) => [...key, event.key]) + } + + if (event.key === "{") { + setLeftCurlyBraceCount((count) => count + 1) + } else if (event.key === "}") { + setRightCurlyBraceCount((count) => count + 1) + } + } + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }; + }, [isScanning]) + + // Update Qr Code Scanner Values + useEffect(() => { + if (leftCurlyBraceCount !== 0 && rightCurlyBraceCount !== 0 && leftCurlyBraceCount === rightCurlyBraceCount) { + const startBrace = keys.indexOf('{'); + const endBrace = keys.lastIndexOf('}'); + setQrCodeScannerValues((value) => [...value, keys.join("").substring(startBrace, endBrace + 1)]) + console.log(keys) + console.log(qcCodeScannerValues) + + // reset + setKeys(() => []) + setLeftCurlyBraceCount(() => 0) + setRightCurlyBraceCount(() => 0) + } + }, [keys, leftCurlyBraceCount, rightCurlyBraceCount]) + + return ( + + {children} + + ) +} + +export const useQcCodeScanner = (): QcCodeScanner => { + const context = useContext(QcCodeScannerContext); + if (!context) { + throw new Error('useQcCodeScanner must be used within a QcCodeScannerProvider'); + } + return context; +}; + +export default QrCodeScannerProvider; \ No newline at end of file