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 19a4a3f..f87b39e 100644 Binary files a/clue_hunt.db and b/clue_hunt.db differ 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==