Skip to content

Commit f630aa1

Browse files
committed
feat: add 360° Room Camera proctoring (QR-based secondary camera)
- Add optional '360° Room Camera' toggle in proctored interview settings - Shows how-it-works guide when enabled (QR scan → phone rear camera → desk placement) - Add QR code generator in Proctored Room tab using qrcode-generator library - Create room-camera.html: mobile-optimized page candidates open on their phone - Streams rear camera with live status indicator - Placement diagram showing optimal phone position - Camera switch, pause/resume, stop controls - Wake lock to prevent screen sleep during interview - Responsive dark UI designed for phone screens - Add copy-link and direct-link buttons for sharing room camera URL
1 parent 7a06d6b commit f630aa1

3 files changed

Lines changed: 611 additions & 0 deletions

File tree

dashboard/hr.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,6 +1435,35 @@ <h6>Browser Lock</h6>
14351435
</div>
14361436
</div>
14371437
</div>
1438+
1439+
<!-- 360 Room Camera QR Section -->
1440+
<div class="card mb-3" style="border:1.5px solid #d8b4fe;background:linear-gradient(135deg,#faf5ff,#f3e8ff);">
1441+
<div class="card-body" style="padding:20px;">
1442+
<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:12px;">
1443+
<div>
1444+
<h5 style="margin:0 0 4px;font-weight:700;display:flex;align-items:center;gap:8px;">
1445+
<i class="fas fa-mobile-screen" style="color:#7c3aed;"></i> 360° Room Camera
1446+
<span style="font-size:0.68rem;font-weight:700;color:#7c3aed;background:#ede9fe;padding:2px 8px;border-radius:10px;">OPTIONAL</span>
1447+
</h5>
1448+
<p style="font-size:0.82rem;color:#6b7280;margin:0;">Generate a QR code for the candidate to scan with their smartphone</p>
1449+
</div>
1450+
<button class="btn-primary-custom" onclick="show360QRCode()" style="background:#7c3aed;border-color:#7c3aed;white-space:nowrap;">
1451+
<i class="fas fa-qrcode"></i> Generate QR Code
1452+
</button>
1453+
</div>
1454+
<div id="qr360Container" style="display:none;margin-top:16px;text-align:center;">
1455+
<div style="background:white;display:inline-block;padding:20px;border-radius:16px;border:2px solid #d8b4fe;">
1456+
<div id="qr360Canvas" style="width:200px;height:200px;margin:0 auto;"></div>
1457+
<div style="margin-top:10px;font-size:0.78rem;color:#6b21a8;font-weight:600;">Scan with phone camera</div>
1458+
<div id="qr360Link" style="margin-top:4px;font-size:0.72rem;color:#94a3b8;word-break:break-all;"></div>
1459+
</div>
1460+
<div style="margin-top:12px;display:flex;justify-content:center;gap:8px;">
1461+
<button class="btn-outline-custom" style="font-size:0.78rem;padding:5px 14px;" onclick="copy360Link()"><i class="fas fa-copy me-1"></i>Copy Link</button>
1462+
<a id="qr360DirectLink" href="#" target="_blank" class="btn-outline-custom" style="font-size:0.78rem;padding:5px 14px;text-decoration:none;"><i class="fas fa-external-link-alt me-1"></i>Open Page</a>
1463+
</div>
1464+
</div>
1465+
</div>
1466+
</div>
14381467
<div class="card">
14391468
<div class="card-header"><h5>Proctored Interview Sessions</h5></div>
14401469
<div class="card-body">
@@ -2329,6 +2358,26 @@ <h6><i class="fas fa-shield-halved"></i> Proctored Interview Settings <span clas
23292358
</div>
23302359
</div>
23312360
</div>
2361+
<!-- 360 Room Camera Option -->
2362+
<div style="margin-top:12px;padding:14px 16px;background:linear-gradient(135deg,#faf5ff,#f3e8ff);border:1.5px solid #d8b4fe;border-radius:12px;">
2363+
<div class="proctor-option" style="margin:0;">
2364+
<input type="checkbox" id="proctor360Camera" onchange="document.getElementById('proctor360Info').style.display=this.checked?'block':'none'">
2365+
<div style="flex:1;">
2366+
<label for="proctor360Camera"><strong>📱 360° Room Camera</strong> <span style="font-size:0.7rem;font-weight:700;color:#7c3aed;background:#ede9fe;padding:2px 8px;border-radius:10px;margin-left:4px;">OPTIONAL</span></label>
2367+
<div class="proctor-desc">Candidate scans QR code on smartphone, places it behind desk as secondary camera</div>
2368+
</div>
2369+
</div>
2370+
<div id="proctor360Info" style="display:none;margin-top:10px;padding:10px 12px;background:white;border-radius:8px;border:1px solid #e9d5ff;">
2371+
<div style="font-size:0.78rem;color:#6b21a8;font-weight:600;margin-bottom:6px;"><i class="fas fa-info-circle me-1"></i>How it works:</div>
2372+
<ol style="font-size:0.76rem;color:#581c87;margin:0;padding-left:18px;line-height:1.7;">
2373+
<li>After scheduling, a <strong>QR code</strong> is generated for the candidate</li>
2374+
<li>Candidate scans it with their <strong>smartphone</strong></li>
2375+
<li>Phone opens a page streaming the <strong>rear camera</strong></li>
2376+
<li>Candidate places phone <strong>behind their desk</strong></li>
2377+
<li>Proctor sees both <strong>webcam + room view</strong> side by side</li>
2378+
</ol>
2379+
</div>
2380+
</div>
23322381
<div class="form-group mt-3">
23332382
<label>Max Violations Before Auto-Flag</label>
23342383
<select class="form-control-custom" id="proctorMaxViolations" style="width:auto;">
@@ -2529,6 +2578,7 @@ <h6 style="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;ali
25292578
</div>
25302579

