check-in / index.html
ivanhoang's picture
help me build a simple check in, check out app with QR code - Initial Deployment
cbc49ed verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>QR Check-In/Check-Out System</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.1/build/qrcode.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<style>
.video-container {
position: relative;
width: 100%;
max-width: 500px;
margin: 0 auto;
}
.scan-region-highlight {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 70%;
height: 70%;
max-width: 300px;
max-height: 300px;
border: 3px dashed rgba(255, 255, 255, 0.5);
border-radius: 10px;
box-sizing: border-box;
}
.hidden {
display: none;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden">
<!-- Header -->
<div class="bg-blue-600 py-4 px-6">
<h1 class="text-2xl font-bold text-white text-center">QR Check-In/Out System</h1>
</div>
<!-- Main Content -->
<div class="p-6">
<!-- Tabs -->
<div class="flex border-b border-gray-200 mb-6">
<button id="checkInTab" class="flex-1 py-2 px-4 font-medium text-blue-600 border-b-2 border-blue-600">Check In</button>
<button id="checkOutTab" class="flex-1 py-2 px-4 font-medium text-gray-500">Check Out</button>
<button id="scanTab" class="flex-1 py-2 px-4 font-medium text-gray-500">Scan QR</button>
</div>
<!-- Check In Section -->
<div id="checkInSection" class="space-y-4">
<div class="form-group">
<label class="block text-gray-700 text-sm font-bold mb-2" for="name">
Full Name
</label>
<input class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" id="name" type="text" placeholder="Enter your name">
</div>
<div class="form-group">
<label class="block text-gray-700 text-sm font-bold mb-2" for="purpose">
Purpose
</label>
<select class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" id="purpose">
<option value="">Select purpose</option>
<option value="Meeting">Meeting</option>
<option value="Delivery">Delivery</option>
<option value="Visit">Visit</option>
<option value="Other">Other</option>
</select>
</div>
<div class="form-group">
<label class="block text-gray-700 text-sm font-bold mb-2" for="host">
Host/Department
</label>
<input class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" id="host" type="text" placeholder="Who are you visiting?">
</div>
<button id="generateCheckInQR" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Generate Check-In QR
</button>
<div id="checkInQRCode" class="hidden p-4 border border-gray-200 rounded-md flex flex-col items-center">
<div id="qrCodeCheckIn" class="mb-4"></div>
<button id="downloadCheckInQR" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Download QR Code
</button>
</div>
</div>
<!-- Check Out Section -->
<div id="checkOutSection" class="space-y-4 hidden">
<div class="form-group">
<label class="block text-gray-700 text-sm font-bold mb-2" for="checkOutId">
Check-In ID
</label>
<input class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" id="checkOutId" type="text" placeholder="Enter your check-in ID">
</div>
<button id="generateCheckOutQR" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Generate Check-Out QR
</button>
<div id="checkOutQRCode" class="hidden p-4 border border-gray-200 rounded-md flex flex-col items-center">
<div id="qrCodeCheckOut" class="mb-4"></div>
<button id="downloadCheckOutQR" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Download QR Code
</button>
</div>
</div>
<!-- Scan Section -->
<div id="scanSection" class="space-y-4 hidden">
<div class="video-container">
<video id="scanner" width="100%" muted playsinline></video>
<div class="scan-region-highlight"></div>
</div>
<div id="scanResult" class="text-center p-4 bg-gray-100 rounded-md hidden">
<h3 class="font-bold mb-2">Scan Result:</h3>
<p id="resultText" class="text-gray-800"></p>
<div id="actionButtons" class="mt-4 hidden">
<button id="checkInAction" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md mr-2 transition duration-200">
Check In
</button>
<button id="checkOutAction" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Check Out
</button>
</div>
</div>
<button id="startScan" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-md transition duration-200">
Start Scanner
</button>
<button id="stopScan" class="w-full bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-md transition duration-200 hidden">
Stop Scanner
</button>
</div>
<!-- Log Section -->
<div class="mt-8 border-t border-gray-200 pt-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">Recent Activity</h2>
<div id="activityLog" class="space-y-2 max-h-60 overflow-y-auto">
<!-- Activity items will be added here -->
</div>
</div>
</div>
</div>
</div>
<script>
// Tab switching
document.getElementById('checkInTab').addEventListener('click', () => {
showSection('checkInSection');
setActiveTab('checkInTab');
});
document.getElementById('checkOutTab').addEventListener('click', () => {
showSection('checkOutSection');
setActiveTab('checkOutTab');
});
document.getElementById('scanTab').addEventListener('click', () => {
showSection('scanSection');
setActiveTab('scanTab');
});
function showSection(sectionId) {
document.getElementById('checkInSection').classList.add('hidden');
document.getElementById('checkOutSection').classList.add('hidden');
document.getElementById('scanSection').classList.add('hidden');
document.getElementById(sectionId).classList.remove('hidden');
}
function setActiveTab(tabId) {
document.getElementById('checkInTab').classList.remove('text-blue-600', 'border-blue-600');
document.getElementById('checkInTab').classList.add('text-gray-500');
document.getElementById('checkOutTab').classList.remove('text-blue-600', 'border-blue-600');
document.getElementById('checkOutTab').classList.add('text-gray-500');
document.getElementById('scanTab').classList.remove('text-blue-600', 'border-blue-600');
document.getElementById('scanTab').classList.add('text-gray-500');
document.getElementById(tabId).classList.remove('text-gray-500');
document.getElementById(tabId).classList.add('text-blue-600', 'border-blue-600');
}
// QR Code Generation for Check-In
document.getElementById('generateCheckInQR').addEventListener('click', () => {
const name = document.getElementById('name').value;
const purpose = document.getElementById('purpose').value;
const host = document.getElementById('host').value;
if (!name || !purpose || !host) {
alert('Please fill in all fields');
return;
}
const checkInData = {
type: 'checkIn',
name: name,
purpose: purpose,
host: host,
timestamp: new Date().toISOString(),
id: generateId()
};
const qrData = JSON.stringify(checkInData);
QRCode.toCanvas(document.getElementById('qrCodeCheckIn'), qrData, {
width: 200,
margin: 2,
color: {
dark: '#000000',
light: '#ffffff'
}
}, (error) => {
if (error) console.error(error);
document.getElementById('checkInQRCode').classList.remove('hidden');
// Store the check-in data in localStorage
localStorage.setItem(checkInData.id, qrData);
// Add to activity log
addActivityLog(`Checked in: ${name} visiting ${host} for ${purpose}`);
});
});
// QR Code Generation for Check-Out
document.getElementById('generateCheckOutQR').addEventListener('click', () => {
const checkOutId = document.getElementById('checkOutId').value;
if (!checkOutId) {
alert('Please enter your check-in ID');
return;
}
// Get the original check-in data
const checkInData = localStorage.getItem(checkOutId);
if (!checkInData) {
alert('No check-in record found with this ID');
return;
}
const checkOutData = {
...JSON.parse(checkInData),
type: 'checkOut',
checkOutTimestamp: new Date().toISOString()
};
const qrData = JSON.stringify(checkOutData);
QRCode.toCanvas(document.getElementById('qrCodeCheckOut'), qrData, {
width: 200,
margin: 2,
color: {
dark: '#000000',
light: '#ffffff'
}
}, (error) => {
if (error) console.error(error);
document.getElementById('checkOutQRCode').classList.remove('hidden');
// Update the localStorage record
localStorage.setItem(checkOutId, qrData);
// Add to activity log
const data = JSON.parse(checkInData);
addActivityLog(`Checked out: ${data.name} from visiting ${data.host}`);
});
});
// Download QR Code functions
document.getElementById('downloadCheckInQR').addEventListener('click', () => {
const canvas = document.querySelector('#qrCodeCheckIn canvas');
const link = document.createElement('a');
link.download = 'check-in-qr.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
document.getElementById('downloadCheckOutQR').addEventListener('click', () => {
const canvas = document.querySelector('#qrCodeCheckOut canvas');
const link = document.createElement('a');
link.download = 'check-out-qr.png';
link.href = canvas.toDataURL('image/png');
link.click();
});
// QR Code Scanner
let scannerInterval;
const video = document.getElementById('scanner');
const startScanBtn = document.getElementById('startScan');
const stopScanBtn = document.getElementById('stopScan');
const scanResult = document.getElementById('scanResult');
const resultText = document.getElementById('resultText');
const actionButtons = document.getElementById('actionButtons');
const checkInAction = document.getElementById('checkInAction');
const checkOutAction = document.getElementById('checkOutAction');
let currentScanData = null;
startScanBtn.addEventListener('click', async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment',
width: { ideal: 1280 },
height: { ideal: 720 }
}
});
video.srcObject = stream;
video.play();
startScanBtn.classList.add('hidden');
stopScanBtn.classList.remove('hidden');
scanResult.classList.add('hidden');
scannerInterval = setInterval(scanQR, 100);
} catch (err) {
console.error('Error accessing camera:', err);
alert('Could not access the camera. Please ensure you have granted camera permissions.');
}
});
stopScanBtn.addEventListener('click', () => {
clearInterval(scannerInterval);
const stream = video.srcObject;
if (stream) {
stream.getTracks().forEach(track => track.stop());
}
video.srcObject = null;
startScanBtn.classList.remove('hidden');
stopScanBtn.classList.add('hidden');
scanResult.classList.add('hidden');
});
function scanQR() {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: 'dontInvert',
});
if (code) {
try {
const data = JSON.parse(code.data);
currentScanData = data;
if (data.type === 'checkIn') {
resultText.textContent = `${data.name} is checking in to visit ${data.host} for ${data.purpose}`;
checkInAction.classList.add('hidden');
checkOutAction.classList.remove('hidden');
} else if (data.type === 'checkOut') {
resultText.textContent = `${data.name} is checking out from visiting ${data.host}`;
checkInAction.classList.add('hidden');
checkOutAction.classList.remove('hidden');
}
scanResult.classList.remove('hidden');
actionButtons.classList.remove('hidden');
// Stop scanning after successful scan
clearInterval(scannerInterval);
} catch (e) {
resultText.textContent = 'Invalid QR Code';
scanResult.classList.remove('hidden');
actionButtons.classList.add('hidden');
}
}
}
checkInAction.addEventListener('click', () => {
if (currentScanData) {
addActivityLog(`Checked in via scanner: ${currentScanData.name} visiting ${currentScanData.host}`);
alert(`Successfully checked in ${currentScanData.name}`);
scanResult.classList.add('hidden');
currentScanData = null;
// Restart scanner
scannerInterval = setInterval(scanQR, 100);
}
});
checkOutAction.addEventListener('click', () => {
if (currentScanData) {
addActivityLog(`Checked out via scanner: ${currentScanData.name} from visiting ${currentScanData.host}`);
alert(`Successfully checked out ${currentScanData.name}`);
scanResult.classList.add('hidden');
currentScanData = null;
// Restart scanner
scannerInterval = setInterval(scanQR, 100);
}
});
// Helper functions
function generateId() {
return 'id-' + Math.random().toString(36).substr(2, 9);
}
function addActivityLog(message) {
const logContainer = document.getElementById('activityLog');
const logItem = document.createElement('div');
logItem.className = 'p-3 bg-gray-50 rounded-md border border-gray-200';
logItem.innerHTML = `
<p class="text-sm text-gray-800">${message}</p>
<p class="text-xs text-gray-500 mt-1">${new Date().toLocaleString()}</p>
`;
logContainer.insertBefore(logItem, logContainer.firstChild);
// Limit log to 10 items
if (logContainer.children.length > 10) {
logContainer.removeChild(logContainer.lastChild);
}
}
// Initialize with check-in tab active
showSection('checkInSection');
setActiveTab('checkInTab');
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=ivanhoang/check-in" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>