Danielchris145 commited on
Commit
3d7ca20
·
verified ·
1 Parent(s): f0eb1ad

Deploy complete premium UI application

Browse files
Files changed (5) hide show
  1. app.py +10 -0
  2. static/assets/js/config.js +14 -8
  3. static/index.html +169 -711
  4. static/login.html +159 -0
  5. static/signup.html +188 -0
app.py CHANGED
@@ -74,6 +74,16 @@ def generate_content_with_groq(prompt):
74
  def index():
75
  return send_file('static/index.html')
76
 
 
 
 
 
 
 
 
 
 
 
77
  # Serve static assets
78
  @app.route('/assets/<path:filename>')
79
  def serve_assets(filename):
 
74
  def index():
75
  return send_file('static/index.html')
76
 
77
+ # Serve login page
78
+ @app.route('/login.html')
79
+ def login():
80
+ return send_file('static/login.html')
81
+
82
+ # Serve signup page
83
+ @app.route('/signup.html')
84
+ def signup():
85
+ return send_file('static/signup.html')
86
+
87
  # Serve static assets
88
  @app.route('/assets/<path:filename>')
89
  def serve_assets(filename):
static/assets/js/config.js CHANGED
@@ -1,12 +1,7 @@
1
- // API Configuration
2
- // Change this to your production URL when deploying
3
  const API_CONFIG = {
4
- // Base URL for API calls
5
- // Development: Python backend runs on port 5000
6
- // Production: 'https://your-domain.com/api'
7
- BASE_URL: window.location.hostname === 'localhost'
8
- ? 'http://localhost:5000'
9
- : window.location.origin,
10
 
11
  // Timeout for API requests (in milliseconds)
12
  TIMEOUT: 30000,
@@ -20,6 +15,17 @@ function getApiUrl(endpoint) {
20
  return `${API_CONFIG.BASE_URL}/${endpoint.replace(/^\//, '')}`;
21
  }
22
 
 
 
 
 
 
 
 
 
 
 
23
  // Export for use in other modules
24
  window.API_CONFIG = API_CONFIG;
25
  window.getApiUrl = getApiUrl;
 
 
1
+ // API Configuration for HuggingFace Spaces
 
2
  const API_CONFIG = {
3
+ // Base URL - same origin for HuggingFace deployment
4
+ BASE_URL: window.location.origin,
 
 
 
 
5
 
6
  // Timeout for API requests (in milliseconds)
7
  TIMEOUT: 30000,
 
15
  return `${API_CONFIG.BASE_URL}/${endpoint.replace(/^\//, '')}`;
16
  }
17
 
18
+ // Session ID management
19
+ function getSessionId() {
20
+ let sessionId = sessionStorage.getItem('sessionId');
21
+ if (!sessionId) {
22
+ sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substring(2, 11);
23
+ sessionStorage.setItem('sessionId', sessionId);
24
+ }
25
+ return sessionId;
26
+ }
27
+
28
  // Export for use in other modules
29
  window.API_CONFIG = API_CONFIG;
30
  window.getApiUrl = getApiUrl;
31
+ window.getSessionId = getSessionId;
static/index.html CHANGED
@@ -9,56 +9,45 @@
9
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
  <link rel="stylesheet" href="assets/css/premium.css">
12
- <style>
13
- .feedback-box { background: rgba(16, 185, 129, 0.1); padding: 20px; border-radius: 10px; margin: 15px 0; border-left: 4px solid var(--color-success); }
14
- .feedback-box.hidden { display: none; }
15
- .profile-display { display: grid; gap: 1rem; padding: 1.5rem; background: var(--color-surface); border-radius: var(--radius-lg); border: 1px solid var(--glass-border); }
16
- .profile-item { display: flex; gap: 1rem; align-items: start; }
17
- .profile-label { font-weight: 600; color: var(--color-text-secondary); min-width: 120px; }
18
- .profile-value { color: var(--color-text-primary); }
19
- .skills-tags { display: flex; flex-wrap: wrap; gap: 0.5rem; }
20
- .skill-tag { padding: 4px 12px; background: rgba(99, 102, 241, 0.1); border: 1px solid rgba(99, 102, 241, 0.3); border-radius: 20px; font-size: 14px; color: var(--color-primary); }
21
- .form-input { width: 100%; padding: 14px 16px; background: var(--color-surface); border: 1px solid var(--glass-border); border-radius: var(--radius-md); color: var(--color-text-primary); font-size: 16px; }
22
- .form-input:focus { outline: none; border-color: var(--color-primary); }
23
- .form-label { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: var(--color-text-secondary); }
24
- .insights-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; }
25
- .insights-column h4 { margin-bottom: 1rem; color: var(--color-text-secondary); }
26
- .insight-item { padding: 12px; margin: 8px 0; border-radius: 8px; display: flex; align-items: center; gap: 10px; }
27
- .insight-item.strength { background: rgba(16, 185, 129, 0.1); border-left: 3px solid var(--color-success); }
28
- .insight-item.improvement { background: rgba(239, 68, 68, 0.1); border-left: 3px solid var(--color-danger); }
29
- .recommendation.recommended { border-color: var(--color-success); color: var(--color-success); background: rgba(16, 185, 129, 0.1); }
30
- .recommendation.consider { border-color: var(--color-warning); color: var(--color-warning); background: rgba(245, 158, 11, 0.1); }
31
- .recommendation.not-recommended { border-color: var(--color-danger); color: var(--color-danger); background: rgba(239, 68, 68, 0.1); }
32
- .score-item { margin: 1rem 0; }
33
- .score-label-row { display: flex; justify-content: space-between; margin-bottom: 0.5rem; }
34
- .score-name { color: var(--color-text-secondary); }
35
- .score-percent { color: var(--color-primary); font-weight: 600; }
36
- .score-bar { height: 8px; background: var(--color-surface); border-radius: 4px; overflow: hidden; }
37
- .score-bar-fill { height: 100%; background: linear-gradient(90deg, var(--color-primary), var(--color-secondary)); border-radius: 4px; transition: width 1s ease; }
38
- </style>
39
  </head>
40
  <body>
41
  <!-- Animated Background -->
42
  <div class="premium-bg">
43
  <div class="gradient-mesh"></div>
44
- <div class="neural-network"><canvas id="neuralCanvas"></canvas></div>
 
 
45
  </div>
46
 
47
  <!-- Navigation -->
48
  <nav class="premium-nav">
49
  <div class="nav-container">
50
  <div class="nav-brand">
51
- <div class="brand-icon"><i class="fas fa-brain"></i></div>
 
 
52
  <span class="brand-text">IntervuAI<span class="brand-pro">Pro</span></span>
53
  </div>
54
  <div class="nav-menu">
55
- <a href="#" class="nav-link active"><i class="fas fa-home"></i><span>Dashboard</span></a>
 
 
 
 
 
 
 
56
  <div class="nav-user">
57
- <div class="user-avatar"><i class="fas fa-user"></i></div>
 
 
58
  <div class="user-info">
59
- <span class="user-name">Guest User</span>
60
- <span class="user-role">Premium Access</span>
61
  </div>
 
 
 
62
  </div>
63
  </div>
64
  </div>
@@ -67,20 +56,47 @@
67
  <!-- Main Content -->
68
  <main class="premium-main">
69
  <!-- Hero Section -->
70
- <section class="hero-section" id="heroSection">
71
  <div class="hero-content">
72
- <div class="hero-badge"><i class="fas fa-sparkles"></i><span>Powered by Advanced AI</span></div>
73
- <h1 class="hero-title">Next-Generation<br><span class="gradient-text">AI Interview</span><br>Platform</h1>
74
- <p class="hero-subtitle">Experience intelligent candidate evaluation with real-time AI analysis, automated scoring, and comprehensive insights.</p>
 
 
 
 
 
 
 
 
 
 
75
  <div class="hero-actions">
76
- <button class="btn-primary" onclick="scrollToUpload()"><i class="fas fa-rocket"></i><span>Start Interview</span><div class="btn-glow"></div></button>
 
 
 
 
 
 
 
 
77
  </div>
78
  <div class="hero-stats">
79
- <div class="stat-item"><div class="stat-value">10K+</div><div class="stat-label">Interviews</div></div>
 
 
 
80
  <div class="stat-divider"></div>
81
- <div class="stat-item"><div class="stat-value">98%</div><div class="stat-label">Accuracy</div></div>
 
 
 
82
  <div class="stat-divider"></div>
83
- <div class="stat-item"><div class="stat-value">AI</div><div class="stat-label">Powered</div></div>
 
 
 
84
  </div>
85
  </div>
86
  </section>
@@ -89,45 +105,38 @@
89
  <section class="upload-section" id="uploadSection">
90
  <div class="glass-card large">
91
  <div class="card-header">
92
- <div class="header-icon"><i class="fas fa-cloud-upload-alt"></i></div>
 
 
93
  <div class="header-content">
94
  <h2 class="card-title">Upload Resume</h2>
95
  <p class="card-subtitle">AI-powered resume analysis in seconds</p>
96
  </div>
97
  </div>
 
98
  <div class="upload-area" id="uploadArea">
99
- <input type="file" id="resumeInput" accept=".pdf" hidden>
100
- <div class="upload-icon"><i class="fas fa-file-upload"></i></div>
 
 
101
  <h3 class="upload-title">Drop your resume here</h3>
102
  <p class="upload-text">or click to browse</p>
103
- <div class="upload-formats"><span class="format-badge">PDF</span></div>
104
- </div>
105
- </div>
106
- </section>
107
-
108
- <!-- Setup Section -->
109
- <section class="upload-section hidden" id="setupSection">
110
- <div class="glass-card large">
111
- <div class="card-header">
112
- <div class="header-icon"><i class="fas fa-user-check"></i></div>
113
- <div class="header-content">
114
- <h2 class="card-title">Candidate Profile</h2>
115
- <p class="card-subtitle">Review and configure interview</p>
116
  </div>
