let html5QrCode = null;
let currentToken = null;
const qrReader = document.getElementById("qr-reader");
const startScanBtn = document.getElementById("start-scan");
const stopScanBtn = document.getElementById("stop-scan");
const searchInput = document.getElementById("search-input");
const searchResults = document.getElementById("search-results");
const guestCard = document.getElementById("guest-card");
const guestPhoto = document.getElementById("guest-photo");
const guestName = document.getElementById("guest-name");
const guestDepartment = document.getElementById("guest-department");
const guestPosition = document.getElementById("guest-position");
const guestEmail = document.getElementById("guest-email");
const checkInStatus = document.getElementById("check-in-status");
const checkInBtn = document.getElementById("check-in-btn");
const resetBtn = document.getElementById("reset-btn");
function resetGuestCard() {
guestCard.classList.add("d-none");
currentToken = null;
guestPhoto.src = "";
guestName.textContent = "";
guestDepartment.textContent = "";
guestPosition.textContent = "";
guestEmail.textContent = "";
checkInStatus.classList.add("d-none");
checkInStatus.className = "alert d-none";
checkInBtn.disabled = false;
checkInBtn.textContent = "Отметить вход";
}
function showGuest(guest) {
currentToken = guest.token;
guestCard.classList.remove("d-none");
if (guest.photo_url) {
guestPhoto.src = guest.photo_url;
guestPhoto.classList.remove("d-none");
} else {
guestPhoto.src = "";
guestPhoto.classList.add("d-none");
// Show placeholder
if (!guestPhoto.nextElementSibling || !guestPhoto.nextElementSibling.classList.contains("guest-photo-placeholder")) {
const placeholder = document.createElement("div");
placeholder.className = "guest-photo-placeholder mx-auto mb-3";
placeholder.textContent = "Нет фото";
guestPhoto.parentNode.insertBefore(placeholder, guestPhoto.nextSibling);
}
}
guestName.textContent = guest.full_name;
guestDepartment.textContent = guest.department;
guestPosition.textContent = guest.position || "";
guestEmail.textContent = guest.email;
if (guest.checked_in) {
checkInStatus.classList.remove("d-none", "alert-success", "alert-warning");
checkInStatus.classList.add("alert-warning");
checkInStatus.textContent = "Гость уже отмечен как вошедший";
checkInBtn.disabled = true;
} else {
checkInStatus.classList.add("d-none");
checkInBtn.disabled = false;
}
window.scrollTo({ top: guestCard.offsetTop - 20, behavior: "smooth" });
}
function loadGuestByToken(token) {
fetch(`/api/guest/${token}`)
.then(response => {
if (!response.ok) throw new Error("Гость не найден");
return response.json();
})
.then(guest => showGuest(guest))
.catch(error => {
alert(error.message);
});
}
// QR Scanner
startScanBtn.addEventListener("click", () => {
html5QrCode = new Html5Qrcode("qr-reader");
Html5Qrcode.getCameras()
.then(cameras => {
if (cameras && cameras.length) {
const cameraId = cameras[0].id;
html5QrCode.start(
cameraId,
{ fps: 10, qrbox: { width: 250, height: 250 } },
(decodedText) => {
html5QrCode.stop();
stopScanBtn.classList.add("d-none");
startScanBtn.classList.remove("d-none");
// Extract token from URL
const url = new URL(decodedText);
const token = url.searchParams.get("token");
if (token) {
loadGuestByToken(token);
}
},
(errorMessage) => {}
);
startScanBtn.classList.add("d-none");
stopScanBtn.classList.remove("d-none");
} else {
alert("Камеры не найдены");
}
})
.catch(err => {
alert("Ошибка доступа к камере: " + err.message);
});
});
stopScanBtn.addEventListener("click", () => {
if (html5QrCode) {
html5QrCode.stop();
}
stopScanBtn.classList.add("d-none");
startScanBtn.classList.remove("d-none");
});
// Search by name
let searchTimeout = null;
searchInput.addEventListener("input", (e) => {
const query = e.target.value.trim();
searchResults.innerHTML = "";
if (query.length < 2) return;
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
fetch(`/api/guests/search?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(guests => {
searchResults.innerHTML = "";
guests.forEach(guest => {
const item = document.createElement("button");
item.className = "list-group-item list-group-item-action";
item.innerHTML = `${guest.full_name}
${guest.department}`;
item.addEventListener("click", () => {
searchResults.innerHTML = "";
searchInput.value = "";
showGuest(guest);
});
searchResults.appendChild(item);
});
});
}, 300);
});
// Check-in button
checkInBtn.addEventListener("click", () => {
if (!currentToken) return;
checkInBtn.disabled = true;
checkInBtn.textContent = "Отметка...";
fetch(`/api/check-in/${currentToken}`, { method: "POST" })
.then(response => response.json().then(data => ({ status: response.status, data })))
.then(({ status, data }) => {
checkInStatus.classList.remove("d-none", "alert-success", "alert-warning", "alert-danger");
if (data.success) {
checkInStatus.classList.add("alert-success");
checkInStatus.textContent = "Вход успешно отмечен!";
checkInBtn.textContent = "Вход отмечен";
} else if (status === 409) {
checkInStatus.classList.add("alert-warning");
checkInStatus.textContent = "Гость уже отмечен как вошедший";
checkInBtn.textContent = "Уже отмечен";
} else {
checkInStatus.classList.add("alert-danger");
checkInStatus.textContent = data.message || "Ошибка";
checkInBtn.disabled = false;
checkInBtn.textContent = "Отметить вход";
}
})
.catch(error => {
checkInStatus.classList.remove("d-none", "alert-success", "alert-warning", "alert-danger");
checkInStatus.classList.add("alert-danger");
checkInStatus.textContent = "Ошибка сети: " + error.message;
checkInBtn.disabled = false;
checkInBtn.textContent = "Отметить вход";
});
});
resetBtn.addEventListener("click", resetGuestCard);
// If token is in URL (scanned via external scanner)
document.addEventListener("DOMContentLoaded", () => {
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get("token");
if (token) {
loadGuestByToken(token);
}
});