Skip to content

Commit c429b65

Browse files
committed
fix: interview results not showing for manually scheduled interviews - switchInterviewTab now re-renders active tab data on switch - Added renderResultsTable to all render chains - DB payload saves scheduled_date, time, interviewer, mode - cancelInterview persists to Supabase - Added Mark Completed button for human-led interviews - Migration 021 adds scheduling columns to interviews table
1 parent 9546200 commit c429b65

2 files changed

Lines changed: 105 additions & 11 deletions

File tree

dashboard/hr.html

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,12 +3204,13 @@ <h5 class="modal-title" id="jobDetailTitle"><i class="fas fa-briefcase me-2" sty
32043204
candidatePhone: dbInt.candidate_phone || '',
32053205
position: dbInt.interview_role || 'Position',
32063206
round: dbInt.interview_level || 'Round',
3207-
date: dbInt.completed_at ? dbInt.completed_at.split('T')[0] :
3208-
dbInt.created_at ? dbInt.created_at.split('T')[0] : new Date().toISOString().split('T')[0],
3209-
startTime: dbInt.started_at ? new Date(dbInt.started_at).toTimeString().slice(0,5) : '10:00',
3210-
endTime: dbInt.completed_at ? new Date(dbInt.completed_at).toTimeString().slice(0,5) : '10:45',
3211-
interviewer: dbInt.interview_type === 'ai_voice' ? 'AI Interviewer' : 'Recruiting Team',
3212-
mode: dbInt.interview_type === 'ai_voice' ? 'proctored' : 'video',
3207+
date: dbInt.scheduled_date || (dbInt.completed_at ? dbInt.completed_at.split('T')[0] :
3208+
dbInt.created_at ? dbInt.created_at.split('T')[0] : new Date().toISOString().split('T')[0]),
3209+
startTime: dbInt.scheduled_time || (dbInt.started_at ? new Date(dbInt.started_at).toTimeString().slice(0,5) : '10:00'),
3210+
endTime: dbInt.scheduled_end_time || (dbInt.completed_at ? new Date(dbInt.completed_at).toTimeString().slice(0,5) : '10:45'),
3211+
interviewer: dbInt.interviewer_name || (dbInt.interview_type === 'ai_voice' ? 'AI Interviewer' : 'Recruiting Team'),
3212+
interviewerEmail: dbInt.interviewer_email || '',
3213+
mode: dbInt.interview_mode || (dbInt.interview_type === 'ai_voice' ? 'proctored' : 'video'),
32133214
status: dbInt.status === 'pending' ? 'scheduled' : (dbInt.status || 'scheduled'),
32143215
token: dbInt.token || '',
32153216
job_id: dbInt.job_id,
@@ -3221,7 +3222,9 @@ <h5 class="modal-title" id="jobDetailTitle"><i class="fas fa-briefcase me-2" sty
32213222
completedAt: dbInt.completed_at || null,
32223223
conversationLog: dbInt.conversation_log || null,
32233224
notificationLog: dbInt.notification_log || [],
3224-
proctorSettings: dbInt.proctor_settings || null
3225+
proctorSettings: dbInt.proctor_settings || null,
3226+
meetingLink: dbInt.meeting_link || '',
3227+
notes: dbInt.notes || ''
32253228
};
32263229
});
32273230
// DEDUP: Remove duplicate interview records (keep first = newest by created_at)
@@ -4165,6 +4168,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
41654168
<button class="btn-sm-action view" title="Details" onclick="viewInterview('${int.id}')"><i class="fas fa-eye"></i></button>
41664169
<button class="btn-sm-action notify" title="Send Notification" onclick="openSendNotifModal('${int.id}')"><i class="fas fa-paper-plane"></i></button>
41674170
${int.mode === 'proctored' ? `<button class="btn-sm-action" style="background:#ede9fe;color:#7c3aed;" title="Proctor Settings" onclick="viewProctorSettings('${int.id}')"><i class="fas fa-shield-halved"></i></button>` : ''}
4171+
<button class="btn-sm-action" style="background:#d1fae5;color:#065f46;" title="Mark Completed" onclick="markInterviewCompleted('${int.id}')"><i class="fas fa-check"></i></button>
41684172
<button class="btn-sm-action delete" title="Cancel" onclick="cancelInterview('${int.id}')"><i class="fas fa-xmark"></i></button>
41694173
</td>
41704174
</tr>
@@ -4457,11 +4461,18 @@ <h3>${stats.tokenLinksGenerated}</h3>
44574461
}
44584462

