diff --git a/app/Http/Controllers/InterviewController.php b/app/Http/Controllers/InterviewController.php index 9c2b7d6..7c68e63 100644 --- a/app/Http/Controllers/InterviewController.php +++ b/app/Http/Controllers/InterviewController.php @@ -309,6 +309,19 @@ public function sendWarning(Request $request, $id) return response()->json(['success' => true, 'warning' => $warning]); } + /** + * Live Sync Candidate Code (before submission). + */ + public function syncCandidateCode(Request $request, $id) + { + $interview = Interview::findOrFail($id); + $interview->update([ + 'submitted_code' => $request->input('code'), + 'submitted_language' => $request->input('language', $interview->language), + ]); + return response()->json(['success' => true]); + } + /** * Save Candidate Live Notepad Content. */ diff --git a/resources/views/interview/candidate_room.blade.php b/resources/views/interview/candidate_room.blade.php index 077487c..adceb0a 100644 --- a/resources/views/interview/candidate_room.blade.php +++ b/resources/views/interview/candidate_room.blade.php @@ -383,6 +383,24 @@ automaticLayout: true, minimap: { enabled: true } }); + + // Real-Time Code Sync to Interviewer (Before Submission) + let codeSyncTimeout = null; + editor.onDidChangeModelContent(function () { + clearTimeout(codeSyncTimeout); + codeSyncTimeout = setTimeout(function () { + const code = editor.getValue(); + const lang = document.getElementById('language-select').value; + fetch(`{{ route("interview.candidate.sync-code", $interview->id) }}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': '{{ csrf_token() }}' + }, + body: JSON.stringify({ code: code, language: lang }) + }); + }, 600); + }); }); // Handle Language Switch diff --git a/resources/views/interview/index.blade.php b/resources/views/interview/index.blade.php index 828bf11..fbf6691 100644 --- a/resources/views/interview/index.blade.php +++ b/resources/views/interview/index.blade.php @@ -669,6 +669,8 @@ function toggleFullscreenScreen() { let activeViolations = []; let sosPollInterval = null; + let acknowledgedViolationCount = 0; + let sosDismissed = false; function switchIdeTab(tab) { document.getElementById('tab-content-code').style.display = (tab === 'code') ? 'block' : 'none'; @@ -687,6 +689,9 @@ function switchIdeTab(tab) { function openReviewModal(id, name, uid) { currentInterviewId = id; + acknowledgedViolationCount = 0; + sosDismissed = false; + document.getElementById('modal-cand-name').innerText = name; document.getElementById('modal-cand-uid').innerText = 'Unique Submission ID: ' + uid; @@ -703,7 +708,7 @@ function openReviewModal(id, name, uid) { pollReviewData(); clearInterval(sosPollInterval); - sosPollInterval = setInterval(pollReviewData, 4000); + sosPollInterval = setInterval(pollReviewData, 2000); } function pollReviewData() { @@ -712,7 +717,7 @@ function pollReviewData() { .then(r => r.json()) .then(data => { document.getElementById('modal-code-lang').innerText = (data.submitted_language || 'UNKNOWN').toUpperCase(); - document.getElementById('modal-code-display').innerText = data.submitted_code || '// Candidate has not submitted code yet.'; + document.getElementById('modal-code-display').innerText = data.submitted_code || '// Candidate has not typed any code yet.'; document.getElementById('modal-output-display').innerText = data.code_output || 'No execution output.'; document.getElementById('modal-notes-display').innerText = data.candidate_notes || 'No candidate notes written yet.'; @@ -728,17 +733,15 @@ function pollReviewData() { } let logsHtml = ''; - let cheatingDetected = false; - activeViolations = []; + let currentViolations = []; if (data.proctor_logs && data.proctor_logs.length > 0) { data.proctor_logs.forEach(l => { let icon = '🔴'; if (l.type === 'focus_lost') icon = '🟡'; if (l.type === 'gaze_anomaly') icon = '👁️'; - if (l.type === 'paste_event' || l.type === 'tab_switch' || l.type === 'focus_lost') { - cheatingDetected = true; - activeViolations.push(l); + if (l.type === 'paste_event' || l.type === 'tab_switch' || l.type === 'focus_lost' || l.type === 'gaze_anomaly') { + currentViolations.push(l); } logsHtml += `