25312580
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
2581+
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
25322582
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
25332583
<script src="../js/hr-config.js"></script>
25342584
<script>

js/360-review.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,58 @@ function simulate360Feedback(id) {
240240
});
241241
}
242242
})();
243+
244+
// ===== 360° ROOM CAMERA (QR Code + Secondary Camera) =====
245+
function show360QRCode() {
246+
var token = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
247+
var baseUrl = window.location.origin;
248+
var roomUrl = baseUrl + '/room-camera.html?token=' + token;
249+
250+
var container = document.getElementById('qr360Container');
251+
var canvas = document.getElementById('qr360Canvas');
252+
var linkEl = document.getElementById('qr360Link');
253+
var directLink = document.getElementById('qr360DirectLink');
254+
255+
// Generate QR code using qrcode-generator library
256+
if (typeof qrcode !== 'undefined') {
257+
var qr = qrcode(0, 'M');
258+
qr.addData(roomUrl);
259+
qr.make();
260+
canvas.innerHTML = qr.createSvgTag({ cellSize: 5, margin: 2, scalable: true });
261+
// Make SVG fill container
262+
var svg = canvas.querySelector('svg');
263+
if (svg) {
264+
svg.setAttribute('width', '100%');
265+
svg.setAttribute('height', '100%');
266+
svg.style.maxWidth = '200px';
267+
}
268+
} else {
269+
// Fallback: show link-only
270+
canvas.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#7c3aed;font-size:0.8rem;"><i class="fas fa-qrcode" style="font-size:3rem;"></i></div>';
271+
}
272+
273+
linkEl.textContent = roomUrl;
274+
directLink.href = roomUrl;
275+
container.style.display = 'block';
276+
277+
// Store the token
278+
window._room360Token = token;
279+
if (window.showToast) showToast('QR code generated! Candidate can scan to start room camera.', 'success');
280+
}
281+
282+
function copy360Link() {
283+
var linkEl = document.getElementById('qr360Link');
284+
if (!linkEl) return;
285+
navigator.clipboard.writeText(linkEl.textContent).then(function() {
286+
if (window.showToast) showToast('Room camera link copied!', 'success');
287+
}).catch(function() {
288+
// Fallback
289+
var ta = document.createElement('textarea');
290+
ta.value = linkEl.textContent;
291+
document.body.appendChild(ta);
292+
ta.select();
293+
document.execCommand('copy');
294+
document.body.removeChild(ta);
295+
if (window.showToast) showToast('Room camera link copied!', 'success');
296+
});
297+
}

0 commit comments

Comments
 (0)