44594463
function switchInterviewTab(tabName, el) {
4460-
document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
4461-
document.querySelectorAll('.custom-tab').forEach(t => t.classList.remove('active'));
4464+
document.querySelectorAll('#section-interviews .tab-content').forEach(t => t.classList.remove('active'));
4465+
document.querySelectorAll('#section-interviews .custom-tab').forEach(t => t.classList.remove('active'));
44624466
const tab = document.getElementById('interviewTab-' + tabName);
44634467
if (tab) tab.classList.add('active');
44644468
if (el) el.classList.add('active');
4469+
// Re-render the active tab's data to ensure fresh results are shown
4470+
if (tabName === 'completed') renderCompletedInterviewTable();
4471+
else if (tabName === 'cancelled') renderCancelledInterviewTable();
4472+
else if (tabName === 'proctored') renderProctoredTable();
4473+
else if (tabName === 'results') renderResultsTable();
4474+
else if (tabName === '360interview') render360InterviewResults();
4475+
else if (tabName === 'upcoming') renderInterviewTable();
44654476
}
44664477

44674478
// ===== HANDLE CREATE JOB =====
@@ -4716,6 +4727,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
47164727
interview_role: position,
47174728
interview_level: round,
47184729
interview_type: (mode === 'proctored' || mode === 'proctored_360') ? 'ai_voice' : 'human_live',
4730+
interview_mode: mode,
47194731
token_type: (mode === 'proctored' || mode === 'proctored_360') ? 'shortlist' : 'custom',
47204732
token,
47214733
status: (mode === 'proctored' || mode === 'proctored_360') ? 'pending' : 'scheduled',
@@ -4727,7 +4739,14 @@ <h3>${stats.tokenLinksGenerated}</h3>
47274739
question_count: parseInt(document.getElementById('intQuestionCount')?.value) || 6,
47284740
max_attempts: 1,
47294741
interview_language: language,
4730-
experience_level: experienceLevel
4742+
experience_level: experienceLevel,
4743+
scheduled_date: date,
4744+
scheduled_time: startTime,
4745+
scheduled_end_time: endTime,
4746+
interviewer_name: interviewer,
4747+
interviewer_email: interviewerEmail || '',
4748+
meeting_link: meetingLink || '',
4749+
notes: notes || ''
47314750
};
47324751

47334752
// Local Payload for immediate UI rendering (camelCase API expectations)
@@ -4780,6 +4799,7 @@ <h3>${stats.tokenLinksGenerated}</h3>
47804799
renderCancelledInterviewTable();
47814800
renderProctoredTable();
47824801
render360InterviewResults();
4802+
renderResultsTable();
47834803
renderDashboardStats();
47844804
renderDashboardInterviews();
47854805
renderDashboardActivities();
@@ -5271,8 +5291,9 @@ <h6 style="color:var(--text-primary);margin-bottom:4px;">No Applications Yet</h6
52715291
showToast('🗑️ Job posting deleted');
52725292
}
52735293

