[Back] @extends('layout')
@section('title')
<title>Scan QR</title>
@endsection
@section('content')
<section class="container py-4">
<h1 class="h4 mb-2">Scan Checkpoint QR</h1>
<p class="text-muted mb-3">
Point your camera at the QR on the sign. Voice guidance will play automatically after we update your location.
</p>
<div id="reader" style="max-width: 520px; aspect-ratio: 1 / 1; background:#f8f9fa; border-radius:12px;"></div>
<div id="scan-status" class="small text-muted mt-2"></div>
<div class="mt-3 d-flex gap-2">
<a href="{{ url()->previous() ?: route('map.show','floor1') }}" class="btn btn-outline-secondary">Back</a>
</div>
</section>
{{-- Local copy: no CORS/CSP issues --}}
<script src="{{ asset('vendor/html5-qrcode/html5-qrcode.min.js') }}"></script>
<script>
(function() {
const box = document.getElementById('reader');
const status = document.getElementById('scan-status');
function setStatus(msg, isErr=false) {
status.textContent = msg || '';
status.classList.toggle('text-danger', !!isErr);
}
// Secure-origin check
const isSecure =
location.protocol === 'https:' ||
location.hostname === 'localhost' ||
location.hostname === '127.0.0.1';
if (!isSecure) {
setStatus('Camera requires HTTPS or http://localhost. Use http://localhost:8000 or HTTPS (Valet/ngrok).', true);
return;
}
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
setStatus('This browser does not support camera access (getUserMedia). Try a modern browser.', true);
return;
}
function go(url) {
sessionStorage.setItem('map_voice_unlocked', '1');
localStorage.setItem('map_voice_enabled', '1');
try {
const u = new URL(url, window.location.origin);
if (!u.searchParams.has('s')) u.searchParams.set('s','1');
window.location.href = u.toString();
} catch { window.location.href = url; }
}
function onScanSuccess(text) {
try { const u = new URL(text); go(u.toString()); return; } catch {}
let path = text.trim();
if (/^https?:/i.test(path)) { go(path); return; }
// Compose /qr/{slug} (supports ?to=...)
const [slugPart, queryPart] = path.split('?');
let url = new URL("{{ route('nav.qr', ['slug'=>'__SLUG__']) }}".replace('__SLUG__',''));
url.pathname += encodeURIComponent(slugPart);
if (queryPart) {
for (const kv of queryPart.split('&')) {
const [k,v] = kv.split('=');
if (k) url.searchParams.set(k, v ?? '');
}
}
url.searchParams.set('s','1');
go(url.toString());
}
function onScanFailure(err) {
// per-frame decode errors are normal; keep silent
}
// Start the high-level scanner; prefers the back camera
setStatus('Requesting camera permission…');
const scanner = new Html5QrcodeScanner('reader', { fps: 10, qrbox: 300, rememberLastUsedCamera: true }, false);
scanner.render(onScanSuccess, onScanFailure);
setTimeout(() => {
if (!document.querySelector('#reader video')) {
setStatus('If you don’t see the camera prompt, allow camera access in the browser/site settings and reload.', true);
} else {
setStatus('');
}
}, 1500);
})();
</script>
@endsection