From b55eacf856cd834f9146df2714ac1de926fde8c8 Mon Sep 17 00:00:00 2001 From: jorgemike Date: Wed, 2 Apr 2025 12:38:05 -0600 Subject: [PATCH] QR lector --- src/app/staff/page.tsx | 85 +++++++++++++++++++++----------- src/components/modal.tsx | 2 +- src/components/qr-scanner.tsx | 93 +++++++++++++++++++++++------------ 3 files changed, 117 insertions(+), 63 deletions(-) diff --git a/src/app/staff/page.tsx b/src/app/staff/page.tsx index df68c8a..9263d5e 100644 --- a/src/app/staff/page.tsx +++ b/src/app/staff/page.tsx @@ -1,51 +1,76 @@ 'use client'; -import Header from '@/components/layout/header'; -import QrScanner from '@/components/qr-scanner'; -import dynamic from 'next/dynamic'; -import React, { useState } from 'react'; +import Header from "@/components/layout/header"; +import React from "react"; +import dynamic from "next/dynamic"; +import Button from "@/components/button"; + +const Html5QrcodePlugin = dynamic(() => import('@/components/qr-scanner'), { + ssr: false, +}); const Modal = dynamic(() => import('@/components/modal'), { ssr: false, }); export default function Page() { - const [resultado, setResultado] = useState(null); - const [mostrarModal, setMostrarModal] = useState(false); + const [text, setText] = React.useState(""); + const [showModal, setShowModal] = React.useState(false); - const handleScan = (text: string) => { - setResultado(text); - setMostrarModal(true); + const onScanSuccess = (decodedText: string) => { + setText(decodedText); + setShowModal(true); // Muestra el modal al escanear }; - const handleConfirm = () => { - // Aquí haces algo con el QR (guardar, redirigir, etc) - alert('Confirmado: ' + resultado); - setMostrarModal(false); - setResultado(null); // Limpiar para volver a escanear + const onScanError = (errorMessage: string) => { + console.warn("QR Error:", errorMessage); + }; + + const handleCloseModal = () => { + setShowModal(false); }; return ( -
+
-
-
-

Lectura de QRs

- {!resultado && } +
+
+

Lectura de QRs

- setMostrarModal(false)} - size='md' - > -
Resultado del QR
-
{resultado}
- -
+
+ +
+ +

+ {text + ? `Texto escaneado: ${text}` + : "Escanea un código QR para ver el texto aquí."} +

+ + +
Texto escaneado
+

{text}

+ + +
); } diff --git a/src/components/modal.tsx b/src/components/modal.tsx index 704edfd..8071c0e 100644 --- a/src/components/modal.tsx +++ b/src/components/modal.tsx @@ -74,7 +74,7 @@ export default function Modal({ aria-hidden="true" >
{closeButton && ( diff --git a/src/components/qr-scanner.tsx b/src/components/qr-scanner.tsx index ea0d39d..4454411 100644 --- a/src/components/qr-scanner.tsx +++ b/src/components/qr-scanner.tsx @@ -1,41 +1,70 @@ -// components/QrScanner.tsx -'use client'; +// file: Html5QrcodePlugin.tsx +"use client"; -import { useEffect } from 'react'; -import { Html5Qrcode } from 'html5-qrcode'; +import { Html5QrcodeScanner, Html5QrcodeCameraScanConfig } from "html5-qrcode"; +import { useEffect } from "react"; -interface Props { - onScan: (text: string) => void; +const qrcodeRegionId = "html5qr-code-full-region"; + +interface Html5QrcodePluginProps extends Partial { + verbose?: boolean; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + qrCodeSuccessCallback: (decodedText: string, decodedResult: any) => void; + qrCodeErrorCallback?: (errorMessage: string) => void; } -export default function QrScanner({ onScan }: Props) { - useEffect(() => { - const scanner = new Html5Qrcode('qr-reader'); +const createConfig = ( + props: Html5QrcodePluginProps +): Html5QrcodeCameraScanConfig => { + const config: Html5QrcodeCameraScanConfig = { fps: 10 }; // Default fps value set to 10 - scanner - .start( - { facingMode: 'environment' }, - { fps: 10, qrbox: 250 }, - (decodedText) => { - onScan(decodedText); - scanner.stop().then(() => scanner.clear()); - }, - (error) => { - // puedes ignorar errores de escaneo frecuentes - console.error('Error de escaneo:', error); - } - ) - .catch((err) => console.error('Error al iniciar el lector QR:', err)); + if (props.fps !== undefined) { + config.fps = props.fps; + } + if (props.qrbox !== undefined) { + config.qrbox = props.qrbox; + } + if (props.aspectRatio !== undefined) { + config.aspectRatio = props.aspectRatio; + } + if (props.disableFlip !== undefined) { + config.disableFlip = props.disableFlip; + } + + return config; +}; + +const Html5QrcodePlugin: React.FC = (props) => { + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(() => { + // Previene ejecución en SSR + if (typeof window === "undefined") return; + + const config = createConfig(props); + const verbose = props.verbose === true; + + if (!props.qrCodeSuccessCallback) { + throw new Error("qrCodeSuccessCallback is required."); + } + + const html5QrcodeScanner = new Html5QrcodeScanner( + qrcodeRegionId, + config, + verbose + ); + html5QrcodeScanner.render( + props.qrCodeSuccessCallback, + props.qrCodeErrorCallback + ); return () => { - scanner.stop().then(() => scanner.clear()); + html5QrcodeScanner.clear().catch((error) => { + console.error("Failed to clear html5QrcodeScanner: ", error); + }); }; - }, [onScan]); + }, []); - return ( -
- ); -} + return
; +}; + +export default Html5QrcodePlugin;