diff options
| author | Mqdd <ahmadmiqdad27@gmail.com> | 2025-12-15 09:58:17 +0700 |
|---|---|---|
| committer | Mqdd <ahmadmiqdad27@gmail.com> | 2025-12-15 09:58:17 +0700 |
| commit | c02762ae157d167ae27bf34f6014abcb49deecc1 (patch) | |
| tree | cab1792aa0ae6b6a897d623b13b84936672a348a /app | |
| parent | 1d4556c57e7e43d3ccb07b016f0d38882a4f32dd (diff) | |
<Miqdad> scanner using zxing
Diffstat (limited to 'app')
| -rw-r--r-- | app/lib/camera/component/scannerBarcode.tsx | 95 |
1 files changed, 65 insertions, 30 deletions
diff --git a/app/lib/camera/component/scannerBarcode.tsx b/app/lib/camera/component/scannerBarcode.tsx index 6319a84..18f953d 100644 --- a/app/lib/camera/component/scannerBarcode.tsx +++ b/app/lib/camera/component/scannerBarcode.tsx @@ -1,52 +1,87 @@ import { QrCode2 } from "@mui/icons-material"; import { Button, TextField } from "@mui/material"; -import dynamic from "next/dynamic"; -import React, { useState } from "react"; +import { BrowserMultiFormatReader } from "@zxing/browser"; +import React, { useEffect, useRef, useState } from "react"; import useCameraStore from "../hooks/useCameraStore"; -const BarcodeScannerComponent = dynamic( - () => import("react-qr-barcode-scanner"), - { ssr: false } -); const BarcodeScanner: React.FC = () => { const { barcode, setBarcode } = useCameraStore(); const [isCameraActive, setIsCameraActive] = useState(false); + const videoRef = useRef<HTMLVideoElement>(null); + const inputRef = useRef<HTMLInputElement>(null); + + const stopCamera = () => { + const video = videoRef.current; + if (!video || !video.srcObject) return; + + const stream = video.srcObject as MediaStream; + stream.getTracks().forEach((track) => track.stop()); + video.srcObject = null; + }; + + useEffect(() => { + if (!isCameraActive || !videoRef.current) return; + + const reader = new BrowserMultiFormatReader(); + let cancelled = false; + + reader + .decodeOnceFromVideoDevice(undefined, videoRef.current) + .then((result) => { + if (cancelled) return; + + setBarcode(result.getText()); + setIsCameraActive(false); + stopCamera(); + + // 🔥 fokus ke input biar bisa diedit + setTimeout(() => { + inputRef.current?.focus(); + }, 100); + }) + .catch(() => { + // ignore cancel / permission stop + }); + + return () => { + cancelled = true; + stopCamera(); + }; + }, [isCameraActive, setBarcode]); + return ( <div> - <Button - variant="outlined" - onClick={() => setIsCameraActive(!isCameraActive)} - startIcon={<QrCode2 />} - color="error" - className="mb-2" - > - {isCameraActive ? "Cancel" : "Scan Code"} - </Button> + <Button + variant="outlined" + color="error" + startIcon={<QrCode2 />} + className="mb-2" + onClick={() => { + if (isCameraActive) stopCamera(); + setIsCameraActive((v) => !v); + }} + > + {isCameraActive ? "Cancel" : "Scan Code"} + </Button> {isCameraActive && ( - <BarcodeScannerComponent - width={500} // Tingkatkan ukuran untuk memperjelas gambar - height={300} - onUpdate={(err, result) => { - if (result) { - setBarcode(result.getText()); - setIsCameraActive(false); - } - }} - /> + <> + <p className="text-xs text-gray-500 mb-1"> + Arahkan barcode ke tengah layar + </p> + <video ref={videoRef} className="w-full h-[300px] border rounded" /> + </> )} <div className="mt-4"> <TextField fullWidth label="Detected Picking Code" - id="outlined-basic" value={barcode} - onChange={ (e) => setBarcode(e.target.value) } - InputLabelProps={{ - shrink: true, // Label akan selalu berada di atas (outline) - }} + inputRef={inputRef} + onChange={(e) => setBarcode(e.target.value)} + InputLabelProps={{ shrink: true }} /> </div> </div> |
