| const video = document.getElementById('videoFeed'); |
| const empIdInput = document.getElementById('empId'); |
| const empNameInput = document.getElementById('empName'); |
| const btnRegister = document.getElementById('btnRegister'); |
| const statusText = document.getElementById('statusText'); |
| const progressCircle = document.getElementById('progressCircle'); |
| const spoofToast = document.getElementById('spoofToast'); |
| const spoofToastMessage = document.getElementById('spoofToastMessage'); |
|
|
| let capturedFrames = []; |
| let isCapturing = false; |
| const REQUIRED_FRAMES = 5; |
| let toastDismissTimer = null; |
|
|
| |
| navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user', width: 640, height: 480 } }) |
| .then(stream => { video.srcObject = stream; }) |
| .catch(err => console.error(err)); |
|
|
| |
| [empIdInput, empNameInput].forEach(input => { |
| input.addEventListener('input', checkInputs); |
| }); |
|
|
| function checkInputs() { |
| const valid = empIdInput.value.trim().length > 0 && empNameInput.value.trim().length > 0; |
| if (valid && !isCapturing && capturedFrames.length === 0) { |
| btnRegister.disabled = false; |
| btnRegister.textContent = "START CAPTURE"; |
| btnRegister.onclick = startCaptureProcess; |
| } else if (capturedFrames.length === REQUIRED_FRAMES) { |
| btnRegister.disabled = false; |
| btnRegister.textContent = "Register"; |
| btnRegister.onclick = submitRegistration; |
| } else { |
| btnRegister.disabled = true; |
| } |
| } |
|
|
| function startCaptureProcess() { |
| isCapturing = true; |
| btnRegister.disabled = true; |
| capturedFrames = []; |
| statusText.textContent = "Keep face steady..."; |
| |
| let count = 0; |
| const interval = setInterval(() => { |
| if (count >= REQUIRED_FRAMES) { |
| clearInterval(interval); |
| finishCapture(); |
| return; |
| } |
| |
| const canvas = document.createElement('canvas'); |
| canvas.width = video.videoWidth; |
| canvas.height = video.videoHeight; |
| canvas.getContext('2d').drawImage(video, 0, 0); |
| capturedFrames.push(canvas.toDataURL('image/jpeg', 0.8)); |
| |
| count++; |
| updateProgress(count / REQUIRED_FRAMES); |
| statusText.textContent = `Scanning... ${Math.round((count/REQUIRED_FRAMES)*100)}%`; |
| |
| }, 600); |
| } |
|
|
| function updateProgress(percent) { |
| const offset = 113 - (113 * percent); |
| progressCircle.style.strokeDashoffset = offset; |
| } |
|
|
| function finishCapture() { |
| isCapturing = false; |
| statusText.textContent = "Face captured ✓"; |
| statusText.style.color = "var(--color-primary)"; |
| checkInputs(); |
| } |
|
|
| async function submitRegistration() { |
| btnRegister.disabled = true; |
| btnRegister.textContent = "REGISTERING..."; |
| |
| const payload = { |
| employee_id: empIdInput.value.trim(), |
| name: empNameInput.value.trim(), |
| frames: capturedFrames |
| }; |
| |
| try { |
| const res = await fetch('/api/register', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(payload) |
| }); |
| |
| const data = await res.json(); |
|
|
| if (data.status === 'registered') { |
| document.getElementById('registerForm').style.display = 'none'; |
| document.getElementById('successState').style.display = 'block'; |
| document.getElementById('successChip').textContent = `${payload.name} — ${payload.employee_id}`; |
| } else if (data.status === 'spoof') { |
| showSpoofToast(data); |
| btnRegister.disabled = false; |
| btnRegister.textContent = "Register"; |
| } else { |
| |
| var msg = data.message || "Unknown error"; |
| if (msg.indexOf("already registered") !== -1 || msg.indexOf("already registered to") !== -1) { |
| if (typeof showSnackbar === 'function') showSnackbar("Duplicate registration — this face is already registered.", 'error'); |
| } else if (msg.indexOf("No face detected") !== -1 || msg.indexOf("no face") !== -1) { |
| if (typeof showSnackbar === 'function') showSnackbar("Clear photo — ensure your face is visible and well lit.", 'info'); |
| } else { |
| if (typeof showSnackbar === 'function') showSnackbar(msg, 'error'); |
| } |
| btnRegister.disabled = false; |
| btnRegister.textContent = "Register"; |
| } |
| } catch (e) { |
| if (typeof showSnackbar === 'function') showSnackbar("Network error. Please try again.", 'error'); |
| btnRegister.disabled = false; |
| btnRegister.textContent = "Register"; |
| } |
| } |
|
|
| function showSpoofToast(data) { |
| const msg = data.reason || data.message || "Use a live face, not a photo or screen."; |
| spoofToastMessage.textContent = msg; |
| spoofToast.classList.add('show'); |
| if (toastDismissTimer) clearTimeout(toastDismissTimer); |
| toastDismissTimer = setTimeout(() => { |
| spoofToast.classList.remove('show'); |
| toastDismissTimer = null; |
| }, 4500); |
| } |
|
|