117
- </div>
118
- <div class="profile-display" id="profileDisplay"></div>
119
- <div class="form-group" style="margin-top: 2rem;">
120
- <label class="form-label"><i class="fas fa-briefcase"></i> Position/Role</label>
121
- <input type="text" id="positionInput" class="form-input" placeholder="e.g., Senior Software Engineer">
122
- </div>
123
- <div class="interview-actions" style="margin-top: 2rem;">
124
- <button class="btn-secondary" onclick="location.reload()"><i class="fas fa-arrow-left"></i> Back</button>
125
- <button class="btn-primary" onclick="startInterview()"><span>Start Interview</span><i class="fas fa-arrow-right"></i><div class="btn-glow"></div></button>
126
  </div>
127
  </div>
128
  </section>
129
 
130
- <!-- Interview Section -->
131
  <section class="interview-section hidden" id="interviewSection">
132
  <div class="glass-card">
133
  <div class="interview-header">
@@ -135,70 +144,128 @@
135
  <div class="progress-circle">
136
  <svg viewBox="0 0 100 100">
137
  <circle cx="50" cy="50" r="45"></circle>
138
- <circle cx="50" cy="50" r="45" class="progress-ring" id="progressRing"></circle>
139
  </svg>
140
- <span class="progress-number" id="progressNumber">1/10</span>
141
  </div>
142
  </div>
143
- <div class="timer"><i class="fas fa-clock"></i><span id="timer">00:00</span></div>
 
 
 
144
  </div>
 
145
  <div class="question-container">
146
- <div class="question-tags" id="questionTags"></div>
147
- <h3 class="question-text" id="questionText">Loading question...</h3>
 
 
 
 
 
148
  </div>
 
149
  <div class="answer-container">
150
- <textarea id="answerText" class="answer-input" placeholder="Type your response here..." rows="8"></textarea>
 
 
 
 
 
 
 
 
 
151
  </div>
152
- <div class="feedback-box hidden" id="feedbackBox"></div>
153
  <div class="interview-actions">
154
- <button class="btn-secondary" onclick="location.reload()"><i class="fas fa-home"></i> Home</button>
155
- <button class="btn-primary" onclick="submitAnswer()"><span>Submit Answer</span><i class="fas fa-paper-plane"></i><div class="btn-glow"></div></button>
 
 
 
 
 
 
 
156
  </div>
157
  </div>
158
  </section>
159
 
160
- <!-- Results Section -->
161
  <section class="results-section hidden" id="resultsSection">
162
  <div class="results-header">
163
  <h2 class="section-title">Interview Assessment</h2>
 
 
 
 
 
 
 
 
 
 
 
164
  </div>
 
165
  <div class="results-grid">
166
  <div class="glass-card">
167
  <div class="score-display">
168
  <div class="score-circle">
169
  <svg viewBox="0 0 200 200">
170
  <circle cx="100" cy="100" r="90"></circle>
171
- <circle cx="100" cy="100" r="90" class="score-ring" id="scoreRing"></circle>
172
  </svg>
173
  <div class="score-content">
174
  <span class="score-value" id="overallScore">--</span>
175
  <span class="score-label">Overall</span>
176
  </div>
177
  </div>
178
- <div class="recommendation" id="recommendation"><i class="fas fa-hourglass-half"></i><span>Loading...</span></div>
 
 
 
179
  </div>
180
  </div>
 
181
  <div class="glass-card">
182
  <h3 class="card-title">Detailed Scores</h3>
183
- <div class="score-breakdown" id="scoreBreakdown">
184
  <div class="score-item">
185
- <div class="score-label-row"><span class="score-name">Technical Skills</span><span class="score-percent">0%</span></div>
186
- <div class="score-bar"><div class="score-bar-fill" style="width: 0%"></div></div>
 
 
 
 
 
187
  </div>
188
  <div class="score-item">
189
- <div class="score-label-row"><span class="score-name">Communication</span><span class="score-percent">0%</span></div>
190
- <div class="score-bar"><div class="score-bar-fill" style="width: 0%"></div></div>
 
 
 
 
 
191
  </div>
192
  <div class="score-item">
193
- <div class="score-label-row"><span class="score-name">Soft Skills</span><span class="score-percent">0%</span></div>
194
- <div class="score-bar"><div class="score-bar-fill" style="width: 0%"></div></div>
 
 
 
 
 
195
  </div>
196
  </div>
197
  </div>
198
  </div>
 
199
  <div class="glass-card" style="margin-top: 2rem;">
200
  <h3 class="card-title">Key Insights</h3>
201
- <div class="insights-grid" id="insightsGrid">
202
  <div class="insights-column">
203
  <h4><i class="fas fa-check-circle" style="color: var(--color-success);"></i> Strengths</h4>
204
  <ul id="strengthsList" style="list-style: none; padding: 0;"></ul>
@@ -209,8 +276,13 @@
209
  </div>
210
  </div>
211
  </div>
 
212
  <div class="interview-actions" style="margin-top: 2rem; justify-content: center;">
213
- <button class="btn-primary" onclick="location.reload()"><i class="fas fa-redo"></i> Start New Interview<div class="btn-glow"></div></button>
 
 
 
 
214
  </div>
215
  </section>
216
  </main>
@@ -219,633 +291,19 @@
219
  <div class="loading-overlay hidden" id="loadingOverlay">
220
  <div class="loading-content">
221
  <div class="loading-spinner"></div>
222
- <p class="loading-text" id="loadingText">Processing with AI...</p>
223
  </div>
224
  </div>
225
 
