@@ -1321,8 +1321,8 @@ <h1>Interview Management</h1>
13211321 < div class ="custom-tab " data-tab ="results " onclick ="switchInterviewTab('results', this) ">
13221322 < i class ="fas fa-chart-simple me-1 "> </ i > Results
13231323 </ div >
1324- < div class ="custom-tab " data-tab ="360review " onclick ="switchInterviewTab('360review ', this) ">
1325- < i class ="fas fa-arrows-spin me-1 " style ="color:#7c3aed ; "> </ i > 360° Review
1324+ < div class ="custom-tab " data-tab ="360interview " onclick ="switchInterviewTab('360interview ', this) ">
1325+ < i class ="fas fa-street-view me-1 " style ="color:#0d9488 ; "> </ i > 360° Interview Result
13261326 </ div >
13271327 </ div >
13281328
@@ -1486,30 +1486,42 @@ <h5>No Results Yet</h5>
14861486 </ div >
14871487 </ div >
14881488
1489- <!-- ===== 360° REVIEW TAB ===== -->
1490- < div id ="interviewTab-360review " class ="tab-content ">
1489+ <!-- ===== 360° INTERVIEW RESULT TAB ===== -->
1490+ < div id ="interviewTab-360interview " class ="tab-content ">
14911491 < div style ="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px;flex-wrap:wrap;gap:12px; ">
14921492 < div >
14931493 < h5 style ="margin:0 0 4px 0;font-weight:700;display:flex;align-items:center;gap:8px; ">
1494- < i class ="fas fa-arrows-spin " style ="color:#7c3aed ; "> </ i > 360° Interview Review
1494+ < i class ="fas fa-street-view " style ="color:#0d9488 ; "> </ i > 360° Interview Results
14951495 </ h5 >
1496- < p style ="font-size:0.84rem;color:var(--text-secondary);margin:0; "> Collect multi-perspective feedback from managers, peers, and cross-functional evaluators </ p >
1496+ < p style ="font-size:0.84rem;color:var(--text-secondary);margin:0; "> View results from AI-proctored 360° interviews with room camera monitoring </ p >
14971497 </ div >
1498- < button class ="btn-primary-custom " onclick ="open360ReviewModal() ">
1499- < i class ="fas fa-plus "> </ i > New 360° Review
1500- </ button >
15011498 </ div >
15021499
1503- <!-- Active 360 Reviews -->
1504- < div id ="review360List "> </ div >
1505-
1506- < div id ="review360EmptyState " class ="empty-state " style ="display:none; ">
1507- < i class ="fas fa-arrows-spin " style ="color:#7c3aed; "> </ i >
1508- < h5 > No 360° Reviews Yet</ h5 >
1509- < p > Create a 360° interview review to collect holistic feedback from multiple evaluators.</ p >
1510- < button class ="btn-primary-custom mt-3 " onclick ="open360ReviewModal() ">
1511- < i class ="fas fa-plus "> </ i > Create First Review
1512- </ button >
1500+ < div class ="card ">
1501+ < div class ="card-header ">
1502+ < h5 > < i class ="fas fa-street-view me-2 " style ="color:#0d9488; "> </ i > 360° Proctored Interview Results</ h5 >
1503+ </ div >
1504+ < div class ="card-body ">
1505+ < div class ="table-responsive ">
1506+ < table class ="data-table ">
1507+ < thead >
1508+ < tr >
1509+ < th > Candidate</ th > < th > Position</ th > < th > Date</ th >
1510+ < th > Overall Score</ th > < th > Trust Score</ th > < th > Room Camera</ th > < th > Violations</ th > < th > Status</ th > < th > Actions</ th >
1511+ </ tr >
1512+ </ thead >
1513+ < tbody id ="interview360ResultsBody "> </ tbody >
1514+ </ table >
1515+ </ div >
1516+ < div id ="interview360EmptyState " class ="empty-state " style ="display:none; ">
1517+ < i class ="fas fa-street-view " style ="color:#0d9488; "> </ i >
1518+ < h5 > No 360° Interview Results</ h5 >
1519+ < p > Schedule a proctored 360° interview to see results here.</ p >
1520+ < button class ="btn-primary-custom mt-3 " onclick ="openModal('scheduleInterviewModal') ">
1521+ < i class ="fas fa-plus "> </ i > Schedule 360° Interview
1522+ </ button >
1523+ </ div >
1524+ </ div >
15131525 </ div >
15141526 </ div >
15151527 </ div >
@@ -2238,6 +2250,12 @@ <h5 class="modal-title"><i class="fas fa-calendar-plus me-2" style="color:var(--
22382250 < div class ="mode-label "> Proctored</ div >
22392251 < div class ="mode-desc "> AI-monitored session</ div >
22402252 </ div >
2253+ < div class ="mode-card " onclick ="selectInterviewMode('proctored_360', this) " style ="position:relative; ">
2254+ < i class ="fas fa-street-view " style ="color:#0d9488; "> </ i >
2255+ < div class ="mode-label "> Proctored 360°</ div >
2256+ < div class ="mode-desc "> AI + 360° room camera</ div >
2257+ < span style ="position:absolute;top:6px;right:6px;font-size:0.6rem;font-weight:700;color:#7c3aed;background:#ede9fe;padding:1px 6px;border-radius:8px;letter-spacing:0.3px; "> OPTIONAL</ span >
2258+ </ div >
22412259 </ div >
22422260 < input type ="hidden " id ="intMode " required >
22432261 </ div >
@@ -2490,64 +2508,7 @@ <h5 class="modal-title" id="jobDetailTitle"><i class="fas fa-briefcase me-2" sty
24902508 </ div >
24912509</ div >
24922510
2493- <!-- ===== 360° REVIEW MODAL ===== -->
2494- < div class ="modal fade " id ="review360Modal " tabindex ="-1 ">
2495- < div class ="modal-dialog modal-lg ">
2496- < div class ="modal-content ">
2497- < div class ="modal-header " style ="background:linear-gradient(135deg,#f5f3ff,#ede9fe);border-bottom:1.5px solid #c4b5fd; ">
2498- < h5 class ="modal-title "> < i class ="fas fa-arrows-spin me-2 " style ="color:#7c3aed; "> </ i > New 360° Interview Review</ h5 >
2499- < button type ="button " class ="btn-close " data-bs-dismiss ="modal "> </ button >
2500- </ div >
2501- < div class ="modal-body ">
2502- < div class ="row ">
2503- < div class ="col-md-6 ">
2504- < div class ="form-group ">
2505- < label > Candidate Name < span style ="color:var(--danger) "> *</ span > </ label >
2506- < input type ="text " class ="form-control-custom " id ="r360CandidateName " placeholder ="Enter candidate name " required >
2507- </ div >
2508- </ div >
2509- < div class ="col-md-6 ">
2510- < div class ="form-group ">
2511- < label > Position < span style ="color:var(--danger) "> *</ span > </ label >
2512- < select class ="form-control-custom " id ="r360Position " required >
2513- < option value =""> Select Position</ option >
2514- </ select >
2515- </ div >
2516- </ div >
2517- </ div >
2518- < div class ="form-group ">
2519- < label > Review Deadline</ label >
2520- < input type ="date " class ="form-control-custom " id ="r360Deadline ">
2521- </ div >
2522-
2523- < div style ="border-top:1px solid var(--border-color);margin:16px 0 12px;padding-top:14px; ">
2524- < h6 style ="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:8px; ">
2525- < i class ="fas fa-users " style ="color:#7c3aed; "> </ i > Evaluators
2526- < span style ="font-size:0.72rem;font-weight:400;color:var(--text-secondary); "> Add at least 2 evaluators from different perspectives</ span >
2527- </ h6 >
2528- < div id ="r360EvaluatorsList "> </ div >
2529- < button type ="button " class ="btn-outline-custom " style ="width:100%;margin-top:8px;border-style:dashed; " onclick ="add360Evaluator() ">
2530- < i class ="fas fa-plus "> </ i > Add Evaluator
2531- </ button >
2532- </ div >
2533-
2534- < div style ="border-top:1px solid var(--border-color);margin:16px 0 12px;padding-top:14px; ">
2535- < h6 style ="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:8px; ">
2536- < i class ="fas fa-clipboard-list " style ="color:var(--primary); "> </ i > Competencies to Evaluate
2537- </ h6 >
2538- < div id ="r360CompetenciesList " style ="display:flex;flex-wrap:wrap;gap:8px; ">
2539- </ div >
2540- </ div >
2541- </ div >
2542- < div class ="modal-footer ">
2543- < button type ="button " class ="btn-outline-custom " data-bs-dismiss ="modal "> Cancel</ button >
2544- < button type ="button " class ="btn-primary-custom " onclick ="create360Review() " style ="background:#7c3aed;border-color:#7c3aed; ">
2545- < i class ="fas fa-arrows-spin "> </ i > Create 360° Review
2546- </ button >
2547- </ div >
2548- </ div >
2549- </ div >
2550- </ div >
2511+ <!-- 360° Review Modal removed — replaced by 360° Interview Result tab -->
25512512
25522513< script src ="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js "> </ script >
25532514< script src ="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js "> </ script >
@@ -2763,6 +2724,7 @@ <h6 style="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;ali
27632724 renderCompletedInterviewTable ( ) ;
27642725 renderCancelledInterviewTable ( ) ;
27652726 renderProctoredTable ( ) ;
2727+ render360InterviewResults ( ) ;
27662728 renderResultsTable ( ) ;
27672729 renderCandidates ( ) ;
27682730 updateInterviewCounts ( ) ;
@@ -2782,6 +2744,7 @@ <h6 style="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;ali
27822744 renderCompletedInterviewTable ( ) ;
27832745 renderCancelledInterviewTable ( ) ;
27842746 renderProctoredTable ( ) ;
2747+ render360InterviewResults ( ) ;
27852748 renderResultsTable ( ) ;
27862749 renderCandidates ( ) ;
27872750 updateInterviewCounts ( ) ;
@@ -3565,11 +3528,18 @@ <h6 style="font-size:0.88rem;font-weight:700;margin-bottom:10px;display:flex;ali
35653528 const meetingHint = document . getElementById ( 'meetingLinkHint' ) ;
35663529 const meetingInput = document . getElementById ( 'intMeetingLink' ) ;
35673530
3568- if ( mode === 'proctored' ) {
3531+ if ( mode === 'proctored' || mode === 'proctored_360' ) {
35693532 proctoredSettings . classList . add ( 'visible' ) ;
35703533 meetingHint . style . display = 'block' ;
35713534 meetingHint . textContent = '🔒 A secure tokenised interview link will be auto-generated for the candidate.' ;
35723535 meetingInput . placeholder = 'Auto-generated (or enter custom proctored platform URL)' ;
3536+ // Auto-enable 360 room camera for proctored_360 mode
3537+ const cam360 = document . getElementById ( 'proctor360Camera' ) ;
3538+ const cam360Info = document . getElementById ( 'proctor360Info' ) ;
3539+ if ( mode === 'proctored_360' && cam360 ) {
3540+ cam360 . checked = true ;
3541+ if ( cam360Info ) cam360Info . style . display = 'block' ;
3542+ }
35733543 } else {
35743544 proctoredSettings . classList . remove ( 'visible' ) ;
35753545 const hints = {
@@ -3943,7 +3913,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
39433913 const emptyState = document . getElementById ( 'proctoredEmptyState' ) ;
39443914 if ( ! tbody ) return ;
39453915
3946- const proctored = appData . interviews . filter ( i => i . mode === 'proctored' ) ;
3916+ const proctored = appData . interviews . filter ( i => i . mode === 'proctored' || i . mode === 'proctored_360' ) ;
39473917
39483918 if ( proctored . length === 0 ) {
39493919 try { tbody . closest ( 'table' ) . style . display = 'none' ; } catch ( e ) { }
@@ -4018,6 +3988,42 @@ <h3>${stats.tokenLinksGenerated}</h3>
40183988 ` ) . join ( '' ) ;
40193989}
40203990
3991+
3992+ function render360InterviewResults ( ) {
3993+ const tbody = document . getElementById ( 'interview360ResultsBody' ) ;
3994+ const emptyState = document . getElementById ( 'interview360EmptyState' ) ;
3995+ if ( ! tbody ) return ;
3996+ const results360 = appData . interviews . filter ( i => ( i . mode === 'proctored_360' || ( i . mode === 'proctored' && i . proctorSettings && i . proctorSettings . roomCamera360 ) ) && i . status === 'completed' ) ;
3997+ if ( results360 . length === 0 ) {
3998+ try { tbody . closest ( 'table' ) . style . display = 'none' ; } catch ( e ) { }
3999+ if ( emptyState ) emptyState . style . display = 'block' ;
4000+ return ;
4001+ }
4002+ try { tbody . closest ( 'table' ) . style . display = 'table' ; } catch ( e ) { }
4003+ if ( emptyState ) emptyState . style . display = 'none' ;
4004+ tbody . innerHTML = results360 . map ( int => {
4005+ const score = int . score || int . overall_score || '---' ;
4006+ const trust = int . trust_score || '---' ;
4007+ const violations = int . violation_count || 0 ;
4008+ const scoreColor = typeof score === 'number' ? ( score >= 70 ? 'var(--success)' : score >= 40 ? 'var(--warning)' : 'var(--danger)' ) : 'var(--text-secondary)' ;
4009+ const trustColor = typeof trust === 'number' ? ( trust >= 70 ? 'var(--success)' : trust >= 40 ? 'var(--warning)' : 'var(--danger)' ) : 'var(--text-secondary)' ;
4010+ return `
4011+ + '<tr>'
4012+ + '<td><strong>' + escapeHtml(int.candidateName) + '</strong></td>'
4013+ + '<td>' + escapeHtml(int.position) + '</td>'
4014+ + '<td>' + formatDate(int.date) + '</td>'
4015+ + '<td><span style="font-weight:700;color:' + scoreColor + ';">' + score + (typeof score === 'number' ? '%' : '') + '</span></td>'
4016+ + '<td><span style="font-weight:700;color:' + trustColor + ';">' + trust + (typeof trust === 'number' ? '%' : '') + '</span></td>'
4017+ + '<td><span style="display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:6px;font-size:0.78rem;font-weight:600;background:#ccfbf1;color:#0d9488;"><i class="fas fa-street-view"></i> Active</span></td>'
4018+ + '<td><span style="font-weight:600;color:' + (violations > 3 ? 'var(--danger)' : 'var(--success)') + ';">' + violations + '</span></td>'
4019+ + '<td><span class="status-badge ' + int.status + '">' + capitalize(int.status) + '</span></td>'
4020+ + '<td>'
4021+ + '<button class="btn-sm-action view" onclick="viewInterview(\'' + int.id + '\')" title="Details"><i class="fas fa-eye"></i></button>'
4022+ + (int.token ? '<button class="btn-sm-action view" onclick="window.open(\'/interview/results.html?token=' + int.token + '\', \'_blank\')" title="Full Report"><i class="fas fa-file-contract"></i></button>' : '')
4023+ + '</td>'
4024+ + '</tr>';
4025+ }).join('');
4026+ }
40214027function updateInterviewCounts() {
40224028 const scheduled = appData.interviews.filter(i => i.status === 'scheduled').length;
40234029 const completed = appData.interviews.filter(i => i.status === 'completed').length;
@@ -4314,7 +4320,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
43144320 const token = generateInterviewToken();
43154321 const interviewLink = getInterviewLink(token);
43164322
4317- const proctorSettings = mode === 'proctored' ? {
4323+ const proctorSettings = ( mode === 'proctored' || mode === 'proctored_360') ? {
43184324 camera: document.getElementById('proctorCamera')?.checked || false,
43194325 eyeTracking: document.getElementById('proctorEyeTracking')?.checked || false,
43204326 screenRecord: document.getElementById('proctorScreenRecord')?.checked || false,
@@ -4340,10 +4346,10 @@ <h3>${stats.tokenLinksGenerated}</h3>
43404346 candidate_phone: candidatePhone,
43414347 interview_role: position,
43424348 interview_level: round,
4343- interview_type : mode === 'proctored' ? 'ai_voice' : 'human_live' ,
4344- token_type : mode === 'proctored' ? 'shortlist' : 'custom' ,
4349+ interview_type: ( mode === 'proctored' || mode === 'proctored_360') ? 'ai_voice' : 'human_live',
4350+ token_type: ( mode === 'proctored' || mode === 'proctored_360') ? 'shortlist' : 'custom',
43454351 token,
4346- status : mode === 'proctored' ? 'pending' : 'scheduled' ,
4352+ status: ( mode === 'proctored' || mode === 'proctored_360') ? 'pending' : 'scheduled',
43474353 job_id: selectedJobId,
43484354 tenant_id: user.company_id || user.tenant_id || null,
43494355 company_id: user.company_id || user.tenant_id || null,
@@ -4389,9 +4395,9 @@ <h3>${stats.tokenLinksGenerated}</h3>
43894395 DataStore.update(data => {
43904396 data.interviews.unshift(localPayload);
43914397 data.activities.unshift({
4392- type : mode === 'proctored' ? 'proctored' : 'interview' ,
4398+ type: ( mode === 'proctored' || mode === 'proctored_360') ? 'proctored' : 'interview',
43934399 title: ` Interview Scheduled : ${candidateName } `,
4394- description : `${ position } · ${ round } · ${ formatDate ( date ) } ${ formatTime ( startTime ) } · ${ mode === 'proctored' ? '🔒 Proctored' : mode } ` ,
4400+ description: ` $ { position } · ${round } · ${formatDate ( date ) } ${formatTime ( startTime ) } · ${( mode === 'proctored' || mode === 'proctored_360' ) ? '🔒 Proctored' : mode } `,
43954401 timestamp: Date.now()
43964402 });
43974403 });
@@ -4402,6 +4408,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
44024408 renderCompletedInterviewTable();
44034409 renderCancelledInterviewTable();
44044410 renderProctoredTable();
4411+ render360InterviewResults();
44054412 renderDashboardStats();
44064413 renderDashboardInterviews();
44074414 renderDashboardActivities();
@@ -4894,6 +4901,7 @@ <h6 style="color:var(--text-primary);margin-bottom:4px;">No Applications Yet</h6
48944901 renderCompletedInterviewTable ( ) ;
48954902 renderCancelledInterviewTable ( ) ;
48964903 renderProctoredTable ( ) ;
4904+ render360InterviewResults ( ) ;
48974905 renderDashboardStats ( ) ;
48984906 renderDashboardActivities ( ) ;
48994907 updateInterviewCounts ( ) ;
0 commit comments