Prathmesh0001/interview-system
0
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <title>Interview in Progress - AI Mock Interview</title>
8 <link rel="stylesheet" href="/static/css/style.css">
9</head>
10
11<body class="interview-page">
12 <div class="interview-container">
13 <!-- Left Panel: Video and Question -->
14 <div class="left-panel">
15 <!-- Video Section -->
16 <div class="video-section">
17 <video id="videoElement" autoplay muted></video>
18 <div class="video-status" id="videoStatus">
19 <div id="recordingIndicator" class="hidden">
20 ๐ด Recording...
21 </div>
22 </div>
23 </div>
24
25 <!-- Question Display -->
26 <div class="question-display">
27 <div class="question-header">
28 <span class="question-number" id="questionNumber">Question 1 of 5</span>
29 <span class="question-category" id="questionCategory">Resume-Based</span>
30 </div>
31 <div class="question-text" id="questionText">
32 Loading question...
33 </div>
34 <div class="question-focus" id="questionFocus"></div>
35 </div>
36 </div>
37
38 <!-- Right Panel: Controls and Transcript -->
39 <div class="right-panel">
40 <div class="controls-section">
41 <h3>Interview Controls</h3>
42
43 <!-- Recording Controls -->
44 <div class="recording-controls">
45 <button id="startRecording" class="btn-record" disabled>
46 ๐ค Start Recording
47 </button>
48 <button id="stopRecording" class="btn-stop" disabled style="display: none;">
49 โน๏ธ Stop Recording
50 </button>
51 <div class="recording-timer" id="recordingTimer">00:00</div>
52 </div>
53
54 <div class="status-message" id="statusMessage">
55 Loading interview...
56 </div>
57
58 <!-- Transcript Display -->
59 <div class="transcript-section">
60 <h4>Your Answer:</h4>
61 <div id="transcript" class="transcript-box">
62 <em>Your answer will appear here as you speak...</em>
63 </div>
64 <div id="wordCount" class="word-count">Words: 0</div>
65 </div>
66
67 <!-- Submit Button -->
68 <button id="submitAnswer" class="btn-submit" disabled style="display: none;">
69 Submit Answer & Continue
70 </button>
71
72 <!-- Feedback Display -->
73 <div id="feedbackSection" class="feedback-section" style="display: none;">
74 <h4>Quick Feedback:</h4>
75 <div class="score-display">
76 Score: <span id="answerScore">0</span>/100
77 </div>
78 <ul id="feedbackList"></ul>
79 </div>
80 </div>
81
82 <!-- Progress Bar -->
83 <div class="progress-section">
84 <div class="progress-bar">
85 <div id="progressFill" class="progress-fill" style="width: 0%"></div>
86 </div>
87 <div class="progress-text" id="progressText">0 of 5 questions completed</div>
88 </div>
89 </div>
90 </div>
91
92 <script>
93 const sessionId = '{{ session_id }}';
94 let mediaRecorder;
95 let audioChunks = [];
96 let recordingStartTime;
97 let timerInterval;
98 let currentTranscript = '';
99 let recognition;
100 let isRecording = false;
101
102 // Safe DOM helper functions
103 function safeGetElement(id) {
104 const element = document.getElementById(id);
105 if (!element) {
106 console.error(`Element not found: ${id}`);
107 }
108 return element;
109 }
110
111 function safeSetText(id, text) {
112 const element = safeGetElement(id);
113 if (element) {
114 element.textContent = text;
115 }
116 }
117
118 function safeSetHTML(id, html) {
119 const element = safeGetElement(id);
120 if (element) {
121 element.innerHTML = html;
122 }
123 }
124
125 function safeSetStyle(id, styles) {
126 const element = safeGetElement(id);
127 if (element) {
128 Object.assign(element.style, styles);
129 }
130 }
131
132 function safeToggleClass(id, className, add) {
133 const element = safeGetElement(id);
134 if (element && element.classList) {
135 if (add) {
136 element.classList.add(className);
137 } else {
138 element.classList.remove(className);
139 }
140 }
141 }
142
143 function safeSetDisabled(id, disabled) {
144 const element = safeGetElement(id);
145 if (element) {
146 element.disabled = disabled;
147 }
148 }
149
150 // Initialize Speech Recognition
151 if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
152 const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
153 recognition = new SpeechRecognition();
154 recognition.continuous = true;
155 recognition.interimResults = true;
156 recognition.lang = 'en-US';
157
158 recognition.onresult = (event) => {
159 let interimTranscript = '';
160 let finalTranscript = '';
161
162 for (let i = event.resultIndex; i < event.results.length; i++) {
163 const transcript = event.results[i][0].transcript;
164 if (event.results[i].isFinal) {
165 finalTranscript += transcript + ' ';
166 } else {
167 interimTranscript += transcript;
168 }
169 }
170
171 if (finalTranscript) {
172 currentTranscript += finalTranscript;
173 }
174
175 const fullText = currentTranscript + interimTranscript;
176 safeSetText('transcript', fullText || 'Listening...');
177
178 // Update word count
179 const wordCount = fullText.trim().split(/\s+/).filter(w => w.length > 0).length;
180 safeSetText('wordCount', `Words: ${wordCount}`);
181 };
182
183 recognition.onerror = (event) => {
184 console.error('Speech recognition error:', event.error);
185 if (event.error === 'no-speech') {
186 safeSetHTML('transcript', '<em>No speech detected. Please try again.</em>');
187 } else if (event.error === 'aborted') {
188 console.log('Recognition stopped');
189 }
190 };
191
192 recognition.onend = () => {
193 console.log('Recognition ended');
194 if (isRecording) {
195 try {
196 recognition.start();
197 } catch (e) {
198 console.log('Recognition restart failed:', e);
199 }
200 }
201 };
202 } else {
203 alert('Speech recognition is not supported in your browser. Please use Chrome or Edge.');
204 }
205
206 // Initialize video
207 async function initVideo() {
208 try {
209 const stream = await navigator.mediaDevices.getUserMedia({
210 video: true,
211 audio: true
212 });
213 const videoElement = safeGetElement('videoElement');
214 if (videoElement) {
215 videoElement.srcObject = stream;
216 }
217 safeSetText('videoStatus', 'โ
Camera Ready');
218
219 // Load first question
220 loadQuestion();
221 } catch (error) {
222 console.error('Error accessing camera:', error);
223 safeSetText('videoStatus', 'โ Camera Error');
224 alert('Please allow camera and microphone access to continue.');
225 }
226 }
227
228 // Load current question
229 async function loadQuestion() {
230 try {
231 safeSetText('statusMessage', 'Loading question...');
232
233 const response = await fetch(`/api/get-question/${sessionId}`);
234 const data = await response.json();
235
236 if (data.completed) {
237 window.location.href = `/results/${sessionId}`;
238 return;
239 }
240
241 // Update question display
242 safeSetText('questionNumber', `Question ${data.question_number} of ${data.total_questions}`);
243 safeSetText('questionCategory', data.category || 'Resume-Based');
244 safeSetText('questionText', data.question);
245 safeSetText('questionFocus', data.focus_area || '');
246
247 // Update progress
248 const progress = ((data.question_number - 1) / data.total_questions) * 100;
249 safeSetStyle('progressFill', { width: `${progress}%` });
250 safeSetText('progressText', `${data.question_number - 1} of ${data.total_questions} questions completed`);
251
252 // Enable recording
253 safeSetDisabled('startRecording', false);
254 safeSetText('statusMessage', 'Ready to record your answer');
255
256 // Speak the question
257 speakQuestion(data.question);
258
259 } catch (error) {
260 console.error('Error loading question:', error);
261 safeSetText('statusMessage', 'Error loading question: ' + error.message);
262 }
263 }
264
265 // Text-to-speech for question
266 function speakQuestion(text) {
267 if ('speechSynthesis' in window) {
268 speechSynthesis.cancel();
269
270 const utterance = new SpeechSynthesisUtterance(text);
271 utterance.rate = 0.9;
272 utterance.pitch = 1;
273 utterance.volume = 1;
274 speechSynthesis.speak(utterance);
275 }
276 }
277
278 // Start recording
279 const startRecordingBtn = safeGetElement('startRecording');
280 if (startRecordingBtn) {
281 startRecordingBtn.addEventListener('click', async () => {
282 try {
283 console.log('Start recording clicked');
284
285 // Reset transcript
286 currentTranscript = '';
287 safeSetText('transcript', 'Listening...');
288 safeSetText('wordCount', 'Words: 0');
289 safeSetStyle('feedbackSection', { display: 'none' });
290
291 if (recognition && !isRecording) {
292 isRecording = true;
293 try {
294 recognition.start();
295 console.log('Speech recognition started');
296 } catch (e) {
297 console.error('Failed to start recognition:', e);
298 isRecording = false;
299 }
300 }
301
302 // Update UI
303 safeSetStyle('startRecording', { display: 'none' });
304 safeSetStyle('stopRecording', { display: 'block' });
305 safeSetDisabled('stopRecording', false);
306 safeToggleClass('recordingIndicator', 'hidden', false);
307 safeSetText('statusMessage', '๐ค Recording... Speak your answer clearly');
308
309 // Start timer
310 recordingStartTime = Date.now();
311 timerInterval = setInterval(updateTimer, 1000);
312
313 } catch (error) {
314 console.error('Error starting recording:', error);
315 alert('Error starting recording: ' + error.message);
316 isRecording = false;
317 }
318 });
319 }
320
321 // Stop recording
322 const stopRecordingBtn = safeGetElement('stopRecording');
323 if (stopRecordingBtn) {
324 stopRecordingBtn.addEventListener('click', () => {
325 console.log('Stop recording clicked');
326
327 // Stop speech recognition
328 isRecording = false;
329 if (recognition) {
330 try {
331 recognition.stop();
332 console.log('Speech recognition stopped');
333 } catch (e) {
334 console.log('Error stopping recognition:', e);
335 }
336 }
337
338 // Stop timer
339 clearInterval(timerInterval);
340
341 // Update UI
342 safeSetStyle('stopRecording', { display: 'none' });
343 safeToggleClass('recordingIndicator', 'hidden', true);
344 safeSetText('statusMessage', 'Processing your answer...');
345
346 // Show submit button
347 if (currentTranscript.trim().length > 0) {
348 safeSetStyle('submitAnswer', { display: 'block' });
349 safeSetDisabled('submitAnswer', false);
350 safeSetText('statusMessage', 'Review your answer and click Submit');
351 } else {
352 safeSetText('statusMessage', 'No speech detected. Please try again.');
353 safeSetStyle('startRecording', { display: 'block' });
354 safeSetDisabled('startRecording', false);
355 }
356 });
357 }
358
359 // Submit answer
360 const submitAnswerBtn = safeGetElement('submitAnswer');
361 if (submitAnswerBtn) {
362 submitAnswerBtn.addEventListener('click', async () => {
363 const answer = currentTranscript.trim();
364
365 if (!answer) {
366 alert('Please record an answer first');
367 return;
368 }
369
370 safeSetDisabled('submitAnswer', true);
371 safeSetText('statusMessage', 'Analyzing your answer...');
372
373 try {
374 const duration = (Date.now() - recordingStartTime) / 1000;
375
376 const response = await fetch(`/api/submit-answer/${sessionId}`, {
377 method: 'POST',
378 headers: {
379 'Content-Type': 'application/json'
380 },
381 body: JSON.stringify({
382 answer: answer,
383 duration: duration
384 })
385 });
386
387 const data = await response.json();
388
389 if (data.success) {
390 // Show feedback
391 safeSetText('answerScore', data.score.toFixed(1));
392 const feedbackList = safeGetElement('feedbackList');
393 if (feedbackList) {
394 feedbackList.innerHTML = '';
395 data.feedback.forEach(fb => {
396 const li = document.createElement('li');
397 li.textContent = fb;
398 feedbackList.appendChild(li);
399 });
400 }
401 safeSetStyle('feedbackSection', { display: 'block' });
402
403 // Wait 3 seconds then move to next question
404 safeSetText('statusMessage',
405 data.is_complete ? 'Interview complete! Redirecting...' : 'Loading next question...');
406
407 setTimeout(() => {
408 if (data.is_complete) {
409 window.location.href = `/results/${sessionId}`;
410 } else {
411 // Reset for next question
412 safeSetStyle('submitAnswer', { display: 'none' });
413 safeSetStyle('startRecording', { display: 'block' });
414 safeSetText('recordingTimer', '00:00');
415 safeSetHTML('transcript', '<em>Your answer will appear here as you speak...</em>');
416 safeSetText('wordCount', 'Words: 0');
417 loadQuestion();
418 }
419 }, 3000);
420 } else {
421 alert('Error submitting answer: ' + (data.error || 'Unknown error'));
422 safeSetDisabled('submitAnswer', false);
423 }
424 } catch (error) {
425 console.error('Error submitting answer:', error);
426 alert('Error submitting answer: ' + error.message);
427 safeSetDisabled('submitAnswer', false);
428 }
429 });
430 }
431
432 // Update recording timer
433 function updateTimer() {
434 const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
435 const minutes = Math.floor(elapsed / 60);
436 const seconds = elapsed % 60;
437 safeSetText('recordingTimer',
438 `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`);
439 }
440
441 // Initialize on page load
442 window.addEventListener('load', () => {
443 console.log('Page loaded, initializing...');
444 initVideo();
445 });
446
447 // Cleanup on page unload
448 window.addEventListener('beforeunload', () => {
449 isRecording = false;
450 if (recognition) {
451 try {
452 recognition.stop();
453 } catch (e) {
454 console.log('Cleanup error:', e);
455 }
456 }
457 });
458 </script>
459</body>
460
461</html>