Files
2026-04-18 14:53:41 +00:00

229 lines
5.4 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}{{ 'scan_qr'|t }} - NetEvents{% endblock %}
{% block content %}
<div class="scan-page">
<div class="scan-header">
<h1>{{ 'scan_attendee'|t }}</h1>
<p>{{ 'scan_qr_description'|t }}</p>
<a href="{{ url_for('attendee_dashboard') }}" class="btn btn-outline">{{ 'back_to_dashboard'|t }}</a>
</div>
<div class="scanner-container">
<div id="qr-reader" class="qr-reader"></div>
<div id="scan-result" class="scan-result hidden">
<div class="result-icon" id="result-icon"></div>
<h2 id="result-title"></h2>
<p id="result-message"></p>
<button id="scan-again" class="btn btn-primary">{{ 'scan_again'|t }}</button>
</div>
</div>
<div class="scan-info">
<p>{{ 'scan_info_text'|t }}</p>
<p><strong>{{ 'scan_info_warning'|t }}</strong></p>
</div>
</div>
<style>
.scan-page {
max-width: 600px;
margin: 0 auto;
}
.scan-header {
text-align: center;
margin-bottom: 20px;
}
.scan-header h1 {
margin-bottom: 5px;
}
.scan-header p {
color: #666;
margin-bottom: 15px;
}
.scanner-container {
background: #000;
border-radius: 12px;
overflow: hidden;
min-height: 300px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.qr-reader {
width: 100%;
}
.qr-reader video {
width: 100% !important;
border-radius: 12px;
}
.scan-result {
text-align: center;
padding: 40px 20px;
background: #fff;
width: 100%;
}
.scan-result.hidden {
display: none;
}
.result-icon {
font-size: 64px;
margin-bottom: 10px;
}
.result-icon.success::before {
content: '✓';
color: #22c55e;
}
.result-icon.pending::before {
content: '⏳';
color: #f59e0b;
}
.result-icon.error::before {
content: '✗';
color: #ef4444;
}
.result-icon.info::before {
content: '';
color: #3b82f6;
}
#result-title {
margin: 0 0 10px 0;
}
#result-message {
color: #666;
margin-bottom: 20px;
}
.scan-info {
background: #f8fafc;
border-radius: 12px;
padding: 20px;
margin-top: 20px;
text-align: center;
}
.scan-info p {
margin: 10px 0;
color: #64748b;
}
.scan-info strong {
color: #1e293b;
}
</style>
<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>
<script>
const myId = {{ session.user_id }};
const myEventId = {{ session.event_id }};
let html5QrCode;
let scanning = true;
async function sendConnectionRequest(scannedId) {
try {
const response = await fetch('/attendee/scan-request', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ scanned_id: scannedId })
});
const data = await response.json();
const resultDiv = document.getElementById('scan-result');
const icon = document.getElementById('result-icon');
const title = document.getElementById('result-title');
const message = document.getElementById('result-message');
icon.className = 'result-icon';
if (data.success) {
document.getElementById('qr-reader').style.display = 'none';
resultDiv.classList.remove('hidden');
icon.classList.add('pending');
title.textContent = '{{ "request_sent"|t }}';
message.textContent = '{{ "request_sent_message"|t }}';
} else {
document.getElementById('qr-reader').style.display = 'none';
resultDiv.classList.remove('hidden');
icon.classList.add('error');
title.textContent = '{{ "error"|t }}';
message.textContent = data.error || '{{ "failed_send_request"|t }}';
}
scanning = false;
} catch (error) {
console.error('Connection error:', error);
alert('Error sending connection request');
}
}
function onScanSuccess(decodedText) {
if (!scanning) return;
// Expected format: "NETEVENT:{event_id}:{attendee_id}"
const parts = decodedText.split(':');
if (parts.length === 3 && parts[0] === 'NETEVENT') {
const eventId = parseInt(parts[1]);
const attendeeId = parseInt(parts[2]);
if (eventId === myEventId) {
if (attendeeId === myId) {
alert('{{ "cannot_scan_own_qr"|t }}');
return;
}
sendConnectionRequest(attendeeId);
} else {
alert('{{ "qr_different_event"|t }}');
}
} else {
console.log('Unknown QR format:', decodedText);
alert('{{ "unrecognized_qr"|t }}');
}
}
function startScanner() {
html5QrCode = new Html5Qrcode("qr-reader");
html5QrCode.start(
{ facingMode: "environment" },
{
fps: 10,
qrbox: { width: 250, height: 250 }
},
onScanSuccess,
(errorMessage) => {
// Ignore scan errors
}
).catch(err => {
console.error('Camera error:', err);
alert('{{ "camera_permission_error"|t }}');
});
}
function resetScanner() {
document.getElementById('qr-reader').style.display = 'block';
document.getElementById('scan-result').classList.add('hidden');
scanning = true;
}
document.getElementById('scan-again').addEventListener('click', resetScanner);
window.addEventListener('DOMContentLoaded', startScanner);
</script>
{% endblock %}