CoolFace
Modelpublic

Prathmesh0001/interview-system

sourceHugging Facemitupdated 8mo agoView on Hugging Face
0likes
interview.html461 linesDownload Raw Back to templates
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>