import React, { useState, useEffect, useRef } from 'react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { getFirestore, collection, addDoc, onSnapshot, query, orderBy, serverTimestamp } from 'firebase/firestore'; import { QrCode, ScanLine, CheckCircle2, XCircle, Loader2, Copy, History, Trash2, Smartphone } from 'lucide-react'; // --- Firebase Configuration --- const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'default-app-id'; // --- Helper: Load External Scripts (for QR Scanner) --- const loadScript = (src) => { return new Promise((resolve, reject) => { if (document.querySelector(`script[src="${src}"]`)) { resolve(); return; } const script = document.createElement('script'); script.src = src; script.async = true; script.onload = resolve; script.onerror = reject; document.body.appendChild(script); }); }; export default function App() { const [user, setUser] = useState(null); const [activeTab, setActiveTab] = useState('generate'); // 'generate' | 'scan' | 'history' const [inputText, setInputText] = useState(''); const [generatedCode, setGeneratedCode] = useState(null); const [registeredCodes, setRegisteredCodes] = useState([]); const [scanResult, setScanResult] = useState(null); const [scannerReady, setScannerReady] = useState(false); const [loading, setLoading] = useState(false); const [notification, setNotification] = useState(null); // Scanner refs const scannerRef = useRef(null); const scannerContainerId = "reader"; // --- 1. Authentication --- useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (error) { console.error("Auth Error:", error); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, setUser); return () => unsubscribe(); }, []); // --- 2. Data Sync (Get Registered Codes) --- useEffect(() => { if (!user) return; // We store codes in a public collection so we can verify them easily across devices // In a real app, you might split this logic const q = query(collection(db, 'artifacts', appId, 'public', 'data', 'qrcodes')); const unsubscribe = onSnapshot(q, (snapshot) => { const codes = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // Sort in memory (Rule 2: avoid complex query errors) codes.sort((a, b) => (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0)); setRegisteredCodes(codes); }, (error) => { console.error("Firestore Error:", error); }); return () => unsubscribe(); }, [user]); // --- 3. Scanner Logic --- useEffect(() => { if (activeTab === 'scan' && !scannerReady) { loadScript("https://unpkg.com/html5-qrcode") .then(() => { setScannerReady(true); }) .catch(err => console.error("Failed to load scanner", err)); } // Cleanup scanner when leaving tab return () => { if (scannerRef.current) { scannerRef.current.clear().catch(console.error); scannerRef.current = null; } }; }, [activeTab, scannerReady]); useEffect(() => { if (activeTab === 'scan' && scannerReady && !scannerRef.current) { startScanner(); } }, [activeTab, scannerReady]); const startScanner = () => { if (!window.Html5QrcodeScanner) return; // Use Html5Qrcode for more control, or Scanner for UI const html5QrCode = new window.Html5Qrcode(scannerContainerId); scannerRef.current = html5QrCode; const config = { fps: 10, qrbox: { width: 250, height: 250 } }; html5QrCode.start( { facingMode: "environment" }, config, (decodedText) => { handleScan(decodedText); // Optional: Pause scanning after success to let user read result html5QrCode.pause(); }, (errorMessage) => { // Parse errors are common, ignore them to keep console clean } ).catch(err => { console.error("Error starting scanner", err); setNotification({ type: 'error', text: 'Erro ao iniciar câmera. Verifique as permissões.' }); }); }; const handleScan = (text) => { // Check against registered codes const found = registeredCodes.find(code => code.content === text); if (found) { setScanResult({ status: 'valid', text: text, data: found }); } else { setScanResult({ status: 'invalid', text: text }); } }; const resetScanner = () => { setScanResult(null); if (scannerRef.current) { scannerRef.current.resume(); } }; // --- 4. Actions --- const handleGenerate = async () => { if (!inputText.trim() || !user) return; setLoading(true); try { // Check if already exists locally to avoid duplicates const exists = registeredCodes.find(c => c.content === inputText); if (!exists) { await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'qrcodes'), { content: inputText, createdAt: serverTimestamp(), createdBy: user.uid }); setNotification({ type: 'success', text: 'QR Code cadastrado com sucesso!' }); } else { setNotification({ type: 'info', text: 'Este código já estava cadastrado.' }); } setGeneratedCode(inputText); setInputText(''); } catch (error) { console.error("Error saving:", error); setNotification({ type: 'error', text: 'Erro ao salvar.' }); } finally { setLoading(false); setTimeout(() => setNotification(null), 3000); } }; const copyToClipboard = (text) => { const textArea = document.createElement("textarea"); textArea.value = text; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); setNotification({ type: 'success', text: 'Copiado!' }); } catch (err) { setNotification({ type: 'error', text: 'Erro ao copiar.' }); } document.body.removeChild(textArea); setTimeout(() => setNotification(null), 2000); }; // --- Render --- return (
{/* Header */}

QR Manager

{registeredCodes.length} Cadastrados
{/* Main Content */}
{/* Navigation */}
{/* Notifications */} {notification && (
{notification.text}
)} {/* --- GENERATE TAB --- */} {activeTab === 'generate' && (