diff --git a/public/Screenshot_15.png b/public/Screenshot_15.png new file mode 100644 index 0000000..abadba9 Binary files /dev/null and b/public/Screenshot_15.png differ diff --git a/resources/views/interview/candidate_room.blade.php b/resources/views/interview/candidate_room.blade.php index 57110bb..57a0e24 100644 --- a/resources/views/interview/candidate_room.blade.php +++ b/resources/views/interview/candidate_room.blade.php @@ -953,12 +953,25 @@ function analyzeCameraFrame() { // 5. 2-Way Real-Time Chat Message Polling (No popup alerts!) function pollCandidateChat() { - fetch(`{{ route('interview.poll', $interview->id) }}`) - .then(r => r.json()) + fetch(`{{ route('interview.candidate.poll', $interview->id) }}`, { + headers: { 'Accept': 'application/json' } + }) + .then(r => { + if (!r.ok) return null; + const contentType = r.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) return null; + return r.json(); + }) .then(data => { + if (!data) return; const chatHistory = document.getElementById('candidate-chat-history'); if (!chatHistory) return; + if (data.status === 'blocked' || data.is_expired) { + window.location.reload(); + return; + } + if (data.call_status === 'ended') { candidateLeaveCall(); const badge = document.getElementById('call-status-badge'); @@ -988,7 +1001,8 @@ function pollCandidateChat() { chatHistory.innerHTML = html; chatHistory.scrollTop = chatHistory.scrollHeight; } - }); + }) + .catch(() => {}); } setInterval(pollCandidateChat, 3000); pollCandidateChat(); diff --git a/resources/views/interview/index.blade.php b/resources/views/interview/index.blade.php index 6315011..03338fc 100644 --- a/resources/views/interview/index.blade.php +++ b/resources/views/interview/index.blade.php @@ -813,9 +813,17 @@ function openReviewModal(id, name, uid) { function pollReviewData() { if (!currentInterviewId) return; - fetch(`{{ route('interview.poll', ':id') }}`.replace(':id', currentInterviewId)) - .then(r => r.json()) + fetch(`{{ route('interview.poll', ':id') }}`.replace(':id', currentInterviewId), { + headers: { 'Accept': 'application/json' } + }) + .then(r => { + if (!r.ok) return null; + const contentType = r.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) return null; + return r.json(); + }) .then(data => { + if (!data) return; document.getElementById('modal-code-lang').innerText = (data.submitted_language || 'UNKNOWN').toUpperCase(); 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.'; @@ -946,7 +954,8 @@ function pollReviewData() { } else { sosBtn.style.display = 'none'; } - }); + }) + .catch(() => {}); } function openSosModal() { diff --git a/routes/web.php b/routes/web.php index 8391d54..ebd3a6c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -36,6 +36,7 @@ Route::post('/candidate/notes/{id}', [InterviewController::class, 'saveCandidateNotes'])->name('interview.candidate.notes'); Route::post('/candidate/drawing/{id}', [InterviewController::class, 'saveCandidateDrawing'])->name('interview.candidate.drawing'); Route::post('/candidate/send-message/{id}', [InterviewController::class, 'candidateSendMessage'])->name('interview.candidate.send-message'); +Route::get('/candidate/poll/{id}', [InterviewController::class, 'getPollData'])->name('interview.candidate.poll'); // User Dashboard Portal (requires authenticated user) Route::middleware(['auth', 'role:user', 'verify-ms-session'])->group(function () {