2025-10-27 17:41:24 -06:00
|
|
|
// components/BarcodeScanner.tsx
|
2025-10-23 14:12:18 -06:00
|
|
|
"use client";
|
|
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
import React, { useEffect, useRef } from "react";
|
|
|
|
|
import Quagga from "@ericblade/quagga2";
|
2025-10-23 14:12:18 -06:00
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
// Tipos mínimos para el resultado de Quagga
|
|
|
|
|
interface CodeResult {
|
|
|
|
|
code: string;
|
|
|
|
|
format: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface QuaggaResult {
|
|
|
|
|
codeResult: CodeResult | null;
|
|
|
|
|
}
|
2025-10-23 14:12:18 -06:00
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
// Tipos para la configuración (opcional, pero mejora legibilidad)
|
|
|
|
|
interface QuaggaConfig {
|
|
|
|
|
inputStream: {
|
|
|
|
|
name: string;
|
|
|
|
|
type: string;
|
|
|
|
|
target: HTMLElement;
|
|
|
|
|
constraints: MediaTrackConstraints;
|
|
|
|
|
};
|
|
|
|
|
decoder: {
|
|
|
|
|
readers: string[];
|
|
|
|
|
};
|
|
|
|
|
locate: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface BarcodeScannerProps {
|
|
|
|
|
onScan: (code: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function BarcodeScanner({ onScan }: BarcodeScannerProps) {
|
|
|
|
|
const scannerRef = useRef<HTMLDivElement>(null);
|
2025-10-23 14:12:18 -06:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
2025-10-27 17:41:24 -06:00
|
|
|
if (!scannerRef.current) return;
|
|
|
|
|
|
|
|
|
|
// ✅ Sin `any`: usamos un objeto literal tipado implícitamente
|
|
|
|
|
const config: QuaggaConfig = {
|
|
|
|
|
inputStream: {
|
|
|
|
|
name: "Live",
|
|
|
|
|
type: "LiveStream",
|
|
|
|
|
target: scannerRef.current,
|
|
|
|
|
constraints: {
|
|
|
|
|
width: 640,
|
|
|
|
|
height: 480,
|
|
|
|
|
facingMode: "environment",
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
decoder: {
|
|
|
|
|
readers: [
|
|
|
|
|
"code_128_reader",
|
|
|
|
|
"ean_reader",
|
|
|
|
|
"ean_8_reader",
|
|
|
|
|
"code_39_reader",
|
|
|
|
|
"upc_reader",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
locate: true,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// El callback de error: tipamos `err` como `unknown`
|
|
|
|
|
const initCallback = (err: unknown) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
console.error("Error al iniciar el escáner:", err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Quagga.start();
|
|
|
|
|
};
|
2025-10-23 14:12:18 -06:00
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
Quagga.init(config as unknown as Record<string, unknown>, initCallback);
|
2025-10-23 14:12:18 -06:00
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
// Handler de detección: `data` es `unknown`, lo validamos
|
|
|
|
|
const handleDetected = (data: unknown) => {
|
|
|
|
|
// Validación segura
|
|
|
|
|
if (
|
|
|
|
|
data != null &&
|
|
|
|
|
typeof data === "object" &&
|
|
|
|
|
"codeResult" in data &&
|
|
|
|
|
data.codeResult != null &&
|
|
|
|
|
typeof data.codeResult === "object" &&
|
|
|
|
|
"code" in data.codeResult &&
|
|
|
|
|
typeof data.codeResult.code === "string"
|
|
|
|
|
) {
|
|
|
|
|
const code = data.codeResult.code.trim();
|
|
|
|
|
if (code !== "") {
|
|
|
|
|
onScan(code);
|
|
|
|
|
Quagga.stop(); // Opcional
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-10-23 14:12:18 -06:00
|
|
|
|
2025-10-27 17:41:24 -06:00
|
|
|
Quagga.onDetected(handleDetected);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
Quagga.offDetected(handleDetected);
|
|
|
|
|
Quagga.stop();
|
|
|
|
|
};
|
|
|
|
|
}, [onScan]);
|
2025-10-23 14:12:18 -06:00
|
|
|
|
|
|
|
|
return (
|
2025-10-27 17:41:24 -06:00
|
|
|
<div
|
|
|
|
|
ref={scannerRef}
|
2025-10-23 14:12:18 -06:00
|
|
|
style={{
|
|
|
|
|
width: "100%",
|
2025-10-27 17:41:24 -06:00
|
|
|
maxWidth: "500px",
|
|
|
|
|
height: "300px",
|
|
|
|
|
borderRadius: "12px",
|
|
|
|
|
overflow: "hidden",
|
|
|
|
|
border: "2px solid #0056b3",
|
2025-10-23 14:12:18 -06:00
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
2025-10-27 17:41:24 -06:00
|
|
|
}
|