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 // Start speech recognition
292 if (recognition) {
293 isRecording = true;
294 recognition.start();
295 console.log('Speech recognition started');
296 }
297
298 // Update UI
299 safeSetStyle('startRecording', { display: 'none' });
300 safeSetStyle('stopRecording', { display: 'block' });
301 safeSetDisabled('stopRecording', false);
302 safeToggleClass('recordingIndicator', 'hidden', false);
303 safeSetText('statusMessage', '๐ค Recording... Speak your answer clearly');
304
305 // Start timer
306 recordingStartTime = Date.now();
307 timerInterval = setInterval(updateTimer, 1000);
308
309 } catch (error) {
310 console.error('Error starting recording:', error);
311 alert('Error starting recording: ' + error.message);
312 isRecording = false;
313 }
314 });
315 }
316
317 // Stop recording
318 const stopRecordingBtn = safeGetElement('stopRecording');
319 if (stopRecordingBtn) {
320 stopRecordingBtn.addEventListener('click', () => {
321 console.log('Stop recording clicked');
322
323 // Stop speech recognition
324 isRecording = false;
325 if (recognition) {
326 try {
327 recognition.stop();
328 console.log('Speech recognition stopped');
329 } catch (e) {
330 console.log('Error stopping recognition:', e);
331 }
332 }
333
334 // Stop timer
335 clearInterval(timerInterval);
336
337 // Update UI
338 safeSetStyle('stopRecording', { display: 'none' });
339 safeToggleClass('recordingIndicator', 'hidden', true);
340 safeSetText('statusMessage', 'Processing your answer...');
341
342 // Show submit button
343 if (currentTranscript.trim().length > 0) {
344 safeSetStyle('submitAnswer', { display: 'block' });
345 safeSetDisabled('submitAnswer', false);
346 safeSetText('statusMessage', 'Review your answer and click Submit');
347 } else {
348 safeSetText('statusMessage', 'No speech detected. Please try again.');
349 safeSetStyle('startRecording', { display: 'block' });
350 safeSetDisabled('startRecording', false);
351 }
352 });
353 }
354
355 // Submit answer
356 const submitAnswerBtn = safeGetElement('submitAnswer');
357 if (submitAnswerBtn) {
358 submitAnswerBtn.addEventListener('click', async () => {
359 const answer = currentTranscript.trim();
360
361 if (!answer) {
362 alert('Please record an answer first');
363 return;
364 }
365
366 safeSetDisabled('submitAnswer', true);
367 safeSetText('statusMessage', 'Analyzing your answer...');
368
369 try {
370 const duration = (Date.now() - recordingStartTime) / 1000;
371
372 const response = await fetch(`/api/submit-answer/${sessionId}`, {
373 method: 'POST',
374 headers: {
375 'Content-Type': 'application/json'
376 },
377 body: JSON.stringify({
378 answer: answer,
379 duration: duration
380 })
381 });
382
383 const data = await response.json();
384
385 if (data.success) {
386 // Show feedback
387 safeSetText('answerScore', data.score.toFixed(1));
388 const feedbackList = safeGetElement('feedbackList');
389 if (feedbackList) {
390 feedbackList.innerHTML = '';
391 data.feedback.forEach(fb => {
392 const li = document.createElement('li');
393 li.textContent = fb;
394 feedbackList.appendChild(li);
395 });
396 }
397 safeSetStyle('feedbackSection', { display: 'block' });
398
399 // Wait 3 seconds then move to next question
400 safeSetText('statusMessage',
401 data.is_complete ? 'Interview complete! Redirecting...' : 'Loading next question...');
402
403 setTimeout(() => {
404 if (data.is_complete) {
405 window.location.href = `/results/${sessionId}`;
406 } else {
407 // Reset for next question
408 safeSetStyle('submitAnswer', { display: 'none' });
409 safeSetStyle('startRecording', { display: 'block' });
410 safeSetText('recordingTimer', '00:00');
411 safeSetHTML('transcript', '<em>Your answer will appear here as you speak...</em>');
412 safeSetText('wordCount', 'Words: 0');
413 loadQuestion();
414 }
415 }, 3000);
416 } else {
417 alert('Error submitting answer: ' + (data.error || 'Unknown error'));
418 safeSetDisabled('submitAnswer', false);
419 }
420 } catch (error) {
421 console.error('Error submitting answer:', error);
422 alert('Error submitting answer: ' + error.message);
423 safeSetDisabled('submitAnswer', false);
424 }
425 });
426 }
427
428 // Update recording timer
429 function updateTimer() {
430 const elapsed = Math.floor((Date.now() - recordingStartTime) / 1000);
431 const minutes = Math.floor(elapsed / 60);
432 const seconds = elapsed % 60;
433 safeSetText('recordingTimer',
434 `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`);
435 }
436
437 // Initialize on page load
438 window.addEventListener('load', () => {
439 console.log('Page loaded, initializing...');
440 initVideo();
441 });
442
443 // Cleanup on page unload
444 window.addEventListener('beforeunload', () => {
445 isRecording = false;
446 if (recognition) {
447 try {
448 recognition.stop();
449 } catch (e) {
450 console.log('Cleanup error:', e);
451 }
452 }
453 });
454 </script>
455</body>
456
457</html>