5274-
function cancelInterview(id) {
5294+
async function cancelInterview(id) {
52755295
if (!confirm('Cancel this interview?')) return;
5296+
const int = appData.interviews.find(i => i.id === id);
52765297
DataStore.update(data => {
52775298
const interview = data.interviews.find(i => i.id === id);
52785299
if (interview) {
@@ -5291,10 +5312,68 @@ <h6 style="color:var(--text-primary);margin-bottom:4px;">No Applications Yet</h6
52915312
renderCancelledInterviewTable();
52925313
renderProctoredTable();
52935314
render360InterviewResults();
5315+
renderResultsTable();
52945316
renderDashboardStats();
52955317
renderDashboardActivities();
52965318
updateInterviewCounts();
52975319
showToast('Interview cancelled');
5320+
5321+
// Persist cancellation to Supabase
5322+
try {
5323+
const db = (typeof getSupabaseClient === 'function' ? getSupabaseClient() : null) || window.SimpaticoDB || window._supabaseClient;
5324+
if (db && int?.id) {
5325+
const { error } = await db.from('interviews').update({ status: 'cancelled' }).eq('id', int.id);
5326+
if (error) console.warn('[cancel] DB update error:', error.message);
5327+
else console.log('[cancel] ✅ Interview cancelled in DB:', int.id);
5328+
}
5329+
} catch (e) {
5330+
console.warn('[cancel] DB persist failed:', e.message);
5331+
}
5332+
}
5333+
5334+
async function markInterviewCompleted(id) {
5335+
if (!confirm('Mark this interview as completed?')) return;
5336+
const int = appData.interviews.find(i => i.id === id);
5337+
DataStore.update(data => {
5338+
const interview = data.interviews.find(i => i.id === id);
5339+
if (interview) {
5340+
interview.status = 'completed';
5341+
interview.completedAt = new Date().toISOString();
5342+
data.activities.unshift({
5343+
type: 'interview',
5344+
title: `Interview Completed: ${interview.candidateName}`,
5345+
description: `${interview.position} · ${interview.round} · Marked completed by HR`,
5346+
timestamp: Date.now()
5347+
});
5348+
}
5349+
});
5350+
appData = DataStore.get();
5351+
renderInterviewTable();
5352+
renderCompletedInterviewTable();
5353+
renderCancelledInterviewTable();
5354+
renderProctoredTable();
5355+
render360InterviewResults();
5356+
renderResultsTable();
5357+
renderDashboardStats();
5358+
renderDashboardInterviews();
5359+
renderDashboardActivities();
5360+
updateInterviewCounts();
5361+
showToast('✅ Interview marked as completed');
5362+
5363+
// Persist to Supabase
5364+
try {
5365+
const db = (typeof getSupabaseClient === 'function' ? getSupabaseClient() : null) || window.SimpaticoDB || window._supabaseClient;
5366+
if (db && int?.id) {
5367+
const { error } = await db.from('interviews').update({
5368+
status: 'completed',
5369+
completed_at: new Date().toISOString()
5370+
}).eq('id', int.id);
5371+
if (error) console.warn('[complete] DB update error:', error.message);
5372+
else console.log('[complete] ✅ Interview marked completed in DB:', int.id);
5373+
}
5374+
} catch (e) {
5375+
console.warn('[complete] DB persist failed:', e.message);
5376+
}
52985377
}
52995378

53005379
function viewProctorSettings(id) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- Migration 021: Add scheduling metadata columns to interviews table
2+
-- These columns preserve the HR-scheduled date/time, interviewer details,
3+
-- and interview mode so they survive database sync round-trips.
4+
5+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS scheduled_date DATE;
6+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS scheduled_time TEXT;
7+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS scheduled_end_time TEXT;
8+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS interviewer_name TEXT;
9+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS interviewer_email TEXT;
10+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS interview_mode TEXT;
11+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS meeting_link TEXT;
12+
ALTER TABLE interviews ADD COLUMN IF NOT EXISTS notes TEXT;
13+
14+
-- Index for faster filtering by scheduled date
15+
CREATE INDEX IF NOT EXISTS idx_interviews_scheduled_date ON interviews(scheduled_date);

0 commit comments

Comments
 (0)