From 02a0088d288aab1af35c3f1534e02bae0d4536aa Mon Sep 17 00:00:00 2001 From: mudabbir-ahmad Date: Mon, 17 Mar 2025 12:41:17 +0000 Subject: [PATCH] SCANNER WORKS!!! JUST ADD THE MORE CLUE BUTTONS !!! AND NEXT CLUE ERROR CHECKING --- app/(main)/clue/[clueId]/page.js | 218 +++++++++++-- app/api/clues/[clueId]/route.js | 5 +- app/api/team/[teamId]/members/route.js | 54 ++++ clue_hunt.db | Bin 86016 -> 131072 bytes components/EnhancedQRScanner.js | 413 ++++++++++++++++++++----- components/QRScanner.js | 40 ++- components/scanner-polyfill.js | 78 +++++ lib/db.js | 40 ++- package-lock.json | 19 +- package.json | 2 + yarn.lock | 209 +++++++------ 11 files changed, 861 insertions(+), 217 deletions(-) create mode 100644 app/api/team/[teamId]/members/route.js create mode 100644 components/scanner-polyfill.js diff --git a/app/(main)/clue/[clueId]/page.js b/app/(main)/clue/[clueId]/page.js index eb9b5b8..e9f5ef2 100644 --- a/app/(main)/clue/[clueId]/page.js +++ b/app/(main)/clue/[clueId]/page.js @@ -1,5 +1,5 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import { useSession } from "next-auth/react"; import { @@ -10,9 +10,8 @@ import { import Link from "next/link"; import dynamic from "next/dynamic"; import { use } from "react"; -import QRCodeHelper from "@/components/QRCodeHelper"; -// Dynamically import the new QR scanner component with no SSR +// Dynamically import the QR scanner component with no SSR const EnhancedQRScanner = dynamic( () => import("@/components/EnhancedQRScanner"), { @@ -35,10 +34,13 @@ export default function CluePage({ params }) { const [clueFound, setClueFound] = useState(false); const [allClues, setAllClues] = useState([]); const [nextClueId, setNextClueId] = useState(null); - const [huntActive, setHuntActive] = useState(true); // Track hunt active status - const [previousHuntState, setPreviousHuntState] = useState(true); // To detect changes + const [huntActive, setHuntActive] = useState(true); + const [previousHuntState, setPreviousHuntState] = useState(true); + const [showSuccessNotification, setShowSuccessNotification] = useState(false); + const [teamData, setTeamData] = useState(null); const router = useRouter(); const { data: session, status } = useSession(); + const notificationTimeoutRef = useRef(null); // Check if user is admin const isAdmin = session?.user?.role === "admin"; @@ -219,9 +221,41 @@ export default function CluePage({ params }) { }; }, [router]); + // Fetch team data on component mount + useEffect(() => { + if (status === "authenticated") { + fetchTeamData(); + } + }, [status]); + + // Add function to fetch team data + const fetchTeamData = async () => { + if (!session) return; + + try { + const response = await fetch("/api/user/team", { + headers: { + "Content-Type": "application/json", + "Cache-Control": "no-cache, no-store, must-revalidate", + }, + credentials: "include", + }); + + if (response.ok) { + const data = await response.json(); + setTeamData(data); + } + } catch (err) { + console.error("Failed to fetch team data:", err); + } + }; + // Handle QR scan result const handleScan = (data) => { - setScanMessage(`QR Code scanned: ${data}`); + // Only update message if we have data + if (data) { + setScanMessage(`QR Code scanned: ${data}`); + } try { // Handle multiple QR code formats: "QR123", "123", or even "Clue 123" @@ -284,6 +318,19 @@ export default function CluePage({ params }) { setScanMessage( "✅ Clue verified! You can now proceed to the next clue." ); + + // Show success notification + setShowSuccessNotification(true); + + // Clear previous timeout if it exists + if (notificationTimeoutRef.current) { + clearTimeout(notificationTimeoutRef.current); + } + + // Auto-hide the notification after 5 seconds + notificationTimeoutRef.current = setTimeout(() => { + setShowSuccessNotification(false); + }, 5000); } else { setScanMessage("Error verifying clue. Please try again."); } @@ -293,6 +340,15 @@ export default function CluePage({ params }) { } }; + // Clear notification timeout on component unmount + useEffect(() => { + return () => { + if (notificationTimeoutRef.current) { + clearTimeout(notificationTimeoutRef.current); + } + }; + }, []); + // Navigate to the next clue const handleNextClue = () => { if (nextClueId) { @@ -381,7 +437,27 @@ export default function CluePage({ params }) { return (
-
{elapsedTime}
+ {/* Map Banner with team info and timer */} +
+
{teamData?.team?.name || "Loading..."}
+
+ {elapsedTime} +
+
{teamData?.team?.code || ""}
+
+ + {showSuccessNotification && ( +
+
+
🎉
+
+

Clue Found!

+

Great work! You've found this clue.

+
+
+
+ )} +
{/* Admin tools section - only visible to admins */} {isAdmin && ( @@ -396,7 +472,7 @@ export default function CluePage({ params }) {
)} - {/* QR Scanner Section - Removed manual entry button */} + {/* QR Scanner Section */}

Scan QR Code

@@ -405,8 +481,7 @@ export default function CluePage({ params }) { {scanMessage}
)} - {/* Add the QR Code helper component */} - + {/* QR Code helper removed as requested */}
{/* Clue Information Section */} @@ -455,9 +530,9 @@ export default function CluePage({ params }) {
👥
Team
- -
🏠
-
Home
+ +
🏆
+
Leaderboard
@@ -467,11 +542,57 @@ export default function CluePage({ params }) { flex-direction: column; min-height: 100vh; background-color: #f4f1ea; - padding: 20px; padding-bottom: 70px; /* Space for bottom navbar */ position: relative; } + .success-notification { + position: fixed; + top: 0; + left: 0; + width: 100%; + padding: 20px; + background-color: rgba(0, 0, 0, 0.7); + z-index: 1000; + display: flex; + justify-content: center; + animation: slide-down 0.5s ease-out; + } + + .success-notification-content { + display: flex; + align-items: center; + background-color: #4caf50; + color: white; + padding: 15px 25px; + border-radius: 10px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + max-width: 400px; + } + + .success-icon { + font-size: 2rem; + margin-right: 15px; + } + + .success-text h3 { + margin: 0 0 5px 0; + font-size: 1.2rem; + } + + .success-text p { + margin: 0; + } + + @keyframes slide-down { + 0% { + transform: translateY(-100%); + } + 100% { + transform: translateY(0); + } + } + .hunt-ended { text-align: center; padding: 30px; @@ -497,6 +618,7 @@ export default function CluePage({ params }) { } .clue-content-wrapper { + padding: 0 20px; flex: 1; display: flex; flex-direction: column; @@ -506,19 +628,6 @@ export default function CluePage({ params }) { margin: 0 auto; } - .timer-display { - position: absolute; - top: 15px; - right: 15px; - background-color: rgba(0, 0, 0, 0.1); - padding: 5px 10px; - border-radius: 5px; - font-family: monospace; - font-weight: bold; - font-size: 1rem; - z-index: 10; - } - .scanner-section { width: 100%; display: flex; @@ -724,6 +833,61 @@ export default function CluePage({ params }) { .admin-refresh-button:hover { background-color: #c9302c; } + + /* Map Banner Styles */ + .map-banner { + background-color: #68543c; + color: white; + padding: 15px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 2px solid #4a3c2a; + z-index: 10; + flex-shrink: 0; + margin-bottom: 20px; + } + + .team-name { + font-size: 18px; + font-weight: bold; + flex: 1; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 5px; + } + + .elapsed-time { + text-align: center; + padding: 5px 10px; + background-color: rgba(0, 0, 0, 0.2); + border-radius: 8px; + flex: 0 0 auto; + margin: 0 10px; + } + + .time-value { + font-size: 18px; + font-weight: bold; + } + + .team-code { + font-size: 16px; + font-weight: bold; + flex: 1; + text-align: right; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-left: 5px; + } + + /* Remove the standalone timer display as it's now in the banner */ + .timer-display { + display: none; + } `} ); diff --git a/app/api/clues/[clueId]/route.js b/app/api/clues/[clueId]/route.js index 994b975..5eab2ff 100644 --- a/app/api/clues/[clueId]/route.js +++ b/app/api/clues/[clueId]/route.js @@ -24,8 +24,9 @@ export async function GET(request, context) { ); } - // Get the clueId directly from the params - const { clueId } = context.params; + // Get the clueId from params - properly awaited + const params = await context.params; + const { clueId } = params; // Validate clueId is a number if (isNaN(parseInt(clueId))) { diff --git a/app/api/team/[teamId]/members/route.js b/app/api/team/[teamId]/members/route.js new file mode 100644 index 0000000..28f21cb --- /dev/null +++ b/app/api/team/[teamId]/members/route.js @@ -0,0 +1,54 @@ +import { query } from "@/lib/db"; +import { cookies } from "next/headers"; +import { verifyToken } from "@/app/api/auth/[...nextauth]/route"; + +export async function GET(request, context) { + try { + const cookieStore = cookies(); + const token = cookieStore.get("next-auth.session-token")?.value; + + if (!token) { + return Response.json({ error: "Unauthorized" }, { status: 401 }); + } + + const userData = verifyToken(token); + if (!userData) { + return Response.json({ error: "Invalid session" }, { status: 401 }); + } + + const { teamId } = context.params; + const requestedTeamId = parseInt(teamId, 10); + const userTeamId = parseInt(userData.teamId, 10); + + console.log("Requested team:", requestedTeamId); + console.log("User team:", userTeamId); + console.log("User role:", userData.role); + + // Ensure the user is requesting their own team or is an admin + if (userTeamId !== requestedTeamId && userData.role !== "admin") { + return Response.json( + { error: "Not authorized to view this team" }, + { status: 403 } + ); + } + + const teamMembers = await query("SELECT * FROM users WHERE team_id = ?", [ + requestedTeamId, + ]); + + if (!teamMembers) { + return Response.json( + { error: "Team members not found" }, + { status: 404 } + ); + } + + return Response.json(teamMembers); + } catch (error) { + console.error("Team members fetch error:", error); + return Response.json( + { error: "Failed to fetch team members" }, + { status: 500 } + ); + } +} diff --git a/clue_hunt.db b/clue_hunt.db index 19a4a3f0d483ca8372fb3dae2d12d8cb21a377b7..f87b39ee4aa7d06005a8c5f42c47bb1bb1e67a21 100644 GIT binary patch literal 131072 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 VC3T_lr(WC}o}evxtvV!O+0U#MsKf zc(YE)4r!1ui>a|e)Z~Nm!U{m`Fu(z1aH8YQ3JDweHaiH2FiJ}@vM~VF7#kTFnd%xC z>lzv<7@Aobm|B^dY+CS^pRsWByY+$s!iB6rGno0`gN)_i|IPoE|NUm62Y2~5zd!%g t9_l9E?cewr`3yLi_&+i5|Kk6&T~LAX89yI83nL>VKNAxx3nM2dKLC3dLkIu> diff --git a/components/EnhancedQRScanner.js b/components/EnhancedQRScanner.js index f6481df..1a1c502 100644 --- a/components/EnhancedQRScanner.js +++ b/components/EnhancedQRScanner.js @@ -1,6 +1,7 @@ "use client"; import { useState, useEffect, useRef } from "react"; -import { Scanner } from "@yudiel/react-qr-scanner"; +import { useZxing } from "react-zxing"; +import { ensureScannerCompatibility } from "./scanner-polyfill"; export default function EnhancedQRScanner({ onScan, @@ -12,12 +13,38 @@ export default function EnhancedQRScanner({ const [initialized, setInitialized] = useState(false); const [cameraActive, setCameraActive] = useState(false); const [hasCameras, setHasCameras] = useState(true); - const [retryCount, setRetryCount] = useState(0); - const maxRetries = 3; - const scannerRef = useRef(null); + const [scanSuccess, setScanSuccess] = useState(false); + const [manualInputValue, setManualInputValue] = useState(""); + const [torchEnabled, setTorchEnabled] = useState(false); + const inputRef = useRef(null); + const [cameraPermissionDenied, setCameraPermissionDenied] = useState(false); + + // Define video constraints explicitly + const videoConstraints = { + facingMode: "environment", + width: { min: 640, ideal: 1280, max: 1920 }, + height: { min: 480, ideal: 720, max: 1080 }, + }; + + // Setup the zxing scanner hook with explicit video constraints + const { ref, torch } = useZxing({ + onDecodeResult(result) { + handleDecode(result.getText()); + }, + onError(error) { + handleError(error); + }, + constraints: { + video: videoConstraints, + }, + paused: !cameraActive, + }); - // Check if we're in a secure context + // Check if we're in a secure context and if camera is accessible useEffect(() => { + // Ensure scanner compatibility + ensureScannerCompatibility(); + // Check if we're in a secure context (https or localhost) const secure = window.isSecureContext; setIsSecureContext(secure); @@ -25,6 +52,23 @@ export default function EnhancedQRScanner({ // Check if camera can be accessed async function checkCameraAccess() { try { + // First check if MediaDevices API is supported + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + throw new Error("MediaDevices API is not supported in this browser"); + } + + // Explicitly request video permission - important to avoid the error + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: false + }); + + // If we got a stream, we have permission - release it immediately + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } + + // Now enumerate devices to confirm we have video devices const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter( (device) => device.kind === "videoinput" @@ -37,38 +81,88 @@ export default function EnhancedQRScanner({ setHasCameras(false); } else { setHasCameras(true); + setCameraActive(true); // Activate camera if we have permission } setInitialized(true); } catch (err) { - console.error("Error checking camera devices:", err); - setError(`Camera initialization error: ${err.message}`); + console.error("Camera access error:", err); + + // Handle permission denied specifically + if (err.name === "NotAllowedError" || err.name === "PermissionDeniedError") { + setCameraPermissionDenied(true); + setError("Camera permission denied. Please allow camera access in your browser settings."); + } else { + setError(`Camera error: ${err.message}`); + } + setInitialized(true); } } - checkCameraAccess(); + if (isSecureContext) { + checkCameraAccess(); + } else { + setInitialized(true); // Still mark as initialized even if not secure + } }, []); - // Handle successful scan - const handleDecode = (result) => { - if (onScan && result) { - onScan(result); + // Toggle torch/flashlight + const toggleTorch = () => { + if (torch) { + setTorchEnabled(!torchEnabled); + torch.toggled = !torchEnabled; } }; - // Try to restart the camera - const retryCamera = () => { - if (retryCount < maxRetries) { + // Handle decode result + const handleDecode = (text) => { + console.log("QR scan result:", text); + + if (!onScan || !text) return; + + try { + // Show success visual + setScanSuccess(true); + setTimeout(() => setScanSuccess(false), 1500); + + // Call the onScan callback with the decoded text + onScan(text); + } catch (err) { + console.error("Error processing QR scan result:", err); + } + }; + + // Manually request camera access + const requestCameraAccess = async () => { + try { setError(null); - setRetryCount((prev) => prev + 1); - // Force re-render of scanner component - setCameraActive(false); - setTimeout(() => setCameraActive(true), 500); - } else { - setError( - "Could not access camera after multiple attempts. Please refresh the page or try another device." - ); + + // Explicitly request video permission + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: false + }); + + // If we got a stream, we have permission + if (stream) { + // Release the stream since useZxing will request it again + stream.getTracks().forEach(track => track.stop()); + setCameraActive(true); + setCameraPermissionDenied(false); + } + } catch (err) { + console.error("Failed to get camera permission:", err); + setError(`Camera error: ${err.message}`); + setCameraPermissionDenied(true); + } + }; + + // Handle manual input submission + const handleManualSubmit = () => { + if (manualInputValue.trim()) { + onScan(manualInputValue.trim()); + setManualInputValue(""); } }; @@ -89,6 +183,7 @@ export default function EnhancedQRScanner({ ) { errorMessage += "Camera permission denied. Please allow camera access in your browser settings."; + setCameraPermissionDenied(true); } else if ( error.name === "NotFoundError" || error.name === "DevicesNotFoundError" @@ -111,13 +206,6 @@ export default function EnhancedQRScanner({ setError(errorMessage); }; - // When initialized, activate camera - useEffect(() => { - if (initialized && hasCameras && isSecureContext && !error) { - setCameraActive(true); - } - }, [initialized, hasCameras, isSecureContext, error]); - // Handle secure context warning if (!isSecureContext) { return ( @@ -129,6 +217,7 @@ export default function EnhancedQRScanner({ network IP.

+ ); } @@ -147,8 +236,29 @@ export default function EnhancedQRScanner({ {error && (

{error}

- {retryCount < maxRetries && ( - +
+ ) : ( + )} @@ -157,63 +267,54 @@ export default function EnhancedQRScanner({ {cameraActive && !error && (
- + {scanSuccess &&
✓
} + + {/* Scanning frame to help user focus */} +
+ + {/* Add flashlight toggle button if available */} + {torch && ( + + )}
)} {/* Text input fallback when camera fails */} {(!cameraActive || error) && (
-

- Having trouble with the camera? Try entering a code manually: -

+

Enter the QR code manually:

setManualInputValue(e.target.value)} onKeyDown={(e) => { - if (e.key === "Enter" && e.target.value) { - onScan(e.target.value); - e.target.value = ""; + if (e.key === "Enter") { + handleManualSubmit(); } }} + ref={inputRef} /> -
@@ -237,6 +338,29 @@ export default function EnhancedQRScanner({ border-radius: 8px; width: 100%; } + + .permission-help { + margin-top: 12px; + background-color: #f8f8f8; + border: 1px solid #ddd; + border-radius: 4px; + padding: 10px; + text-align: left; + } + + .permission-help p { + margin: 0 0 8px 0; + font-weight: bold; + } + + .permission-help ol { + margin: 0 0 10px 0; + padding-left: 20px; + } + + .permission-help li { + margin-bottom: 5px; + } .retry-button { margin-top: 10px; @@ -271,6 +395,76 @@ export default function EnhancedQRScanner({ overflow: hidden; border: 2px solid #ddd; background-color: #f5f5f5; + transition: all 0.3s ease; + } + + .scanning-frame { + position: absolute; + top: 50%; + left: 50%; + width: 70%; + height: 70%; + transform: translate(-50%, -50%); + border: 2px solid rgba(255, 255, 255, 0.8); + border-radius: 8px; + box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.3); + z-index: 2; + } + + .torch-button { + position: absolute; + bottom: 15px; + left: 50%; + transform: translateX(-50%); + background-color: rgba(0, 0, 0, 0.5); + color: white; + border: none; + border-radius: 20px; + padding: 8px 16px; + cursor: pointer; + z-index: 3; + font-size: 14px; + } + + .torch-button.active { + background-color: rgba(255, 193, 7, 0.8); + color: black; + } + + .scan-success { + border-color: #4caf50; + box-shadow: 0 0 15px rgba(76, 175, 80, 0.5); + } + + .scan-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(76, 175, 80, 0.5); + color: white; + font-size: 80px; + animation: fade-in-out 1.5s forwards; + z-index: 10; + } + + @keyframes fade-in-out { + 0% { + opacity: 0; + } + 20% { + opacity: 1; + } + 80% { + opacity: 1; + } + 100% { + opacity: 0; + } } .manual-input-container { @@ -315,3 +509,82 @@ export default function EnhancedQRScanner({
); } + +// Helper component for manual input +function ManualInput({ onScan }) { + const [value, setValue] = useState(""); + + const handleSubmit = () => { + if (value.trim()) { + onScan(value.trim()); + setValue(""); + } + }; + + return ( +
+

Manual Code Entry

+

Enter the QR code value manually:

+
+ setValue(e.target.value)} + placeholder="Enter code (e.g. QR1)" + onKeyDown={(e) => { + if (e.key === "Enter") { + handleSubmit(); + } + }} + /> + +
+ + +
+ ); +} diff --git a/components/QRScanner.js b/components/QRScanner.js index cd16c9d..0494306 100644 --- a/components/QRScanner.js +++ b/components/QRScanner.js @@ -1,6 +1,6 @@ "use client"; import { useState, useEffect } from "react"; -import { Scanner } from "@yudiel/react-qr-scanner"; +import QrScanner from "@yudiel/react-qr-scanner"; // Correct import export default function QRScanner({ onScan, width = 300, height = 300 }) { const [error, setError] = useState(null); @@ -39,8 +39,37 @@ export default function QRScanner({ onScan, width = 300, height = 300 }) { }, []); const handleDecode = (result) => { - if (onScan && result) { - onScan(result); + console.log("QR scan result:", result); + + if (!onScan || !result) return; + + try { + // Handle different result formats + if (result && typeof result === "object") { + if (result.text) { + onScan(result.text); + return; + } + if (result.data) { + onScan(result.data); + return; + } + if (result.toString && typeof result.toString === "function") { + const stringValue = result.toString(); + if (stringValue !== "[object Object]") { + onScan(stringValue); + return; + } + } + } else if (typeof result === "string") { + onScan(result); + return; + } + + // Last resort + onScan(String(result)); + } catch (err) { + console.error("Error processing QR scan result:", err); } }; @@ -112,8 +141,8 @@ export default function QRScanner({ onScan, width = 300, height = 300 }) { )}
-
diff --git a/components/scanner-polyfill.js b/components/scanner-polyfill.js new file mode 100644 index 0000000..8dc2ed6 --- /dev/null +++ b/components/scanner-polyfill.js @@ -0,0 +1,78 @@ +// This is a helper function to ensure QR scanner compatibility +export const ensureScannerCompatibility = () => { + // Add basic polyfills for methods that might be missing + if (typeof Function.prototype.bind !== "function") { + Function.prototype.bind = function (context) { + const fn = this; + return function () { + return fn.apply(context, arguments); + }; + }; + } + + // Ensure MediaDevices API is available + if (typeof window !== "undefined") { + if (!navigator.mediaDevices) { + navigator.mediaDevices = {}; + } + + // Polyfill for older browsers that don't implement getUserMedia + if (!navigator.mediaDevices.getUserMedia) { + navigator.mediaDevices.getUserMedia = function(constraints) { + const getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; + + // Return a rejected promise if getUserMedia is not implemented + if (!getUserMedia) { + return Promise.reject(new Error('getUserMedia is not implemented in this browser')); + } + + // Otherwise, wrap the call to the old navigator.getUserMedia with a Promise + return new Promise(function(resolve, reject) { + getUserMedia.call(navigator, constraints, resolve, reject); + }); + }; + } + + // Ensure console methods exist for debugging + if (!window.console) window.console = {}; + const methods = ["log", "error", "warn", "info"]; + methods.forEach((method) => { + if (!window.console[method]) { + window.console[method] = function () {}; + } + }); + } + + return true; +}; + +// Helper function to check if camera is available and accessible +export const checkCameraAvailability = async () => { + try { + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + return { available: false, error: 'MediaDevices API not supported' }; + } + + // Try to access the camera with explicit video request + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, + audio: false + }); + + // If we get here, camera is available - stop the stream + if (stream) { + stream.getTracks().forEach(track => track.stop()); + } + + return { available: true }; + } catch (error) { + return { + available: false, + error: error.message, + name: error.name, + permission: error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError' + ? 'denied' + : 'unknown' + }; + } +}; diff --git a/lib/db.js b/lib/db.js index e691c18..fc338c9 100644 --- a/lib/db.js +++ b/lib/db.js @@ -1,5 +1,6 @@ import sqlite3 from "sqlite3"; +// Create a singleton database connection let db = null; export function getDb() { @@ -15,11 +16,12 @@ export function getDb() { return db; } +// Execute a query that returns a single row export function queryOne(sql, params = []) { return new Promise((resolve, reject) => { const db = getDb(); if (!db) { - reject(new Error("Database connection not available")); + reject(Error("Database connection not available")); return; } @@ -31,17 +33,17 @@ export function queryOne(sql, params = []) { reject(err); return; } - resolve(row); }); }); } +// Execute a query that returns multiple rows export function query(sql, params = []) { return new Promise((resolve, reject) => { const db = getDb(); if (!db) { - reject(new Error("Database connection not available")); + reject(Error("Database connection not available")); return; } @@ -53,23 +55,23 @@ export function query(sql, params = []) { reject(err); return; } - resolve(rows || []); }); }); } +// Execute a query that doesn't return results (INSERT, UPDATE, DELETE) export function run(sql, params = []) { return new Promise((resolve, reject) => { const db = getDb(); if (!db) { - const error = new Error("Database connection not available"); + const error = Error("Database connection not available"); console.error(error); reject(error); return; } - // Add better logging + // Log transaction operations for debugging if ( sql.toLowerCase().includes("begin") || sql.toLowerCase().includes("commit") || @@ -86,15 +88,31 @@ export function run(sql, params = []) { reject(err); return; } - - resolve({ - lastID: this.lastID, - changes: this.changes, - }); + resolve({ lastID: this.lastID, changes: this.changes }); }); }); } +// Close database connection - useful for tests and clean shutdown +export function closeDb() { + return new Promise((resolve, reject) => { + if (db) { + db.close((err) => { + if (err) { + console.error("Error closing database:", err.message); + reject(err); + } else { + console.log("Database connection closed"); + db = null; + resolve(); + } + }); + } else { + resolve(); + } + }); +} + // New function to explicitly enable foreign keys export function enableForeignKeys() { return run("PRAGMA foreign_keys = ON;") diff --git a/package-lock.json b/package-lock.json index 040c3e0..e044575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-zxing": "^2.0.0", "sqlite": "^5.1.1", "sqlite3": "^5.1.7" }, @@ -1446,7 +1447,7 @@ "react-dom": "^18.0.0" } }, - "node_modules/@yudiel/react-qr-scanner/node_modules/@zxing/library": { + "node_modules/@zxing/library": { "version": "0.20.0", "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.20.0.tgz", "integrity": "sha512-6Ev6rcqVjMakZFIDvbUf0dtpPGeZMTfyxYg4HkVWioWeN7cRcnUWT3bU6sdohc82O1nPXcjq6WiGfXX2Pnit6A==", @@ -5782,6 +5783,22 @@ "dev": true, "license": "MIT" }, + "node_modules/react-zxing": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-zxing/-/react-zxing-2.1.0.tgz", + "integrity": "sha512-iFZ8KPfZl+biiT5/bluuMGaSLQAwnE0ialQHbJioHFuwqhCo1WmjQmAtI4WN8BmiKYFVkHGgwHSl+pgwpwtQEg==", + "license": "MIT", + "dependencies": { + "@zxing/library": "^0.20.0" + }, + "engines": { + "node": "^18 || ^20 || ^22", + "yarn": "^1.22" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", diff --git a/package.json b/package.json index e279976..fcaeb02 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-zxing": "^2.0.0", "sqlite": "^5.1.1", "sqlite3": "^5.1.7" }, @@ -39,3 +40,4 @@ "typescript": "^5" } } + diff --git a/yarn.lock b/yarn.lock index 12901c5..4352451 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,7 +16,7 @@ "@emnapi/runtime@^1.2.0": version "1.3.1" - resolved "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== dependencies: tslib "^2.4.0" @@ -122,110 +122,110 @@ "@img/sharp-darwin-arm64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== optionalDependencies: "@img/sharp-libvips-darwin-arm64" "1.0.4" "@img/sharp-darwin-x64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== optionalDependencies: "@img/sharp-libvips-darwin-x64" "1.0.4" "@img/sharp-libvips-darwin-arm64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== "@img/sharp-libvips-darwin-x64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== -"@img/sharp-libvips-linux-arm@1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz" - integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== - "@img/sharp-libvips-linux-arm64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== + "@img/sharp-libvips-linux-s390x@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== "@img/sharp-libvips-linux-x64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== "@img/sharp-libvips-linuxmusl-arm64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== "@img/sharp-libvips-linuxmusl-x64@1.0.4": version "1.0.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== -"@img/sharp-linux-arm@0.33.5": - version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz" - integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== - optionalDependencies: - "@img/sharp-libvips-linux-arm" "1.0.5" - "@img/sharp-linux-arm64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== optionalDependencies: "@img/sharp-libvips-linux-arm64" "1.0.4" +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-linux-s390x@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== optionalDependencies: "@img/sharp-libvips-linux-s390x" "1.0.4" "@img/sharp-linux-x64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== optionalDependencies: "@img/sharp-libvips-linux-x64" "1.0.4" "@img/sharp-linuxmusl-arm64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== optionalDependencies: "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" "@img/sharp-linuxmusl-x64@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== optionalDependencies: "@img/sharp-libvips-linuxmusl-x64" "1.0.4" "@img/sharp-wasm32@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== dependencies: "@emnapi/runtime" "^1.2.0" "@img/sharp-win32-ia32@0.33.5": version "0.33.5" - resolved "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== "@img/sharp-win32-x64@0.33.5": @@ -247,37 +247,37 @@ "@next/swc-darwin-arm64@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.2.tgz#d3131279d4815ed7604eba7bf0063e0b8bfd2135" integrity sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw== "@next/swc-darwin-x64@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.2.tgz#8562f6b51177aa30f3a6e95a8b3e0e176a6cb04f" integrity sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA== "@next/swc-linux-arm64-gnu@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.2.tgz#c2466cffc541f39ae4d435087c81f5899d401903" integrity sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ== "@next/swc-linux-arm64-musl@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.2.tgz#24ed87fe47325597e986bd01c427efae53d6b677" integrity sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA== "@next/swc-linux-x64-gnu@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.2.tgz#0240f44530ca39c1fc899fabe8f33aa35d7e77da" integrity sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA== "@next/swc-linux-x64-musl@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.2.tgz#cca6ce8e9ec89afaa618673f0f8658682c30ff56" integrity sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw== "@next/swc-win32-arm64-msvc@15.2.2": version "15.2.2" - resolved "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.2.tgz" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.2.tgz#08f621602ed53fa21ddf6f656160b9186860be15" integrity sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg== "@next/swc-win32-x64-msvc@15.2.2": @@ -293,7 +293,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -365,52 +365,52 @@ "@tailwindcss/oxide-android-arm64@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.0.12.tgz#f79cf637bc11b0ae71e75b66d7c5ba5159bd3df3" integrity sha512-dAXCaemu3mHLXcA5GwGlQynX8n7tTdvn5i1zAxRvZ5iC9fWLl5bGnjZnzrQqT7ttxCvRwdVf3IHUnMVdDBO/kQ== "@tailwindcss/oxide-darwin-arm64@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.0.12.tgz#6b730b5d19ff01748a27af5103966f534a899f07" integrity sha512-vPNI+TpJQ7sizselDXIJdYkx9Cu6JBdtmRWujw9pVIxW8uz3O2PjgGGzL/7A0sXI8XDjSyRChrUnEW9rQygmJQ== "@tailwindcss/oxide-darwin-x64@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.0.12.tgz#2eb33ba40cbb78922004be9d6b81542347043fe5" integrity sha512-RL/9jM41Fdq4Efr35C5wgLx98BirnrfwuD+zgMFK6Ir68HeOSqBhW9jsEeC7Y/JcGyPd3MEoJVIU4fAb7YLg7A== "@tailwindcss/oxide-freebsd-x64@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.0.12.tgz#317b980504b0bbcaee32c9e4e961f77cdadb02a6" integrity sha512-7WzWiax+LguJcMEimY0Q4sBLlFXu1tYxVka3+G2M9KmU/3m84J3jAIV4KZWnockbHsbb2XgrEjtlJKVwHQCoRA== "@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.0.12.tgz#0f7153dec6e200327b76775ad2d1154706cef34d" integrity sha512-X9LRC7jjE1QlfIaBbXjY0PGeQP87lz5mEfLSVs2J1yRc9PSg1tEPS9NBqY4BU9v5toZgJgzKeaNltORyTs22TQ== "@tailwindcss/oxide-linux-arm64-gnu@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.0.12.tgz#50a3ec46600cc998f45a265ebdd5de2d7bb27ee0" integrity sha512-i24IFNq2402zfDdoWKypXz0ZNS2G4NKaA82tgBlE2OhHIE+4mg2JDb5wVfyP6R+MCm5grgXvurcIcKWvo44QiQ== "@tailwindcss/oxide-linux-arm64-musl@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.0.12.tgz#2de10c5239766e2fb810045fe5b75db54d481fd4" integrity sha512-LmOdshJBfAGIBG0DdBWhI0n5LTMurnGGJCHcsm9F//ISfsHtCnnYIKgYQui5oOz1SUCkqsMGfkAzWyNKZqbGNw== "@tailwindcss/oxide-linux-x64-gnu@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.0.12.tgz#f89a24ffb0f14ea23d110efcbf58baa1db783582" integrity sha512-OSK667qZRH30ep8RiHbZDQfqkXjnzKxdn0oRwWzgCO8CoTxV+MvIkd0BWdQbYtYuM1wrakARV/Hwp0eA/qzdbw== "@tailwindcss/oxide-linux-x64-musl@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.0.12.tgz#c53a5b494e241a744f067ba3a261adca80c20af4" integrity sha512-uylhWq6OWQ8krV8Jk+v0H/3AZKJW6xYMgNMyNnUbbYXWi7hIVdxRKNUB5UvrlC3RxtgsK5EAV2i1CWTRsNcAnA== "@tailwindcss/oxide-win32-arm64-msvc@4.0.12": version "4.0.12" - resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.0.12.tgz#63c0befad43355fb5cea6c64efd946a718acb06b" integrity sha512-XDLnhMoXZEEOir1LK43/gHHwK84V1GlV8+pAncUAIN2wloeD+nNciI9WRIY/BeFTqES22DhTIGoilSO39xDb2g== "@tailwindcss/oxide-win32-x64-msvc@4.0.12": @@ -603,7 +603,7 @@ acorn@^8.14.0: resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== -agent-base@^6.0.2, agent-base@6: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -954,15 +954,14 @@ client-only@0.0.1: resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -"clue-hunt-app@file:": +"clue-hunt-app@file:.": version "0.1.0" - resolved "file:" dependencies: "@panva/hkdf" "^1.1.1" "@yudiel/react-qr-scanner" "^1.2.4" bcryptjs "^3.0.2" better-sqlite3 "^11.8.1" - clue-hunt-app "file:" + clue-hunt-app "file:../../AppData/Local/Yarn/Cache/v6/npm-clue-hunt-app-0.1.0-dfb72df2-52f6-42b4-8429-9f5630443ed9-1742214702181/node_modules/clue-hunt-app" html5-qrcode "^2.3.8" jose "^4.14.4" jsonwebtoken "^9.0.2" @@ -971,6 +970,7 @@ client-only@0.0.1: qrcode.react "^4.2.0" react "^19.0.0" react-dom "^19.0.0" + react-zxing "^2.0.0" sqlite "^5.1.1" sqlite3 "^5.1.7" @@ -1068,6 +1068,13 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +debug@4, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.7: + version "4.4.0" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + debug@^3.2.7: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -1075,13 +1082,6 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.7, debug@4: - version "4.4.0" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" @@ -1541,17 +1541,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.3.2: - version "3.3.3" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" - integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.8" - fast-glob@3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" @@ -1563,6 +1552,17 @@ fast-glob@3.3.1: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" @@ -1914,7 +1914,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.3, inherits@^2.0.4, inherits@2: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2283,47 +2283,47 @@ levn@^0.4.1: lightningcss-darwin-arm64@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz#6ceff38b01134af48e859394e1ca21e5d49faae6" integrity sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA== lightningcss-darwin-x64@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz#891b6f9e57682d794223c33463ca66d3af3fb038" integrity sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w== lightningcss-freebsd-x64@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz#8a95f9ab73b2b2b0beefe1599fafa8b058938495" integrity sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg== lightningcss-linux-arm-gnueabihf@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz#5c60bbf92b39d7ed51e363f7b98a7111bf5914a1" integrity sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg== lightningcss-linux-arm64-gnu@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz#e73d7608c4cce034c3654e5e8b53be74846224de" integrity sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ== lightningcss-linux-arm64-musl@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz#a95a18d5a909831c092e0a8d2de4b9ac1a8db151" integrity sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ== lightningcss-linux-x64-gnu@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz#551ca07e565394928642edee92acc042e546cb78" integrity sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg== lightningcss-linux-x64-musl@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz#2fd164554340831bce50285b57101817850dd258" integrity sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w== lightningcss-win32-arm64-msvc@1.29.2: version "1.29.2" - resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz#da43ea49fafc5d2de38e016f1a8539d5eed98318" integrity sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw== lightningcss-win32-x64-msvc@1.29.2: @@ -2837,15 +2837,6 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== -postcss@^8.4.41: - version "8.5.3" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz" - integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - postcss@8.4.31: version "8.4.31" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" @@ -2855,6 +2846,15 @@ postcss@8.4.31: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^8.4.41: + version "8.5.3" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + preact-render-to-string@^5.1.19: version "5.2.6" resolved "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz" @@ -2962,6 +2962,13 @@ react-is@^16.13.1: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-zxing@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/react-zxing/-/react-zxing-2.1.0.tgz" + integrity sha512-iFZ8KPfZl+biiT5/bluuMGaSLQAwnE0ialQHbJioHFuwqhCo1WmjQmAtI4WN8BmiKYFVkHGgwHSl+pgwpwtQEg== + dependencies: + "@zxing/library" "^0.20.0" + react@^19.0.0: version "19.0.0" resolved "https://registry.npmjs.org/react/-/react-19.0.0.tgz" @@ -3287,11 +3294,6 @@ sprintf-js@^1.1.3: resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== -sqlite@^5.1.1: - version "5.1.1" - resolved "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz" - integrity sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q== - sqlite3@^5.1.7: version "5.1.7" resolved "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz" @@ -3304,6 +3306,11 @@ sqlite3@^5.1.7: optionalDependencies: node-gyp "8.x" +sqlite@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz" + integrity sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q== + ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz" @@ -3321,13 +3328,6 @@ streamsearch@^1.1.0: resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -3405,6 +3405,13 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -3446,7 +3453,7 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tailwindcss@^4, tailwindcss@4.0.12: +tailwindcss@4.0.12, tailwindcss@^4: version "4.0.12" resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.12.tgz" integrity sha512-bT0hJo91FtncsAMSsMzUkoo/iEU0Xs5xgFgVC9XmdM9bw5MhZuQFjPNl6wxAE0SiQF/YTZJa+PndGWYSDtuxAg==