226
- <!-- TensorFlow and BlazeFace for face detection -->
 
 
227
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
228
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface@0.0.7/dist/blazeface.min.js"></script>
229
-
230
- <!-- Config -->
231
- <script>
232
- const API_CONFIG = { BASE_URL: window.location.origin, TIMEOUT: 30000 };
233
- function getApiUrl(endpoint) { return `${API_CONFIG.BASE_URL}/${endpoint.replace(/^\//, '')}`; }
234
- function getSessionId() {
235
- let sid = sessionStorage.getItem('sessionId');
236
- if (!sid) { sid = 'session_' + Date.now() + '_' + Math.random().toString(36).substring(2, 11); sessionStorage.setItem('sessionId', sid); }
237
- return sid;
238
- }
239
- window.API_CONFIG = API_CONFIG;
240
- window.getApiUrl = getApiUrl;
241
- window.getSessionId = getSessionId;
242
- </script>
243
-
244
- <!-- Neural Network Animation -->
245
- <script>
246
- (function() {
247
- const canvas = document.getElementById('neuralCanvas');
248
- if (!canvas) return;
249
- const ctx = canvas.getContext('2d');
250
- let nodes = [], connections = [];
251
- function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; init(); }
252
- function init() {
253
- nodes = []; connections = [];
254
- const nodeCount = Math.floor((canvas.width * canvas.height) / 25000);
255
- for (let i = 0; i < nodeCount; i++) {
256
- nodes.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 0.5, vy: (Math.random() - 0.5) * 0.5, radius: Math.random() * 2 + 1 });
257
- }
258
- }
259
- function animate() {
260
- ctx.clearRect(0, 0, canvas.width, canvas.height);
261
- nodes.forEach(node => {
262
- node.x += node.vx; node.y += node.vy;
263
- if (node.x < 0 || node.x > canvas.width) node.vx *= -1;
264
- if (node.y < 0 || node.y > canvas.height) node.vy *= -1;
265
- ctx.beginPath(); ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
266
- ctx.fillStyle = 'rgba(99, 102, 241, 0.5)'; ctx.fill();
267
- });
268
- for (let i = 0; i < nodes.length; i++) {
269
- for (let j = i + 1; j < nodes.length; j++) {
270
- const dx = nodes[i].x - nodes[j].x, dy = nodes[i].y - nodes[j].y;
271
- const dist = Math.sqrt(dx * dx + dy * dy);
272
- if (dist < 150) {
273
- ctx.beginPath(); ctx.moveTo(nodes[i].x, nodes[i].y); ctx.lineTo(nodes[j].x, nodes[j].y);
274
- ctx.strokeStyle = `rgba(99, 102, 241, ${0.2 * (1 - dist / 150)})`; ctx.stroke();
275
- }
276
- }
277
- }
278
- requestAnimationFrame(animate);
279
- }
280
- window.addEventListener('resize', resize);
281
- resize(); animate();
282
- })();
283
- </script>
284
-
285
- <!-- Exam Security System -->
286
- <script>
287
- class ExamSecurity {
288
- constructor() {
289
- this.tabSwitchCount = 0; this.maxTabSwitches = 2; this.isExamMode = false;
290
- this.questionTimeLimit = 180; this.questionTimer = null; this.currentQuestionStartTime = null;
291
- this.init();
292
- }
293
- init() {
294
- document.addEventListener('contextmenu', (e) => { if (this.isExamMode) { e.preventDefault(); this.showWarning('Right-click disabled'); } });
295
- document.addEventListener('copy', (e) => { if (this.isExamMode) { e.preventDefault(); this.showWarning('Copy disabled'); } });
296
- document.addEventListener('paste', (e) => { if (this.isExamMode) { e.preventDefault(); this.showWarning('Paste disabled'); } });
297
- document.addEventListener('keydown', (e) => {
298
- if (this.isExamMode) {
299
- if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && (e.key === 'I' || e.key === 'J')) || (e.ctrlKey && e.key === 'u')) { e.preventDefault(); return false; }
300
- if (e.ctrlKey && (e.key === 'c' || e.key === 'v' || e.key === 'x')) { e.preventDefault(); this.showWarning('Copy/Paste disabled'); return false; }
301
- }
302
- });
303
- document.addEventListener('visibilitychange', () => { if (this.isExamMode && document.hidden) { const ae = document.activeElement; if (ae && ae.tagName === 'IFRAME') return; this.handleTabSwitch(); } });
304
- window.addEventListener('blur', () => { if (this.isExamMode) { const ae = document.activeElement; if (ae && ae.tagName === 'IFRAME') return; setTimeout(() => { if (!document.hasFocus()) this.handleTabSwitch(); }, 100); } });
305
- document.addEventListener('fullscreenchange', () => { if (this.isExamMode && !document.fullscreenElement) { this.showWarning('Please stay in fullscreen'); this.enterFullscreen(); } });
306
- }
307
- handleTabSwitch() {
308
- this.tabSwitchCount++;
309
- this.logSecurityEvent('tab_switch', { count: this.tabSwitchCount });
310
- if (this.tabSwitchCount >= this.maxTabSwitches) { this.terminateExam(); }
311
- else { this.showCriticalWarning(`TAB SWITCH DETECTED!\n\nYou have ${this.maxTabSwitches - this.tabSwitchCount} warning(s) remaining.`); }
312
- }
313
- terminateExam() {
314
- this.isExamMode = false;
315
- this.logSecurityEvent('exam_terminated', { reason: 'exceeded_tab_switches' });
316
- const overlay = document.createElement('div');
317
- overlay.className = 'critical-warning-overlay show';
318
- overlay.style.zIndex = '99999';
319
- overlay.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-ban"></i></div><h3>Interview Terminated</h3><p>You exceeded the maximum tab switches (2).</p><button class="btn-primary" onclick="location.reload()"><span>Go Home</span></button></div>`;
320
- document.body.appendChild(overlay);
321
- }
322
- async startExamMode() {
323
- if (!this.checkBrowser()) { this.showBrowserError(); return false; }
324
- this.tabSwitchCount = 0;
325
- this.enterFullscreen();
326
- this.showSecurityNotice();
327
- this.logSecurityEvent('exam_started', { browser: this.getBrowserInfo() });
328
- return true;
329
- }
330
- activateExamMode() { this.isExamMode = true; }
331
- checkBrowser() { const ua = navigator.userAgent.toLowerCase(); return ua.includes('chrome') || ua.includes('edg') || ua.includes('brave') || ua.includes('opr') || ua.includes('opera'); }
332
- getBrowserInfo() { const ua = navigator.userAgent.toLowerCase(); if (ua.includes('edg')) return 'Edge'; if (ua.includes('brave')) return 'Brave'; if (ua.includes('opr') || ua.includes('opera')) return 'Opera'; if (ua.includes('chrome')) return 'Chrome'; return 'Unknown'; }
333
- async detectExtensions() { return false; }
334
- showBrowserError() {
335
- const overlay = document.createElement('div');
336
- overlay.className = 'critical-warning-overlay show';
337
- overlay.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-exclamation-circle"></i></div><h3>Unsupported Browser</h3><p>Please use Chrome, Edge, Brave, or Opera.</p><button class="btn-primary" onclick="location.reload()"><span>Go Back</span></button></div>`;
338
- document.body.appendChild(overlay);
339
- }
340
- endExamMode() { this.isExamMode = false; this.exitFullscreen(); if (this.questionTimer) clearInterval(this.questionTimer); }
341
- enterFullscreen() { const e = document.documentElement; if (e.requestFullscreen) e.requestFullscreen().catch(() => {}); }
342
- exitFullscreen() { if (document.exitFullscreen) document.exitFullscreen(); }
343
- startQuestionTimer(onTimeout, customTimeLimit = null) {
344
- this.currentQuestionStartTime = Date.now();
345
- let remaining = customTimeLimit || this.questionTimeLimit;
346
- const update = () => {
347
- const m = Math.floor(remaining / 60), s = remaining % 60;
348
- const el = document.getElementById('timer');
349
- if (el) { el.textContent = `${m.toString().padStart(2,'0')}:${s.toString().padStart(2,'0')}`; el.style.color = remaining < 30 ? 'var(--color-danger)' : 'var(--color-primary)'; }
350
- };
351
- update();
352
- if (this.questionTimer) clearInterval(this.questionTimer);
353
- this.questionTimer = setInterval(() => {
354
- remaining--;
355
- update();
356
- if (remaining === 30) this.showWarning('30 seconds remaining!');
357
- if (remaining <= 0) { clearInterval(this.questionTimer); this.showWarning('Time up! Moving to next...'); setTimeout(() => { if (onTimeout) onTimeout(); }, 1500); }
358
- }, 1000);
359
- }
360
- stopQuestionTimer() { if (this.questionTimer) clearInterval(this.questionTimer); }
361
- getQuestionDuration() {
362
- if (this.currentQuestionStartTime) { const d = Math.floor((Date.now() - this.currentQuestionStartTime) / 1000); return `${Math.floor(d/60).toString().padStart(2,'0')}:${(d%60).toString().padStart(2,'0')}`; }
363
- return '00:00';
364
- }
365
- showSecurityNotice() {
366
- const notice = document.createElement('div');
367
- notice.className = 'security-notice';
368
- notice.innerHTML = `<div class="security-notice-content"><div class="security-icon"><i class="fas fa-shield-alt"></i></div><h3>Interview Security Active</h3><ul class="security-rules"><li><i class="fas fa-check"></i> Fullscreen mode enabled</li><li><i class="fas fa-check"></i> Tab switching limited (2 warnings)</li><li><i class="fas fa-check"></i> Copy/paste disabled</li><li><i class="fas fa-check"></i> 3 minutes per question</li></ul><button class="btn-primary" onclick="this.parentElement.parentElement.remove()"><span>I Understand</span></button></div>`;
369
- document.body.appendChild(notice);
370
- }
371
- showWarning(msg) {
372
- const w = document.createElement('div');
373
- w.className = 'security-warning';
374
- w.innerHTML = `<i class="fas fa-exclamation-triangle"></i><span>${msg}</span>`;
375
- document.body.appendChild(w);
376
- setTimeout(() => w.classList.add('show'), 100);
377
- setTimeout(() => { w.classList.remove('show'); setTimeout(() => w.remove(), 300); }, 3000);
378
- }
379
- showCriticalWarning(msg) {
380
- const overlay = document.createElement('div');
381
- overlay.className = 'critical-warning-overlay';
382
- overlay.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-exclamation-triangle"></i></div><h3>Security Warning</h3><p>${msg.replace(/\n/g, '<br>')}</p><button class="btn-primary" onclick="this.parentElement.parentElement.remove()"><span>I Understand</span></button></div>`;
383
- document.body.appendChild(overlay);
384
- setTimeout(() => overlay.classList.add('show'), 100);
385
- }
386
- logSecurityEvent(type, data) { fetch(getApiUrl('log_security'), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-User-Session-Id': getSessionId() }, body: JSON.stringify({ event_type: type, data: data }) }).catch(() => {}); }
387
- }
388
- window.examSecurity = new ExamSecurity();
389
- </script>
390
-
391
- <!-- Proctoring System -->
392
- <script>
393
- class ProctoringSystem {
394
- constructor() {
395
- this.isActive = false; this.stream = null; this.videoElement = null;
396
- this.lookAwayCount = 0; this.maxLookAwayWarnings = 2; this.faceDetectionModel = null;
397
- this.faceDetectionInterval = null; this.noFaceDetectedTime = 0; this.multipleFacesCount = 0;
398
- }
399
- async startProctoring() {
400
- try {
401
- console.log('Loading face detection...');
402
- this.faceDetectionModel = await blazeface.load();
403
- console.log('Face detection loaded');
404
- this.stream = await navigator.mediaDevices.getUserMedia({ video: { width: { ideal: 640 }, height: { ideal: 480 }, facingMode: 'user' }, audio: { echoCancellation: false, noiseSuppression: false } });
405
- this.createVideoElement();
406
- this.videoElement.srcObject = this.stream;
407
- await this.videoElement.play();
408
- this.isActive = true;
409
- this.startFaceDetection();
410
- return true;
411
- } catch (error) {
412
- console.error('Proctoring error:', error);
413
- this.showProctoringError(error);
414
- return false;
415
- }
416
- }
417
- createVideoElement() {
418
- const container = document.createElement('div');
419
- container.id = 'proctoringVideo';
420
- container.style.cssText = 'position:fixed;bottom:20px;right:20px;width:200px;height:150px;border:2px solid var(--color-primary);border-radius:var(--radius-lg);overflow:hidden;z-index:9998;box-shadow:0 4px 12px rgba(0,0,0,0.3);';
421
- this.videoElement = document.createElement('video');
422
- this.videoElement.style.cssText = 'width:100%;height:100%;object-fit:cover;transform:scaleX(-1);';
423
- this.videoElement.autoplay = true; this.videoElement.muted = true;
424
- const label = document.createElement('div');
425
- label.style.cssText = 'position:absolute;top:5px;left:5px;background:rgba(0,0,0,0.7);color:white;padding:4px 8px;border-radius:4px;font-size:12px;display:flex;align-items:center;gap:5px;';
426
- label.innerHTML = '<i class="fas fa-video" style="color:var(--color-success);"></i><span>Proctoring Active</span>';
427
- container.appendChild(this.videoElement);
428
- container.appendChild(label);
429
- document.body.appendChild(container);
430
- }
431
- startFaceDetection() {
432
- this.faceDetectionInterval = setInterval(() => { if (this.isActive) this.detectFace(); }, 1000);
433
- }
434
- async detectFace() {
435
- try {
436
- if (!this.faceDetectionModel || !this.videoElement) return;
437
- const predictions = await this.faceDetectionModel.estimateFaces(this.videoElement, false);
438
- const count = predictions.length;
439
- this.updateFaceIndicator(count);
440
- if (count === 0) { this.noFaceDetectedTime += 1000; if (this.noFaceDetectedTime >= 3000) { this.handleNoFace(); this.noFaceDetectedTime = 0; } }
441
- else if (count === 1) { this.noFaceDetectedTime = 0; }
442
- else if (count > 1) { this.handleMultipleFaces(count); }
443
- } catch (e) { console.error('Face detection error:', e); }
444
- }
445
- handleNoFace() {
446
- this.lookAwayCount++;
447
- if (this.lookAwayCount >= this.maxLookAwayWarnings) { this.terminateExam('no_face'); }
448
- else { this.showWarning(`NO FACE DETECTED!\n\nYou have ${this.maxLookAwayWarnings - this.lookAwayCount} warning(s) remaining.`); }
449
- }
450
- handleMultipleFaces(count) {
451
- this.multipleFacesCount++;
452
- if (this.multipleFacesCount >= 2) { this.terminateExam('multiple_faces'); }
453
- else { this.showWarning(`MULTIPLE PEOPLE DETECTED!\n\n${count} faces in frame. Only candidate should be visible.`); }
454
- }
455
- terminateExam(reason) {
456
- this.isActive = false;
457
- const overlay = document.createElement('div');
458
- overlay.className = 'critical-warning-overlay show';
459
- overlay.style.zIndex = '99999';
460
- overlay.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-ban"></i></div><h3>Interview Terminated</h3><p>${reason === 'multiple_faces' ? 'Multiple people detected.' : 'Face not detected for too long.'}</p><button class="btn-primary" onclick="location.reload()"><span>Go Home</span></button></div>`;
461
- document.body.appendChild(overlay);
462
- this.stopProctoring();
463
- }
464
- updateFaceIndicator(count) {
465
- const container = document.getElementById('proctoringVideo');
466
- if (!container) return;
467
- let indicator = document.getElementById('faceCountIndicator');
468
- if (!indicator) {
469
- indicator = document.createElement('div');
470
- indicator.id = 'faceCountIndicator';
471
- indicator.style.cssText = 'position:absolute;top:5px;right:5px;background:rgba(0,0,0,0.7);color:white;padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold;';
472
- container.appendChild(indicator);
473
- }
474
- if (count === 0) { indicator.style.background = 'rgba(239,68,68,0.9)'; indicator.innerHTML = '<i class="fas fa-user-slash"></i> No Face'; }
475
- else if (count === 1) { indicator.style.background = 'rgba(16,185,129,0.9)'; indicator.innerHTML = '<i class="fas fa-user-check"></i> 1 Face'; }
476
- else { indicator.style.background = 'rgba(239,68,68,0.9)'; indicator.innerHTML = `<i class="fas fa-users"></i> ${count} Faces`; }
477
- }
478
- stopProctoring() {
479
- this.isActive = false;
480
- if (this.faceDetectionInterval) clearInterval(this.faceDetectionInterval);
481
- if (this.stream) this.stream.getTracks().forEach(t => t.stop());
482
- const container = document.getElementById('proctoringVideo');
483
- if (container) container.remove();
484
- }
485
- showWarning(msg) {
486
- const w = document.createElement('div');
487
- w.className = 'critical-warning-overlay show';
488
- w.style.zIndex = '99999';
489
- w.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-exclamation-triangle"></i></div><h3>Proctoring Warning</h3><p>${msg.replace(/\n/g, '<br>')}</p><button class="btn-primary" onclick="this.parentElement.parentElement.remove()"><span>I Understand</span></button></div>`;
490
- document.body.appendChild(w);
491
- }
492
- showProctoringError(error) {
493
- const msg = error.name === 'NotAllowedError' ? 'Camera access required. Please allow and refresh.' : 'Failed to access camera. Check device settings.';
494
- const overlay = document.createElement('div');
495
- overlay.className = 'critical-warning-overlay show';
496
- overlay.style.zIndex = '99999';
497
- overlay.innerHTML = `<div class="critical-warning-box"><div class="warning-icon"><i class="fas fa-video-slash"></i></div><h3>Camera Access Required</h3><p>${msg}</p><div style="margin-top:2rem;display:flex;gap:1rem;justify-content:center;"><button class="btn-secondary" onclick="location.reload()"><span>Cancel</span></button><button class="btn-primary" onclick="location.reload()"><span>Retry</span></button></div></div>`;
498
- document.body.appendChild(overlay);
499
- }
500
- }
501
- window.proctoringSystem = new ProctoringSystem();
502
- </script>
503
-
504
- <!-- Main Application -->
505
- <script>
506
- function scrollToUpload() { document.getElementById('uploadSection')?.scrollIntoView({ behavior: 'smooth', block: 'center' }); }
507
- function showLoading(msg = 'Processing...') { const o = document.getElementById('loadingOverlay'); if (o) { o.querySelector('.loading-text').textContent = msg; o.classList.remove('hidden'); } }
508
- function hideLoading() { document.getElementById('loadingOverlay')?.classList.add('hidden'); }
509
- function showAlert(msg, type = 'info') {
510
- const a = document.createElement('div');
511
- a.className = `alert-toast alert-${type}`;
512
- a.innerHTML = `<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i><span>${msg}</span>`;
513
- document.body.appendChild(a);
514
- setTimeout(() => a.classList.add('show'), 100);
515
- setTimeout(() => { a.classList.remove('show'); setTimeout(() => a.remove(), 300); }, 3000);
516
- }
517
-
518
- document.addEventListener('DOMContentLoaded', () => {
519
- const uploadArea = document.getElementById('uploadArea');
520
- const resumeInput = document.getElementById('resumeInput');
521
- if (uploadArea && resumeInput) {
522
- uploadArea.addEventListener('click', () => resumeInput.click());
523
- uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.style.borderColor = 'var(--color-primary)'; });
524
- uploadArea.addEventListener('dragleave', () => { uploadArea.style.borderColor = ''; });
525
- uploadArea.addEventListener('drop', (e) => { e.preventDefault(); uploadArea.style.borderColor = ''; if (e.dataTransfer.files.length > 0) handleFileUpload(e.dataTransfer.files[0]); });
526
- resumeInput.addEventListener('change', (e) => { if (e.target.files.length > 0) handleFileUpload(e.target.files[0]); });
527
- }
528
- });
529
-
530
- async function handleFileUpload(file) {
531
- showLoading('Analyzing resume with AI...');
532
- const formData = new FormData();
533
- formData.append('resume', file);
534
- try {
535
- const response = await fetch(getApiUrl('upload_resume'), { method: 'POST', body: formData, headers: { 'X-User-Session-Id': getSessionId() } });
536
- if (!response.ok) throw new Error('Upload failed');
537
- const result = await response.json();
538
- hideLoading();
539
- if (result.candidate_profile) {
540
- sessionStorage.setItem('candidateProfile', JSON.stringify(result.candidate_profile));
541
- showAlert('Resume analyzed successfully!', 'success');
542
- setTimeout(() => showInterviewSetup(result.candidate_profile), 1000);
543
- } else { showAlert('Failed to analyze resume.', 'error'); }
544
- } catch (error) { hideLoading(); showAlert('Failed to upload: ' + error.message, 'error'); }
545
- }
546
-
547
- function showInterviewSetup(profile) {
548
- document.getElementById('uploadSection')?.classList.add('hidden');
549
- const setupSection = document.getElementById('setupSection');
550
- if (setupSection) {
551
- setupSection.classList.remove('hidden');
552
- const profileDisplay = document.getElementById('profileDisplay');
553
- if (profileDisplay) {
554
- profileDisplay.innerHTML = `
555
- <div class="profile-item"><span class="profile-label">Name:</span><span class="profile-value">${profile.name || 'N/A'}</span></div>
556
- <div class="profile-item"><span class="profile-label">Email:</span><span class="profile-value">${profile.email || 'N/A'}</span></div>
557
- <div class="profile-item"><span class="profile-label">Experience:</span><span class="profile-value">${profile.experience || 'N/A'}</span></div>
558
- <div class="profile-item"><span class="profile-label">Skills:</span><div class="skills-tags">${(profile.key_skills || []).map(s => `<span class="skill-tag">${s}</span>`).join('')}</div></div>
559
- `;
560
- }
561
- const posInput = document.getElementById('positionInput');
562
- if (posInput && profile.inferred_position) posInput.value = profile.inferred_position;
563
- }
564
- }
565
-
566
- async function startInterview() {
567
- const position = document.getElementById('positionInput')?.value;
568
- if (!position) { showAlert('Please enter a position/role', 'error'); return; }
569
- showPreExamChecklist(position);
570
- }
571
-
572
- async function showPreExamChecklist(position) {
573
- const overlay = document.createElement('div');
574
- overlay.className = 'critical-warning-overlay show';
575
- overlay.id = 'preExamChecklist';
576
- overlay.style.zIndex = '99999';
577
- overlay.innerHTML = `
578
- <div class="critical-warning-box" style="max-width:600px;">
579
- <div class="warning-icon" style="background:rgba(99,102,241,0.2);"><i class="fas fa-clipboard-check" style="color:var(--color-primary);"></i></div>
580
- <h3>Pre-Interview Checklist</h3>
581
- <p style="margin-bottom:1.5rem;">Please ensure all requirements are met:</p>
582
- <div class="checklist-items">
583
- <div class="checklist-item" id="check-browser"><i class="fas fa-spinner fa-spin"></i><span>Checking browser...</span></div>
584
- <div class="checklist-item" id="check-camera"><i class="fas fa-spinner fa-spin"></i><span>Requesting camera...</span></div>
585
- <div class="checklist-item" id="check-microphone"><i class="fas fa-spinner fa-spin"></i><span>Requesting microphone...</span></div>
586
- <div class="checklist-item" id="check-questions"><i class="fas fa-spinner fa-spin"></i><span>Generating questions...</span></div>
587
- </div>
588
- <div id="checklistActions" style="margin-top:2rem;display:none;"><button class="btn-primary" onclick="beginInterview()" style="width:100%;"><i class="fas fa-play"></i><span>Start Interview</span><div class="btn-glow"></div></button></div>
589
- <div id="checklistError" style="margin-top:2rem;display:none;"><button class="btn-secondary" onclick="document.getElementById('preExamChecklist').remove()" style="width:100%;"><i class="fas fa-times"></i><span>Cancel</span></button></div>
590
- </div>
591
- `;
592
- document.body.appendChild(overlay);
593
- await runPreExamChecks(position);
594
- }
595
-
596
- async function runPreExamChecks(position) {
597
- let allPassed = true;
598
- // Browser check
599
- await checkItem('check-browser', async () => { if (!window.examSecurity.checkBrowser()) throw new Error('Unsupported browser'); return 'Browser compatible'; });
600
- // Camera check
601
- const cameraOk = await checkItem('check-camera', async () => {
602
- const stream = await navigator.mediaDevices.getUserMedia({ video: true });
603
- stream.getTracks().forEach(t => t.stop());
604
- return 'Camera access granted';
605
- });
606
- if (!cameraOk) allPassed = false;
607
- // Microphone check
608
- const micOk = await checkItem('check-microphone', async () => {
609
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
610
- stream.getTracks().forEach(t => t.stop());
611
- return 'Microphone access granted';
612
- });
613
- if (!micOk) allPassed = false;
614
- // Questions check
615
- const questionsOk = await checkItem('check-questions', async () => {
616
- const response = await fetch(getApiUrl('setup_interview'), { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-User-Session-Id': getSessionId() }, body: JSON.stringify({ position_role: position }) });
617
- const result = await response.json();
618
- if (!result.questions) throw new Error('Failed to generate questions');
619
- sessionStorage.setItem('questions', JSON.stringify(result.questions));
620
- sessionStorage.setItem('currentQuestion', '0');
621
- sessionStorage.setItem('responses', JSON.stringify([]));
622
- sessionStorage.setItem('isCodingRole', result.is_coding_role || false);
623
- return `${result.questions.length} questions generated`;
624
- });
625
- if (!questionsOk) allPassed = false;
626
- if (allPassed) document.getElementById('checklistActions').style.display = 'block';
627
- else document.getElementById('checklistError').style.display = 'block';
628
- }
629
-
630
- async function checkItem(itemId, checkFn) {
631
- const item = document.getElementById(itemId);
632
- if (!item) return false;
633
- try {
634
- const msg = await checkFn();
635
- item.innerHTML = `<i class="fas fa-check-circle"></i><span>${msg}</span>`;
636
- item.classList.add('success');
637
- return true;
638
- } catch (error) {
639
- item.innerHTML = `<i class="fas fa-times-circle"></i><span>${error.message}</span>`;
640
- item.classList.add('error');
641
- return false;
642
- }
643
- }
644
-
645
- async function beginInterview() {
646
- document.getElementById('preExamChecklist')?.remove();
647
- showLoading('Initializing secure exam environment...');
648
- try {
649
- const securityStarted = await window.examSecurity.startExamMode();
650
- if (!securityStarted) { hideLoading(); return; }
651
- const proctoringStarted = await window.proctoringSystem.startProctoring();
652
- if (!proctoringStarted) { hideLoading(); return; }
653
- window.examSecurity.activateExamMode();
654
- hideLoading();
655
- hideAllUIForExam();
656
- document.getElementById('setupSection')?.classList.add('hidden');
657
- document.getElementById('interviewSection')?.classList.remove('hidden');
658
- loadQuestion(0);
659
- } catch (error) { hideLoading(); showAlert('Error starting interview: ' + error.message, 'error'); }
660
- }
661
-
662
- function hideAllUIForExam() {
663
- const nav = document.querySelector('.premium-nav'); if (nav) nav.style.display = 'none';
664
- const hero = document.querySelector('.hero-section'); if (hero) hero.style.display = 'none';
665
- const bg = document.querySelector('.premium-bg'); if (bg) bg.style.opacity = '0.3';
666
- document.body.classList.add('exam-mode');
667
- }
668
-
669
- function showAllUIAfterExam() {
670
- const nav = document.querySelector('.premium-nav'); if (nav) nav.style.display = 'block';
671
- const hero = document.querySelector('.hero-section'); if (hero) hero.style.display = 'flex';
672
- const bg = document.querySelector('.premium-bg'); if (bg) bg.style.opacity = '1';
673
- document.body.classList.remove('exam-mode');
674
- }
675
-
676
- function loadQuestion(index) {
677
- const questions = JSON.parse(sessionStorage.getItem('questions') || '[]');
678
- const question = questions[index];
679
- if (!question) return;
680
- const answerTextarea = document.getElementById('answerText');
681
- if (answerTextarea) answerTextarea.value = '';
682
- document.getElementById('questionText').textContent = question.question;
683
- const progressNumber = document.querySelector('.progress-number');
684
- if (progressNumber) progressNumber.textContent = `${index + 1}/${questions.length}`;
685
- const progress = ((index + 1) / questions.length) * 100;
686
- const progressRing = document.querySelector('.progress-ring');
687
- if (progressRing) {
688
- const circumference = 2 * Math.PI * 45;
689
- progressRing.style.strokeDasharray = `${circumference} ${circumference}`;
690
- progressRing.style.strokeDashoffset = circumference - (progress / 100) * circumference;
691
- }
692
- window.examSecurity.startQuestionTimer(() => autoSubmitAndNext(), 180);
693
- }
694
-
695
- async function submitAnswer() {
696
- const currentIndex = parseInt(sessionStorage.getItem('currentQuestion') || '0');
697
- const questions = JSON.parse(sessionStorage.getItem('questions') || '[]');
698
- const question = questions[currentIndex];
699
- const answerTextarea = document.getElementById('answerText');
700
- const answer = answerTextarea?.value.trim();
701
- if (!answer) { showAlert('Please provide an answer', 'error'); return; }
702
- showLoading('Evaluating your answer...');
703
- try {
704
- const response = await fetch(getApiUrl('submit_answer'), {
705
- method: 'POST',
706
- headers: { 'Content-Type': 'application/json', 'X-User-Session-Id': getSessionId() },
707
- body: JSON.stringify({ question_id: question.id || `q${currentIndex + 1}`, response_text: answer, duration: window.examSecurity.getQuestionDuration() })
708
- });
709
- if (!response.ok) { const err = await response.json(); throw new Error(err.error || 'Failed to submit'); }
710
- const result = await response.json();
711
- hideLoading();
712
- const responses = JSON.parse(sessionStorage.getItem('responses') || '[]');
713
- responses.push({ question: question.question, answer: answer, evaluation: result.evaluation, time_taken: window.examSecurity.getQuestionDuration() });
714
- sessionStorage.setItem('responses', JSON.stringify(responses));
715
- window.examSecurity.stopQuestionTimer();
716
- if (currentIndex < questions.length - 1) { sessionStorage.setItem('currentQuestion', (currentIndex + 1).toString()); loadQuestion(currentIndex + 1); }
717
- else { completeInterview(); }
718
- } catch (error) {
719
- hideLoading();
720
- showAlert('Error: ' + error.message, 'error');
721
- const currentIndex = parseInt(sessionStorage.getItem('currentQuestion') || '0');
722
- const questions = JSON.parse(sessionStorage.getItem('questions') || '[]');
723
- if (currentIndex < questions.length - 1) { setTimeout(() => { sessionStorage.setItem('currentQuestion', (currentIndex + 1).toString()); loadQuestion(currentIndex + 1); }, 2000); }
724
- }
725
- }
726
-
727
- async function autoSubmitAndNext() {
728
- const answerTextarea = document.getElementById('answerText');
729
- const answer = answerTextarea?.value.trim() || '[No answer - Time expired]';
730
- const currentIndex = parseInt(sessionStorage.getItem('currentQuestion') || '0');
731
- const questions = JSON.parse(sessionStorage.getItem('questions') || '[]');
732
- const question = questions[currentIndex];
733
- try {
734
- const response = await fetch(getApiUrl('submit_answer'), {
735
- method: 'POST',
736
- headers: { 'Content-Type': 'application/json', 'X-User-Session-Id': getSessionId() },
737
- body: JSON.stringify({ question_id: question.id || `q${currentIndex + 1}`, response_text: answer, duration: '03:00', auto_submitted: true })
738
- });
739
- const result = await response.json();
740
- const responses = JSON.parse(sessionStorage.getItem('responses') || '[]');
741
- responses.push({ question: question.question, answer: answer, evaluation: result.evaluation, time_taken: '03:00', auto_submitted: true });
742
- sessionStorage.setItem('responses', JSON.stringify(responses));
743
- if (currentIndex < questions.length - 1) { sessionStorage.setItem('currentQuestion', (currentIndex + 1).toString()); loadQuestion(currentIndex + 1); }
744
- else { completeInterview(); }
745
- } catch (error) {
746
- if (currentIndex < questions.length - 1) { sessionStorage.setItem('currentQuestion', (currentIndex + 1).toString()); loadQuestion(currentIndex + 1); }
747
- else { completeInterview(); }
748
- }
749
- }
750
-
751
- async function completeInterview() {
752
- showLoading('Generating your assessment report...');
753
- window.examSecurity.endExamMode();
754
- window.proctoringSystem.stopProctoring();
755
- showAllUIAfterExam();
756
- try {
757
- const response = await fetch(getApiUrl('get_assessment'), { method: 'GET', headers: { 'X-User-Session-Id': getSessionId() } });
758
- if (!response.ok) { const err = await response.json(); throw new Error(err.error || 'Failed to generate assessment'); }
759
- const result = await response.json();
760
- hideLoading();
761
- if (result.assessment) { sessionStorage.setItem('assessment', JSON.stringify(result.assessment)); showResults(); }
762
- else { showAlert('Failed to generate assessment', 'error'); }
763
- } catch (error) {
764
- hideLoading();
765
- showAlert('Error: ' + error.message, 'error');
766
- setTimeout(() => showResults(), 2000);
767
- }
768
- }
769
-
770
- function showResults() {
771
- document.getElementById('interviewSection')?.classList.add('hidden');
772
- document.getElementById('resultsSection')?.classList.remove('hidden');
773
- const assessment = JSON.parse(sessionStorage.getItem('assessment') || '{}');
774
- if (!assessment || typeof assessment.overallScore === 'undefined') { showAlert('No assessment data', 'error'); return; }
775
- const overallScoreEl = document.getElementById('overallScore');
776
- if (overallScoreEl) overallScoreEl.textContent = assessment.overallScore;
777
- const recommendationEl = document.getElementById('recommendation');
778
- if (recommendationEl && assessment.recommendation) {
779
- const rec = assessment.recommendation;
780
- let icon = 'fa-check-circle', color = 'var(--color-success)';
781
- if (rec.toLowerCase().includes('not recommended')) { icon = 'fa-times-circle'; color = 'var(--color-danger)'; }
782
- else if (rec.toLowerCase().includes('consider')) { icon = 'fa-exclamation-circle'; color = 'var(--color-warning)'; }
783
- recommendationEl.innerHTML = `<i class="fas ${icon}"></i><span>${rec}</span>`;
784
- recommendationEl.style.borderColor = color; recommendationEl.style.color = color; recommendationEl.style.background = color + '20';
785
- }
786
- if (assessment.detailedScores) {
787
- const scores = assessment.detailedScores;
788
- const scoreItems = document.querySelectorAll('.score-item');
789
- if (scoreItems.length >= 3) {
790
- const techScore = scores.technicalSkills ?? 0;
791
- scoreItems[0].querySelector('.score-percent').textContent = techScore + '%';
792
- scoreItems[0].querySelector('.score-bar-fill').style.width = techScore + '%';
793
- const commScore = scores.communication ?? 0;
794
- scoreItems[1].querySelector('.score-percent').textContent = commScore + '%';
795
- scoreItems[1].querySelector('.score-bar-fill').style.width = commScore + '%';
796
- const softScore = scores.softSkills ?? 0;
797
- scoreItems[2].querySelector('.score-percent').textContent = softScore + '%';
798
- scoreItems[2].querySelector('.score-bar-fill').style.width = softScore + '%';
799
- }
800
- }
801
- const scoreRing = document.querySelector('.results-section .score-ring');
802
- if (scoreRing) {
803
- const score = assessment.overallScore ?? 0;
804
- const circumference = 2 * Math.PI * 90;
805
- scoreRing.style.strokeDasharray = `${circumference} ${circumference}`;
806
- scoreRing.style.strokeDashoffset = circumference - (score / 100) * circumference;
807
- }
808
- const strengthsList = document.getElementById('strengthsList');
809
- if (strengthsList && assessment.keyStrengths && assessment.keyStrengths.length > 0) {
810
- strengthsList.innerHTML = assessment.keyStrengths.map(s => `<li style="padding:0.75rem;background:rgba(16,185,129,0.1);border-left:3px solid var(--color-success);margin:0.5rem 0;border-radius:4px;"><i class="fas fa-check-circle" style="color:var(--color-success);margin-right:0.5rem;"></i>${s}</li>`).join('');
811
- }
812
- const improvementsList = document.getElementById('improvementsList');
813
- if (improvementsList && assessment.areasForImprovement && assessment.areasForImprovement.length > 0) {
814
- improvementsList.innerHTML = assessment.areasForImprovement.map(a => `<li style="padding:0.75rem;background:rgba(239,68,68,0.1);border-left:3px solid var(--color-danger);margin:0.5rem 0;border-radius:4px;"><i class="fas fa-exclamation-circle" style="color:var(--color-danger);margin-right:0.5rem;"></i>${a}</li>`).join('');
815
- }
816
- }
817
- </script>
818
-
819
- <!-- Additional Styles -->
820
- <style>
821
- .alert-toast { position: fixed; top: 100px; right: 20px; padding: 16px 24px; background: var(--glass-bg); backdrop-filter: blur(20px); border: 1px solid var(--glass-border); border-radius: var(--radius-lg); display: flex; align-items: center; gap: 12px; z-index: 10000; transform: translateX(400px); transition: transform 0.3s ease; box-shadow: var(--glass-shadow); }
822
- .alert-toast.show { transform: translateX(0); }
823
- .alert-toast.alert-success { border-color: var(--color-success); color: var(--color-success); }
824
- .alert-toast.alert-error { border-color: var(--color-danger); color: var(--color-danger); }
825
- .alert-toast.alert-info { border-color: var(--color-primary); color: var(--color-primary); }
826
- .checklist-items { display: flex; flex-direction: column; gap: 1rem; margin: 1rem 0; }
827
- .checklist-item { display: flex; align-items: center; gap: 1rem; padding: 1rem; background: var(--color-surface); border-radius: var(--radius-md); border-left: 3px solid var(--color-text-tertiary); }
828
- .checklist-item.success { border-left-color: var(--color-success); }
829
- .checklist-item.error { border-left-color: var(--color-danger); }
830
- .checklist-item i { font-size: 20px; min-width: 20px; }
831
- .checklist-item.success i { color: var(--color-success); }
832
- .checklist-item.error i { color: var(--color-danger); }
833
- .security-notice { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.95); backdrop-filter: blur(10px); display: flex; align-items: center; justify-content: center; z-index: 99999; }
834
- .security-notice-content { background: var(--glass-bg); border: 1px solid var(--glass-border); border-radius: var(--radius-xl); padding: var(--spacing-xl); max-width: 500px; text-align: center; }
835
- .security-icon { width: 80px; height: 80px; background: linear-gradient(135deg, var(--color-primary), var(--color-secondary)); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 36px; margin: 0 auto var(--spacing-lg); }
836
- .security-rules { list-style: none; text-align: left; margin: var(--spacing-lg) 0; padding: 0; }
837
- .security-rules li { display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-sm) 0; color: var(--color-text-secondary); }
838
- .security-rules i { color: var(--color-success); }
839
- .security-warning { position: fixed; top: 100px; right: -400px; padding: var(--spacing-md) var(--spacing-lg); background: rgba(239, 68, 68, 0.95); border: 2px solid var(--color-danger); border-radius: var(--radius-lg); color: white; font-weight: 600; display: flex; align-items: center; gap: var(--spacing-sm); z-index: 99999; transition: right 0.3s ease; box-shadow: 0 8px 32px rgba(239, 68, 68, 0.5); }
840
- .security-warning.show { right: 20px; }
841
- .critical-warning-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.95); backdrop-filter: blur(10px); display: flex; align-items: center; justify-content: center; z-index: 99999; opacity: 0; transition: opacity 0.3s ease; }
842
- .critical-warning-overlay.show { opacity: 1; }
843
- .critical-warning-box { background: var(--glass-bg); border: 2px solid var(--color-danger); border-radius: var(--radius-xl); padding: var(--spacing-xl); max-width: 500px; text-align: center; animation: shake 0.5s; }
844
- @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-10px); } 75% { transform: translateX(10px); } }
845
- .warning-icon { width: 100px; height: 100px; background: rgba(239, 68, 68, 0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 48px; color: var(--color-danger); margin: 0 auto var(--spacing-lg); animation: pulse 1s infinite; }
846
- @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
847
- .critical-warning-box h3 { font-size: 28px; color: var(--color-danger); margin-bottom: var(--spacing-md); }
848
- .critical-warning-box p { font-size: 16px; color: var(--color-text-secondary); line-height: 1.8; margin-bottom: var(--spacing-lg); }
849
- </style>
850
  </body>
851
  </html>
 
9
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
10
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
  <link rel="stylesheet" href="assets/css/premium.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  </head>
13
  <body>
14
  <!-- Animated Background -->
15
  <div class="premium-bg">
16
  <div class="gradient-mesh"></div>
17
+ <div class="neural-network">
18
+ <canvas id="neuralCanvas"></canvas>
19
+ </div>
20
  </div>
21
 
22
  <!-- Navigation -->
23
  <nav class="premium-nav">
24
  <div class="nav-container">
25
  <div class="nav-brand">
26
+ <div class="brand-icon">
27
+ <i class="fas fa-brain"></i>
28
+ </div>
29
  <span class="brand-text">IntervuAI<span class="brand-pro">Pro</span></span>
30
  </div>
31
  <div class="nav-menu">
32
+ <a href="#" class="nav-link active">
33
+ <i class="fas fa-home"></i>
34
+ <span>Dashboard</span>
35
+ </a>
36
+ <a href="#" class="nav-link">
37
+ <i class="fas fa-comment-dots"></i>
38
+ <span>Feedback</span>
39
+ </a>
40
  <div class="nav-user">
41
+ <div class="user-avatar">
42
+ <i class="fas fa-user"></i>
43
+ </div>
44
  <div class="user-info">
45
+ <span class="user-name" id="userName">Guest</span>
46
+ <span class="user-role">Premium User</span>
47
  </div>
48
+ <a href="login.html" class="logout-btn" title="Logout">
49
+ <i class="fas fa-sign-out-alt"></i>
50
+ </a>
51
  </div>
52
  </div>
53
  </div>
 
56
  <!-- Main Content -->
57
  <main class="premium-main">
58
  <!-- Hero Section -->
59
+ <section class="hero-section">
60
  <div class="hero-content">
61
+ <div class="hero-badge">
62
+ <i class="fas fa-sparkles"></i>
63
+ <span>Powered by Advanced AI</span>
64
+ </div>
65
+ <h1 class="hero-title">
66
+ Next-Generation
67
+ <span class="gradient-text">AI Interview</span>
68
+ Platform
69
+ </h1>
70
+ <p class="hero-subtitle">
71
+ Experience intelligent candidate evaluation with real-time AI analysis,
72
+ automated scoring, and comprehensive insights.
73
+ </p>
74
  <div class="hero-actions">
75
+ <button class="btn-primary" onclick="scrollToUpload()">
76
+ <i class="fas fa-rocket"></i>
77
+ <span>Start Interview</span>
78
+ <div class="btn-glow"></div>
79
+ </button>
80
+ <button class="btn-secondary">
81
+ <i class="fas fa-play-circle"></i>
82
+ <span>Watch Demo</span>
83
+ </button>
84
  </div>
85
  <div class="hero-stats">
86
+ <div class="stat-item">
87
+ <div class="stat-value">10K+</div>
88
+ <div class="stat-label">Interviews</div>
89
+ </div>
90
  <div class="stat-divider"></div>
91
+ <div class="stat-item">
92
+ <div class="stat-value">98%</div>
93
+ <div class="stat-label">Accuracy</div>
94
+ </div>
95
  <div class="stat-divider"></div>
96
+ <div class="stat-item">
97
+ <div class="stat-value">AI</div>
98
+ <div class="stat-label">Detection</div>
99
+ </div>
100
  </div>
101
  </div>
102
  </section>
 
105
  <section class="upload-section" id="uploadSection">
106
  <div class="glass-card large">
107
  <div class="card-header">
108
+ <div class="header-icon">
109
+ <i class="fas fa-cloud-upload-alt"></i>
110
+ </div>
111
  <div class="header-content">
112
  <h2 class="card-title">Upload Resume</h2>
113
  <p class="card-subtitle">AI-powered resume analysis in seconds</p>
114
  </div>
115
  </div>
116
+
117
  <div class="upload-area" id="uploadArea">
118
+ <input type="file" id="resumeInput" accept=".pdf,.doc,.docx" hidden>
119
+ <div class="upload-icon">
120
+ <i class="fas fa-file-upload"></i>
121
+ </div>
122
  <h3 class="upload-title">Drop your resume here</h3>
123
  <p class="upload-text">or click to browse</p>
124
+ <div class="upload-formats">
125
+ <span class="format-badge">PDF</span>
126
+ <span class="format-badge">DOC</span>
127
+ <span class="format-badge">DOCX</span>
128
+ </div>
129
+ <div class="upload-progress hidden" id="uploadProgress">
130
+ <div class="progress-bar">
131
+ <div class="progress-fill"></div>
132
+ </div>
133
+ <span class="progress-text">Analyzing...</span>
 
 
 
134
  </div>
 
 
 
 
 
 
 
 
 
135
  </div>
136
  </div>
137
  </section>
138
 
139
+ <!-- Interview Section (Hidden initially) -->
140
  <section class="interview-section hidden" id="interviewSection">
141
  <div class="glass-card">
142
  <div class="interview-header">
 
144
  <div class="progress-circle">
145
  <svg viewBox="0 0 100 100">
146
  <circle cx="50" cy="50" r="45"></circle>
147
+ <circle cx="50" cy="50" r="45" class="progress-ring"></circle>
148
  </svg>
149
+ <span class="progress-number">1/15</span>
150
  </div>
151
  </div>
152
+ <div class="timer">
153
+ <i class="fas fa-clock"></i>
154
+ <span id="timer">00:00</span>
155
+ </div>
156
  </div>
157
+
158
  <div class="question-container">
159
+ <div class="question-tags">
160
+ <span class="tag">Technical</span>
161
+ <span class="tag">Problem Solving</span>
162
+ </div>
163
+ <h3 class="question-text" id="questionText">
164
+ Loading question...
165
+ </h3>
166
  </div>
167
+
168
  <div class="answer-container">
169
+ <textarea
170
+ id="answerText"
171
+ class="answer-input"
172
+ placeholder="Type your response here..."
173
+ rows="8"
174
+ ></textarea>
175
+ <div class="ai-warning hidden" id="aiWarning">
176
+ <i class="fas fa-exclamation-triangle"></i>
177
+ <span>AI-generated content detected</span>
178
+ </div>
179
  </div>
180
+
181
  <div class="interview-actions">
182
+ <button class="btn-secondary" onclick="resetInterview()">
183
+ <i class="fas fa-home"></i>
184
+ <span>Home</span>
185
+ </button>
186
+ <button class="btn-primary" onclick="submitAnswer()">
187
+ <span>Submit & Next</span>
188
+ <i class="fas fa-arrow-right"></i>
189
+ <div class="btn-glow"></div>
190
+ </button>
191
  </div>
192
  </div>
193
  </section>
194
 
195
+ <!-- Results Section (Hidden initially) -->
196
  <section class="results-section hidden" id="resultsSection">
197
  <div class="results-header">
198
  <h2 class="section-title">Interview Assessment</h2>
199
+ <div style="display: flex; gap: 1rem;">
200
+ <button class="btn-secondary" onclick="downloadReportHTML()">
201
+ <i class="fas fa-file-code"></i>
202
+ <span>Download HTML</span>
203
+ </button>
204
+ <button class="btn-primary" onclick="downloadReportPDF()">
205
+ <i class="fas fa-file-pdf"></i>
206
+ <span>Download PDF</span>
207
+ <div class="btn-glow"></div>
208
+ </button>
209
+ </div>
210
  </div>
211
+
212
  <div class="results-grid">
213
  <div class="glass-card">
214
  <div class="score-display">
215
  <div class="score-circle">
216
  <svg viewBox="0 0 200 200">
217
  <circle cx="100" cy="100" r="90"></circle>
218
+ <circle cx="100" cy="100" r="90" class="score-ring"></circle>
219
  </svg>
220
  <div class="score-content">
221
  <span class="score-value" id="overallScore">--</span>
222
  <span class="score-label">Overall</span>
223
  </div>
224
  </div>
225
+ <div class="recommendation" id="recommendation">
226
+ <i class="fas fa-hourglass-half"></i>
227
+ <span>Loading...</span>
228
+ </div>
229
  </div>
230
  </div>
231
+
232
  <div class="glass-card">
233
  <h3 class="card-title">Detailed Scores</h3>
234
+ <div class="score-breakdown">
235
  <div class="score-item">
236
+ <div class="score-info">
237
+ <span class="score-name">Technical Skills</span>
238
+ <span class="score-percent">--%</span>
239
+ </div>
240
+ <div class="score-bar">
241
+ <div class="score-bar-fill" style="width: 0%"></div>
242
+ </div>
243
  </div>
244
  <div class="score-item">
245
+ <div class="score-info">
246
+ <span class="score-name">Communication</span>
247
+ <span class="score-percent">--%</span>
248
+ </div>
249
+ <div class="score-bar">
250
+ <div class="score-bar-fill" style="width: 0%"></div>
251
+ </div>
252
  </div>
253
  <div class="score-item">
254
+ <div class="score-info">
255
+ <span class="score-name">Soft Skills</span>
256
+ <span class="score-percent">--%</span>
257
+ </div>
258
+ <div class="score-bar">
259
+ <div class="score-bar-fill" style="width: 0%"></div>
260
+ </div>
261
  </div>
262
  </div>
263
  </div>
264
  </div>
265
+
266
  <div class="glass-card" style="margin-top: 2rem;">
267
  <h3 class="card-title">Key Insights</h3>
268
+ <div class="insights-grid">
269
  <div class="insights-column">
270
  <h4><i class="fas fa-check-circle" style="color: var(--color-success);"></i> Strengths</h4>
271
  <ul id="strengthsList" style="list-style: none; padding: 0;"></ul>
 
276
  </div>
277
  </div>
278
  </div>
279
+
280
  <div class="interview-actions" style="margin-top: 2rem; justify-content: center;">
281
+ <button class="btn-primary" onclick="location.reload()">
282
+ <i class="fas fa-redo"></i>
283
+ <span>Start New Interview</span>
284
+ <div class="btn-glow"></div>
285
+ </button>
286
  </div>
287
  </section>
288
  </main>
 
291
  <div class="loading-overlay hidden" id="loadingOverlay">
292
  <div class="loading-content">
293
  <div class="loading-spinner"></div>
294
+ <p class="loading-text">Processing with AI...</p>
295
  </div>
296
  </div>
297
 
298
+ <!-- Scripts -->
299
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
300
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
301
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.18.0/dist/tf.min.js"></script>
302
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface@0.0.7/dist/blazeface.min.js"></script>
303
+ <script src="assets/js/config.js"></script>
304
+ <script src="assets/js/neural-bg.js"></script>
305
+ <script src="assets/js/exam-security.js"></script>
306
+ <script src="assets/js/proctoring.js"></script>
307
+ <script src="assets/js/premium-app.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
  </body>
309
  </html>
static/login.html ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Login - IntervuAI Pro</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link rel="stylesheet" href="assets/css/premium.css">
10
+ <link rel="stylesheet" href="assets/css/auth.css">
11
+ </head>
12
+ <body>
13
+ <div class="premium-bg">
14
+ <div class="gradient-mesh"></div>
15
+ <div class="neural-network">
16
+ <canvas id="neuralCanvas"></canvas>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="auth-container">
21
+ <div class="auth-card">
22
+ <div class="auth-header">
23
+ <div class="brand-logo">
24
+ <div class="brand-icon">
25
+ <i class="fas fa-brain"></i>
26
+ </div>
27
+ <span class="brand-text">IntervuAI<span class="brand-pro">Pro</span></span>
28
+ </div>
29
+ <h1 class="auth-title">Welcome Back</h1>
30
+ <p class="auth-subtitle">Sign in to continue to your dashboard</p>
31
+ </div>
32
+
33
+ <div class="alert alert-error hidden" id="errorAlert">
34
+ <i class="fas fa-exclamation-circle"></i>
35
+ <span id="errorMessage"></span>
36
+ </div>
37
+
38
+ <form id="loginForm" class="auth-form">
39
+ <div class="form-group">
40
+ <label class="form-label">
41
+ <i class="fas fa-envelope"></i>
42
+ <span>Email Address</span>
43
+ </label>
44
+ <input
45
+ type="email"
46
+ name="email"
47
+ id="emailInput"
48
+ class="form-input"
49
+ placeholder="you@example.com"
50
+ required
51
+ >
52
+ </div>
53
+
54
+ <div class="form-group">
55
+ <label class="form-label">
56
+ <i class="fas fa-lock"></i>
57
+ <span>Password</span>
58
+ </label>
59
+ <input
60
+ type="password"
61
+ name="password"
62
+ id="passwordInput"
63
+ class="form-input"
64
+ placeholder="••••••••"
65
+ required
66
+ >
67
+ </div>
68
+
69
+ <div class="form-options">
70
+ <label class="checkbox-label">
71
+ <input type="checkbox" name="remember">
72
+ <span>Remember me</span>
73
+ </label>
74
+ <a href="#" class="link-primary">Forgot password?</a>
75
+ </div>
76
+
77
+ <button type="submit" class="btn-primary btn-block">
78
+ <span>Sign In</span>
79
+ <i class="fas fa-arrow-right"></i>
80
+ <div class="btn-glow"></div>
81
+ </button>
82
+ </form>
83
+
84
+ <div class="auth-divider">
85
+ <span>or continue with</span>
86
+ </div>
87
+
88
+ <div class="social-login">
89
+ <button class="btn-social" onclick="socialLogin('google')">
90
+ <i class="fab fa-google"></i>
91
+ </button>
92
+ <button class="btn-social" onclick="socialLogin('github')">
93
+ <i class="fab fa-github"></i>
94
+ </button>
95
+ <button class="btn-social" onclick="socialLogin('linkedin')">
96
+ <i class="fab fa-linkedin"></i>
97
+ </button>
98
+ </div>
99
+
100
+ <div class="auth-footer">
101
+ <p>Don't have an account? <a href="signup.html" class="link-primary">Sign up</a></p>
102
+ </div>
103
+ </div>
104
+
105
+ <div class="auth-features">
106
+ <div class="feature-item">
107
+ <div class="feature-icon">
108
+ <i class="fas fa-shield-alt"></i>
109
+ </div>
110
+ <h3>Enterprise Security</h3>
111
+ <p>Bank-level encryption and data protection</p>
112
+ </div>
113
+ <div class="feature-item">
114
+ <div class="feature-icon">
115
+ <i class="fas fa-bolt"></i>
116
+ </div>
117
+ <h3>Lightning Fast</h3>
118
+ <p>AI-powered analysis in seconds</p>
119
+ </div>
120
+ <div class="feature-item">
121
+ <div class="feature-icon">
122
+ <i class="fas fa-chart-line"></i>
123
+ </div>
124
+ <h3>Advanced Analytics</h3>
125
+ <p>Comprehensive insights and reports</p>
126
+ </div>
127
+ </div>
128
+ </div>
129
+
130
+ <script src="assets/js/neural-bg.js"></script>
131
+ <script>
132
+ document.getElementById('loginForm').addEventListener('submit', function(e) {
133
+ e.preventDefault();
134
+ const email = document.getElementById('emailInput').value;
135
+ const password = document.getElementById('passwordInput').value;
136
+
137
+ if (email && password) {
138
+ // Store user info in session
139
+ sessionStorage.setItem('userName', email.split('@')[0]);
140
+ sessionStorage.setItem('userEmail', email);
141
+ sessionStorage.setItem('isLoggedIn', 'true');
142
+
143
+ // Redirect to main page
144
+ window.location.href = 'index.html';
145
+ } else {
146
+ document.getElementById('errorAlert').classList.remove('hidden');
147
+ document.getElementById('errorMessage').textContent = 'Please enter both email and password';
148
+ }
149
+ });
150
+
151
+ function socialLogin(provider) {
152
+ // Mock social login
153
+ sessionStorage.setItem('userName', provider + '_user');
154
+ sessionStorage.setItem('isLoggedIn', 'true');
155
+ window.location.href = 'index.html';
156
+ }
157
+ </script>
158
+ </body>
159
+ </html>
static/signup.html ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Sign Up - IntervuAI Pro</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800&family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link rel="stylesheet" href="assets/css/premium.css">
10
+ <link rel="stylesheet" href="assets/css/auth.css">
11
+ </head>
12
+ <body>
13
+ <div class="premium-bg">
14
+ <div class="gradient-mesh"></div>
15
+ <div class="neural-network">
16
+ <canvas id="neuralCanvas"></canvas>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="auth-container">
21
+ <div class="auth-card">
22
+ <div class="auth-header">
23
+ <div class="brand-logo">
24
+ <div class="brand-icon">
25
+ <i class="fas fa-brain"></i>
26
+ </div>
27
+ <span class="brand-text">IntervuAI<span class="brand-pro">Pro</span></span>
28
+ </div>
29
+ <h1 class="auth-title">Create Account</h1>
30
+ <p class="auth-subtitle">Start your AI-powered interview journey</p>
31
+ </div>
32
+
33
+ <div class="alert alert-error hidden" id="errorAlert">
34
+ <i class="fas fa-exclamation-circle"></i>
35
+ <span id="errorMessage"></span>
36
+ </div>
37
+
38
+ <form id="signupForm" class="auth-form">
39
+ <div class="form-group">
40
+ <label class="form-label">
41
+ <i class="fas fa-user"></i>
42
+ <span>Full Name</span>
43
+ </label>
44
+ <input
45
+ type="text"
46
+ name="name"
47
+ id="nameInput"
48
+ class="form-input"
49
+ placeholder="John Doe"
50
+ required
51
+ >
52
+ </div>
53
+
54
+ <div class="form-group">
55
+ <label class="form-label">
56
+ <i class="fas fa-envelope"></i>
57
+ <span>Email Address</span>
58
+ </label>
59
+ <input
60
+ type="email"
61
+ name="email"
62
+ id="emailInput"
63
+ class="form-input"
64
+ placeholder="you@example.com"
65
+ required
66
+ >
67
+ </div>
68
+
69
+ <div class="form-group">
70
+ <label class="form-label">
71
+ <i class="fas fa-lock"></i>
72
+ <span>Password</span>
73
+ </label>
74
+ <input
75
+ type="password"
76
+ name="password"
77
+ id="passwordInput"
78
+ class="form-input"
79
+ placeholder="••••••••"
80
+ required
81
+ >
82
+ </div>
83
+
84
+ <div class="form-group">
85
+ <label class="form-label">
86
+ <i class="fas fa-lock"></i>
87
+ <span>Confirm Password</span>
88
+ </label>
89
+ <input
90
+ type="password"
91
+ name="confirmPassword"
92
+ id="confirmPasswordInput"
93
+ class="form-input"
94
+ placeholder="••••••••"
95
+ required
96
+ >
97
+ </div>
98
+
99
+ <button type="submit" class="btn-primary btn-block">
100
+ <span>Create Account</span>
101
+ <i class="fas fa-arrow-right"></i>
102
+ <div class="btn-glow"></div>
103
+ </button>
104
+ </form>
105
+
106
+ <div class="auth-divider">
107
+ <span>or continue with</span>
108
+ </div>
109
+
110
+ <div class="social-login">
111
+ <button class="btn-social" onclick="socialSignup('google')">
112
+ <i class="fab fa-google"></i>
113
+ </button>
114
+ <button class="btn-social" onclick="socialSignup('github')">
115
+ <i class="fab fa-github"></i>
116
+ </button>
117
+ <button class="btn-social" onclick="socialSignup('linkedin')">
118
+ <i class="fab fa-linkedin"></i>
119
+ </button>
120
+ </div>
121
+
122
+ <div class="auth-footer">
123
+ <p>Already have an account? <a href="login.html" class="link-primary">Sign in</a></p>
124
+ </div>
125
+ </div>
126
+
127
+ <div class="auth-features">
128
+ <div class="feature-item">
129
+ <div class="feature-icon">
130
+ <i class="fas fa-shield-alt"></i>
131
+ </div>
132
+ <h3>Enterprise Security</h3>
133
+ <p>Bank-level encryption and data protection</p>
134
+ </div>
135
+ <div class="feature-item">
136
+ <div class="feature-icon">
137
+ <i class="fas fa-bolt"></i>
138
+ </div>
139
+ <h3>Lightning Fast</h3>
140
+ <p>AI-powered analysis in seconds</p>
141
+ </div>
142
+ <div class="feature-item">
143
+ <div class="feature-icon">
144
+ <i class="fas fa-chart-line"></i>
145
+ </div>
146
+ <h3>Advanced Analytics</h3>
147
+ <p>Comprehensive insights and reports</p>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ <script src="assets/js/neural-bg.js"></script>
153
+ <script>
154
+ document.getElementById('signupForm').addEventListener('submit', function(e) {
155
+ e.preventDefault();
156
+ const name = document.getElementById('nameInput').value;
157
+ const email = document.getElementById('emailInput').value;
158
+ const password = document.getElementById('passwordInput').value;
159
+ const confirmPassword = document.getElementById('confirmPasswordInput').value;
160
+
161
+ if (password !== confirmPassword) {
162
+ document.getElementById('errorAlert').classList.remove('hidden');
163
+ document.getElementById('errorMessage').textContent = 'Passwords do not match';
164
+ return;
165
+ }
166
+
167
+ if (name && email && password) {
168
+ // Store user info in session
169
+ sessionStorage.setItem('userName', name);
170
+ sessionStorage.setItem('userEmail', email);
171
+ sessionStorage.setItem('isLoggedIn', 'true');
172
+
173
+ // Redirect to main page
174
+ window.location.href = 'index.html';
175
+ } else {
176
+ document.getElementById('errorAlert').classList.remove('hidden');
177
+ document.getElementById('errorMessage').textContent = 'Please fill in all fields';
178
+ }
179
+ });
180
+
181
+ function socialSignup(provider) {
182
+ sessionStorage.setItem('userName', provider + '_user');
183
+ sessionStorage.setItem('isLoggedIn', 'true');
184
+ window.location.href = 'index.html';
185
+ }
186
+ </script>
187
+ </body>
188
+ </html>