joker7094's picture
with all the changes
0583f91
// Helper function to convert basic markdown to HTML
function convertMarkdownToHTML(text) {
if (typeof text !== 'string') {
return text;
}
let htmlText = text;
htmlText = htmlText.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
htmlText = htmlText.replace(/\*(.*?)\*/g, '<em>$1</em>');
htmlText = htmlText.replace(/\n/g, '<br>');
return htmlText;
}
document.addEventListener('DOMContentLoaded', () => {
const studentSelector = document.getElementById('student-selector');
const generateReportBtn = document.getElementById('generate-report-btn');
const jobApplicationInput = document.getElementById('job-application-input');
const analyzeJobBtn = document.getElementById('analyze-job-btn');
const loadingSpinner = document.getElementById('loading-spinner');
const reportContainer = document.getElementById('reports');
const jobAnalysisContainer = document.getElementById('job-analysis');
const chatbotContainer = document.getElementById('chat');
const chatForm = document.getElementById('chat-form');
const chatInput = document.getElementById('chat-input');
const chatHistory = document.getElementById('chat-history');
const newChatBtn = document.getElementById('new-chat-btn');
const chatSessionsList = document.getElementById('chat-sessions-list');
// Chat State
let currentSessionId = null;
// Chart Instances
let skillsChart = null;
let dsaChart = null;
let jobMatchChart = null;
let cgpaTrendChart = null;
let dsaPerformanceChart = null;
// Store pan-zoom instances to prevent memory leaks
const panZoomInstances = new Map();
// Initialize Mermaid
mermaid.initialize({
startOnLoad: false,
theme: 'dark',
themeVariables: {
primaryColor: '#00E5FF',
primaryTextColor: '#F0F0F0',
primaryBorderColor: '#00E5FF',
lineColor: '#00E5FF',
secondaryColor: '#0099CC',
tertiaryColor: '#2C2C2C'
},
flowchart: {
useMaxWidth: false,
htmlLabels: true,
curve: 'basis'
}
});
// Navigation Elements
const navLinks = document.querySelectorAll('.nav-link');
const contentSections = document.querySelectorAll('.content-section');
// --- Sidebar Navigation ---
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetId = link.getAttribute('href').substring(1);
navLinks.forEach(l => l.classList.remove('active'));
link.classList.add('active');
contentSections.forEach(section => {
section.classList.add('hidden');
if (section.id === targetId) {
section.classList.remove('hidden');
section.classList.add('active');
if (targetId === 'dashboard') {
loadDashboardData();
}
} else {
section.classList.remove('active');
}
});
});
});
// --- Tab Navigation for Report Section ---
initializeTabs('.ai-analysis-report', 'data-tab');
initializeTabs('.ai-suggestion-channel', 'data-tab');
function initializeTabs(containerSelector, attribute) {
const container = document.querySelector(containerSelector);
if (!container) return;
const tabButtons = container.querySelectorAll('.tab-btn');
const tabContents = container.querySelectorAll('.tab-content');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const targetTab = button.getAttribute(attribute);
tabButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
tabContents.forEach(content => {
content.classList.remove('active');
if (content.id === `${targetTab}-tab`) {
content.classList.add('active');
if (targetTab === 'performance' && studentSelector.value) {
loadPerformanceCharts(studentSelector.value);
}
}
});
});
});
}
// --- Initial Navigation Setup ---
const dashboardLink = document.querySelector('.nav-link[href="#dashboard"]');
if (dashboardLink) {
dashboardLink.click();
}
// --- Populate student dropdown ---
fetch('/api/students')
.then(response => response.json())
.then(students => {
students.forEach(student => {
const option = document.createElement('option');
option.value = student.enrollment_no;
option.textContent = `${student.name} (${student.enrollment_no})`;
studentSelector.appendChild(option);
});
})
.catch(error => console.error('Error fetching students:', error));
// --- Enable buttons when inputs are filled ---
studentSelector.addEventListener('change', () => {
const hasSelection = !!studentSelector.value;
generateReportBtn.disabled = !hasSelection;
});
jobApplicationInput.addEventListener('input', () => {
analyzeJobBtn.disabled = !jobApplicationInput.value.trim();
});
// --- Report History Logic ---
const reportHistoryBtn = document.getElementById('report-history-btn');
const reportHistoryModal = document.getElementById('report-history-modal');
const reportHistoryList = document.getElementById('report-history-list');
const closeModalBtn = document.querySelector('.close-modal');
if (reportHistoryBtn) {
reportHistoryBtn.addEventListener('click', () => {
const enrollmentNo = studentSelector.value;
if (!enrollmentNo) {
alert('Please select a student first.');
return;
}
loadReportHistory(enrollmentNo);
reportHistoryModal.classList.remove('hidden');
});
}
if (closeModalBtn) {
closeModalBtn.addEventListener('click', () => {
reportHistoryModal.classList.add('hidden');
});
}
window.addEventListener('click', (e) => {
if (e.target === reportHistoryModal) {
reportHistoryModal.classList.add('hidden');
}
});
function loadReportHistory(enrollmentNo) {
reportHistoryList.innerHTML = '<div class="loading">Loading history...</div>';
fetch(`/api/reports/history/${enrollmentNo}`)
.then(response => response.json())
.then(history => {
reportHistoryList.innerHTML = '';
if (history.length === 0) {
reportHistoryList.innerHTML = '<p style="text-align:center; color:var(--text-tertiary)">No saved reports found.</p>';
return;
}
history.forEach(report => {
const item = document.createElement('div');
item.className = 'report-item';
const date = new Date(report.timestamp).toLocaleDateString(undefined, {
year: 'numeric', month: 'short', day: 'numeric',
hour: '2-digit', minute: '2-digit'
});
item.innerHTML = `
<div class="report-title">${report.title}</div>
<div class="report-date">${date}</div>
`;
item.addEventListener('click', () => {
loadSavedReport(enrollmentNo, report.id);
reportHistoryModal.classList.add('hidden');
});
reportHistoryList.appendChild(item);
});
})
.catch(error => {
console.error('Error fetching report history:', error);
reportHistoryList.innerHTML = '<p style="color:var(--error)">Failed to load history.</p>';
});
}
function loadSavedReport(enrollmentNo, reportId) {
loadingSpinner.classList.remove('hidden');
fetch(`/api/report/load/${enrollmentNo}/${reportId}`)
.then(response => response.json())
.then(report => {
loadingSpinner.classList.add('hidden');
if (report.error) {
alert(`Error loading report: ${report.error}`);
} else {
displayNewReport(report);
}
})
.catch(error => {
loadingSpinner.classList.add('hidden');
console.error('Error loading saved report:', error);
alert('Failed to load report.');
});
}
// --- Handle "Generate Report" button click ---
generateReportBtn.addEventListener('click', () => {
const enrollmentNo = studentSelector.value;
if (!enrollmentNo) return;
loadingSpinner.classList.remove('hidden');
document.querySelector('.nav-link[href="#reports"]').click();
fetch(`/api/report/${enrollmentNo}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(report => {
loadingSpinner.classList.add('hidden');
if (report.error) {
alert(`Error generating report: ${report.error}`);
} else {
displayNewReport(report);
}
})
.catch(error => {
loadingSpinner.classList.add('hidden');
console.error('Report generation error:', error);
alert(`An unexpected error occurred: ${error.message}`);
});
if (enrollmentNo) {
// Also load chat history for this student
loadChatSessions(enrollmentNo);
} else {
alert('Please select a student first.');
}
});
// New Chat Button
if (newChatBtn) {
newChatBtn.addEventListener('click', () => {
currentSessionId = null;
chatHistory.innerHTML = '';
// Remove active class from all sessions
document.querySelectorAll('.session-item').forEach(item => item.classList.remove('active'));
appendMessage('Start a new conversation!', 'ai', false, true);
});
}
// --- Handle "Analyze Job Application" button click ---
analyzeJobBtn.addEventListener('click', () => {
const jobDescription = jobApplicationInput.value.trim();
const enrollmentNo = studentSelector.value;
if (!jobDescription) {
alert('Please paste a job description.');
return;
}
if (!enrollmentNo) {
alert('Please select a student first.');
return;
}
loadingSpinner.classList.remove('hidden');
jobAnalysisContainer.classList.add('hidden');
fetch('/api/job-analysis', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enrollment_no: enrollmentNo, job_description: jobDescription })
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
loadingSpinner.classList.add('hidden');
if (data.error) {
alert(`Error analyzing job application: ${data.error}`);
} else {
displayJobAnalysis(data.data);
}
})
.catch(error => {
loadingSpinner.classList.add('hidden');
console.error('Job analysis error:', error);
alert(`An unexpected error occurred: ${error.message}`);
});
});
// --- Chat form submission ---
chatForm.addEventListener('submit', (e) => {
e.preventDefault();
const enrollmentNo = studentSelector.value;
const question = chatInput.value.trim();
if (!question || !enrollmentNo) {
if (!enrollmentNo) alert('Please select a student first.');
return;
}
// If not already in chat tab, switch to it
const chatTabLink = document.querySelector('.nav-link[href="#chat"]');
if (chatTabLink && !chatTabLink.classList.contains('active')) {
chatTabLink.click();
}
appendMessage(question, 'user');
chatInput.value = '';
appendMessage('Thinking...', 'ai', true);
fetch('/api/ask', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
enrollment_no: enrollmentNo,
question: question,
session_id: currentSessionId
})
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
const loadingElement = chatHistory.querySelector('.loading');
if (loadingElement) {
loadingElement.parentElement.remove();
}
if (data.success) {
appendMessage(data.answer, 'ai', false, true);
// Update session ID if it was a new session
if (!currentSessionId && data.session_id) {
currentSessionId = data.session_id;
// Reload sessions list to show the new one
loadChatSessions(enrollmentNo);
}
} else {
appendMessage(`Error: ${data.error}`, 'ai');
}
chatHistory.scrollTop = chatHistory.scrollHeight;
})
.catch(error => {
console.error('Chat error:', error);
const loadingElement = chatHistory.querySelector('.loading');
if (loadingElement) {
loadingElement.parentElement.remove();
}
appendMessage('Sorry, an error occurred while fetching the answer.', 'ai', false, true);
});
});
// --- Chat History Functions ---
function loadChatSessions(enrollmentNo) {
if (!chatSessionsList) return;
fetch(`/api/chat/history/${enrollmentNo}`)
.then(res => res.json())
.then(sessions => {
chatSessionsList.innerHTML = '';
if (sessions.error) {
console.error(sessions.error);
return;
}
if (sessions.length === 0) {
chatSessionsList.innerHTML = '<div style="padding:1rem; color:var(--text-tertiary); text-align:center;">No history</div>';
return;
}
sessions.forEach(session => {
const el = document.createElement('div');
el.className = `session-item ${session.id === currentSessionId ? 'active' : ''}`;
el.innerHTML = `
<div class="session-title">${session.title}</div>
<div class="session-date">${new Date(session.timestamp).toLocaleDateString()} ${new Date(session.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</div>
`;
el.addEventListener('click', () => {
loadChatMessages(session);
// Update active state
document.querySelectorAll('.session-item').forEach(item => item.classList.remove('active'));
el.classList.add('active');
});
chatSessionsList.appendChild(el);
});
})
.catch(err => console.error('Error loading chat sessions:', err));
}
function loadChatMessages(session) {
currentSessionId = session.id;
chatHistory.innerHTML = '';
session.messages.forEach(msg => {
appendMessage(msg.text, msg.sender, false, true); // Assuming stored messages are markdown safe or plain text
});
chatHistory.scrollTop = chatHistory.scrollHeight;
}
// --- IMPROVED: Initialize Mermaid Flowchart with Pan/Zoom ---
function initializeMermaidFlowchart(flowchartId, mermaidCode) {
const flowchartCanvas = document.getElementById(flowchartId);
if (!flowchartCanvas) {
console.error(`Flowchart canvas not found: ${flowchartId}`);
return;
}
// Clean up existing pan-zoom instance if any
if (panZoomInstances.has(flowchartId)) {
try {
panZoomInstances.get(flowchartId).destroy();
panZoomInstances.delete(flowchartId);
} catch (e) {
console.warn('Error destroying previous pan-zoom instance:', e);
}
}
// Clear existing content
flowchartCanvas.innerHTML = '';
// Create mermaid div
const mermaidDiv = document.createElement('div');
mermaidDiv.className = 'mermaid';
mermaidDiv.textContent = mermaidCode;
flowchartCanvas.appendChild(mermaidDiv);
// Render mermaid diagram
mermaid.run({
nodes: [mermaidDiv]
}).then(() => {
// Wait for render to complete
setTimeout(() => {
const svg = flowchartCanvas.querySelector('svg');
if (!svg) {
console.error('SVG not found after mermaid render');
return;
}
try {
// Get the actual SVG dimensions
const bbox = svg.getBBox();
const padding = 40;
// Set viewBox with padding to prevent cutoff
svg.setAttribute('viewBox',
`${bbox.x - padding} ${bbox.y - padding} ${bbox.width + padding * 2} ${bbox.height + padding * 2}`
);
// Remove fixed dimensions to allow responsive sizing
svg.removeAttribute('width');
svg.removeAttribute('height');
svg.style.width = '100%';
svg.style.height = '100%';
svg.style.maxWidth = 'none';
svg.style.maxHeight = 'none';
// Initialize svg-pan-zoom
if (typeof svgPanZoom !== 'undefined') {
setTimeout(() => {
try {
const panZoomInstance = svgPanZoom(svg, {
zoomEnabled: true,
controlIconsEnabled: false,
fit: true,
center: true,
minZoom: 0.1,
maxZoom: 10,
zoomScaleSensitivity: 0.3,
dblClickZoomEnabled: true,
mouseWheelZoomEnabled: true,
preventMouseEventsDefault: true,
panEnabled: true,
refreshRate: 'auto'
});
// Store instance for cleanup
panZoomInstances.set(flowchartId, panZoomInstance);
// Apply initial zoom and center
setTimeout(() => {
panZoomInstance.resize();
panZoomInstance.fit();
panZoomInstance.center();
// Zoom in slightly for better readability
panZoomInstance.zoomBy(1.2);
}, 100);
// Add control instructions
if (!flowchartCanvas.querySelector('.flowchart-instructions')) {
const instructions = document.createElement('div');
instructions.className = 'flowchart-instructions';
instructions.innerHTML = '🖱️ Drag to pan • Scroll to zoom • Double-click to reset';
flowchartCanvas.appendChild(instructions);
}
} catch (panZoomError) {
console.error('Pan-zoom initialization error:', panZoomError);
}
}, 300);
}
} catch (error) {
console.error('Error setting up flowchart:', error);
}
}, 200);
}).catch(error => {
console.error('Mermaid rendering error:', error);
flowchartCanvas.innerHTML = '<p style="color: var(--danger-color); padding: 20px;">Error rendering flowchart. Please check the diagram syntax.</p>';
});
}
// --- Display Report Function ---
function displayNewReport(report) {
// Update Profile Card
document.getElementById('student-name').textContent = report.name || 'Student Name';
document.getElementById('student-id').textContent = `ID: ${report.enrollment_no || 'N/A'}`;
document.getElementById('student-phone').textContent = report.phone || '+91 9876543210';
document.getElementById('student-email').textContent = report.email || 'student@example.com';
if (report.photo) {
document.getElementById('student-photo').src = report.photo;
}
// Update Summary Tab
document.getElementById('hr-summary').innerHTML = convertMarkdownToHTML(report.overall_summary || 'No summary available.');
const strengthsList = document.getElementById('summary-strengths');
strengthsList.innerHTML = '';
if (report.analysis?.strengths) {
report.analysis.strengths.forEach(strength => {
const li = document.createElement('li');
li.innerHTML = convertMarkdownToHTML(strength);
strengthsList.appendChild(li);
});
}
const weaknessesList = document.getElementById('summary-weaknesses');
weaknessesList.innerHTML = '';
if (report.analysis?.weaknesses) {
report.analysis.weaknesses.forEach(weakness => {
const li = document.createElement('li');
li.innerHTML = convertMarkdownToHTML(weakness);
weaknessesList.appendChild(li);
});
}
// Update Skills Tab
const skillsList = document.getElementById('skills-list');
skillsList.innerHTML = '';
if (report.skills) {
report.skills.forEach(skill => {
const skillRow = document.createElement('div');
skillRow.className = 'skill-row';
skillRow.innerHTML = `
<div class="skill-cell">${skill.name || 'Skill'}</div>
<div class="skill-cell">${skill.category || 'General'}</div>
<div class="skill-cell">
<div class="progress-bar">
<div class="progress" style="width: ${skill.performance || 50}%"></div>
</div>
<span class="percentage">${skill.performance || 50}%</span>
</div>
`;
skillsList.appendChild(skillRow);
});
}
// Update Performance Tab (CGPA Chart)
if (report.cgpa_trend) {
const ctx = document.getElementById('cgpa-trend-chart');
if (ctx) {
if (cgpaTrendChart) {
cgpaTrendChart.destroy();
}
cgpaTrendChart = new Chart(ctx, {
type: 'line',
data: {
labels: report.cgpa_trend.labels || ['Sem 1', 'Sem 2', 'Sem 3', 'Sem 4', 'Sem 5', 'Sem 6'],
datasets: [{
label: 'CGPA',
data: report.cgpa_trend.values || [7.5, 7.8, 8.0, 8.2, 8.1, 8.3],
backgroundColor: 'rgba(0, 229, 255, 0.2)',
borderColor: 'rgba(0, 229, 255, 1)',
borderWidth: 2,
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: false,
min: 6,
max: 10
}
},
plugins: {
legend: {
display: false
}
}
}
});
}
}
// Update Action Plan Tab with IMPROVED flowchart rendering
const actionPlanContent = document.querySelector('.action-plan-content');
actionPlanContent.innerHTML = '';
if (report.actionable_advice?.recommendations) {
report.actionable_advice.recommendations.forEach((recommendation, index) => {
const actionItem = document.createElement('div');
actionItem.className = 'action-item';
const flowchartId = `flowchart-${index}`;
actionItem.innerHTML = `
<h4>${recommendation.title || 'Action Item'}</h4>
<p>${convertMarkdownToHTML(recommendation.description || '')}</p>
${recommendation.mermaid_flowchart ? `
<div class="flowchart-container">
<div class="flowchart-canvas" id="${flowchartId}"></div>
</div>
` : ''}
`;
actionPlanContent.appendChild(actionItem);
// Render Mermaid diagram using improved function
if (recommendation.mermaid_flowchart) {
// Delay to ensure DOM is ready
setTimeout(() => {
initializeMermaidFlowchart(flowchartId, recommendation.mermaid_flowchart);
}, 100);
}
});
}
// Update Learning Path Tab
const learningPathContent = document.querySelector('.learning-path-content');
learningPathContent.innerHTML = '';
if (report.learning_path) {
report.learning_path.forEach(path => {
const pathItem = document.createElement('div');
pathItem.className = 'path-item';
pathItem.innerHTML = `
<h4>${path.title || 'Learning Path'}</h4>
<p>${convertMarkdownToHTML(path.description || '')}</p>
`;
if (path.resources) {
const resourcesDiv = document.createElement('div');
resourcesDiv.className = 'recommended-resources';
resourcesDiv.innerHTML = '<h5>Recommended Resources:</h5>';
path.resources.forEach(resource => {
const resourceItem = document.createElement('div');
resourceItem.className = 'resource-item';
resourceItem.innerHTML = `
<div class="resource-info">
<h6>${resource.title || 'Resource'}</h6>
<p>${resource.description || ''}</p>
</div>
<button class="btn btn-primary start-learning-btn" onclick="window.open('${resource.url}', '_blank')">
Start Learning
</button>
`;
resourcesDiv.appendChild(resourceItem);
});
pathItem.appendChild(resourcesDiv);
}
learningPathContent.appendChild(pathItem);
});
}
}
// --- Load Performance Charts ---
async function loadPerformanceCharts(enrollmentNo) {
if (!enrollmentNo) return;
if (dsaPerformanceChart) {
dsaPerformanceChart.destroy();
dsaPerformanceChart = null;
}
try {
const response = await fetch(`/api/dashboard/metrics/${enrollmentNo}`);
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const dashboardData = await response.json();
const dsaCtx = document.getElementById('dsa-performance-chart');
if (dsaCtx && dashboardData.coding_profiles?.leetcode?.score !== undefined) {
dsaPerformanceChart = new Chart(dsaCtx, {
type: 'bar',
data: {
labels: ['DSA Score'],
datasets: [{
label: 'DSA Performance (0-10)',
data: [dashboardData.coding_profiles.leetcode.score],
backgroundColor: 'rgba(75, 192, 192, 0.2)',
borderColor: 'rgb(75, 192, 192)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
max: 10
}
}
}
});
}
} catch (error) {
console.error('Error loading performance charts:', error);
}
}
// --- Load Dashboard Metrics ---
async function loadDashboardData() {
const totalStudentsEl = document.getElementById('total-students-count');
const reportsGeneratedEl = document.getElementById('reports-generated-count');
const jobAnalysesEl = document.getElementById('job-analyses-count');
try {
const studentsResponse = await fetch('/api/students');
const students = await studentsResponse.json();
if (totalStudentsEl) totalStudentsEl.textContent = students.length;
if (reportsGeneratedEl) reportsGeneratedEl.textContent = '0';
if (jobAnalysesEl) jobAnalysesEl.textContent = '0';
} catch (error) {
console.error('Error loading dashboard metrics:', error);
}
}
// --- Helper Functions ---
function appendMessage(text, sender, isLoading = false, useMarkdown = false) {
const messageWrapper = document.createElement('div');
messageWrapper.classList.add('chat-message', `${sender}-message`);
const messageP = document.createElement('p');
if (useMarkdown) {
messageP.innerHTML = convertMarkdownToHTML(text);
} else {
messageP.textContent = text;
}
if (isLoading) {
messageP.classList.add('loading');
}
messageWrapper.appendChild(messageP);
chatHistory.appendChild(messageWrapper);
chatHistory.scrollTop = chatHistory.scrollHeight;
}
// --- Keep existing display functions for backward compatibility ---
function displayReport(report) {
console.warn('displayReport is deprecated. Use displayNewReport instead.');
displayNewReport(report);
}
// --- Display Job Analysis Results ---
function displayJobAnalysis(data) {
console.log("Job Analysis Data:", data);
const strengthsList = document.getElementById('job-strengths-list');
if (strengthsList && data.your_core_strengths_for_this_role) {
strengthsList.innerHTML = '';
data.your_core_strengths_for_this_role.forEach(strength => {
const li = document.createElement('li');
const text = typeof strength === 'string' ? strength :
(strength.strength || strength.text || strength.description || JSON.stringify(strength));
li.textContent = text;
strengthsList.appendChild(li);
});
}
const weaknessesList = document.getElementById('job-weaknesses-list');
if (weaknessesList && data.strategic_areas_for_growth) {
weaknessesList.innerHTML = '';
data.strategic_areas_for_growth.forEach(area => {
const li = document.createElement('li');
const text = typeof area === 'string' ? area : (area.area_to_develop || JSON.stringify(area));
li.textContent = text;
weaknessesList.appendChild(li);
});
}
const enhancementsList = document.getElementById('job-enhancements-list');
if (enhancementsList && data.strategic_overview) {
enhancementsList.innerHTML = '';
if (data.strategic_overview.summary) {
const summaryItem = document.createElement('div');
summaryItem.className = 'enhancement-item';
summaryItem.innerHTML = `<strong>Summary:</strong> ${data.strategic_overview.summary}`;
enhancementsList.appendChild(summaryItem);
}
if (data.strategic_overview.your_key_opportunity) {
const opportunityItem = document.createElement('div');
opportunityItem.className = 'enhancement-item';
opportunityItem.innerHTML = `<strong>Key Opportunity:</strong> ${data.strategic_overview.your_key_opportunity}`;
enhancementsList.appendChild(opportunityItem);
}
}
const youtubeContainer = document.getElementById('job-youtube-recommendations');
if (youtubeContainer && data.video_recommendations) {
youtubeContainer.innerHTML = '';
data.video_recommendations.forEach(topic => {
// Create Topic Section
const topicSection = document.createElement('div');
topicSection.className = 'video-topic-section';
const topicTitle = document.createElement('h4');
topicTitle.className = 'video-topic-title';
topicTitle.textContent = topic.topic || 'Recommended Topic';
topicSection.appendChild(topicTitle);
const topicReason = document.createElement('p');
topicReason.className = 'video-topic-reason';
topicReason.textContent = topic.reason || '';
topicSection.appendChild(topicReason);
// Video Grid for this topic
const videoGrid = document.createElement('div');
videoGrid.className = 'video-grid';
if (topic.videos && topic.videos.length > 0) {
topic.videos.forEach(video => {
const videoCard = document.createElement('div');
videoCard.className = 'youtube-card';
const embedUrl = video.embed_url || video.url.replace('watch?v=', 'embed/');
const title = video.title || 'Video Tutorial';
videoCard.innerHTML = `
<div class="video-wrapper">
<iframe
src="${embedUrl}"
title="${title}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
<div class="video-info">
<h5>${title}</h5>
<p>${video.reason || ''}</p>
</div>
`;
videoGrid.appendChild(videoCard);
});
} else {
videoGrid.innerHTML = '<p>No videos found for this topic.</p>';
}
topicSection.appendChild(videoGrid);
youtubeContainer.appendChild(topicSection);
});
}
}
// --- Dashboard Logic ---
const refreshDashboardBtn = document.getElementById('refresh-dashboard-btn');
if (refreshDashboardBtn) {
refreshDashboardBtn.addEventListener('click', loadDashboardData);
}
function loadDashboardData() {
console.log("Loading dashboard data...");
fetch('/api/dashboard/students')
.then(response => response.json())
.then(data => {
if (data.error) {
console.error("Error loading dashboard:", data.error);
return;
}
// Update Summary Cards
const totalStudentsEl = document.getElementById('total-students-count');
if (totalStudentsEl) totalStudentsEl.textContent = data.total_students;
const classAvgEl = document.getElementById('class-avg-cgpa');
if (classAvgEl) classAvgEl.textContent = data.class_average_cgpa;
// Count reports ready
const reportsReady = data.students.filter(s => s.report_status === 'Generated').length;
const reportsReadyEl = document.getElementById('reports-ready-count');
if (reportsReadyEl) reportsReadyEl.textContent = reportsReady;
// Populate Student Table
const tableBody = document.getElementById('student-table-body');
if (tableBody) {
tableBody.innerHTML = '';
data.students.forEach(student => {
const row = document.createElement('tr');
// Status Badges
const reportBadgeClass = student.report_status === 'Generated' ? 'badge-success' : 'badge-pending';
const cgpaClass = parseFloat(student.cgpa) >= 8.0 ? 'text-success' : (parseFloat(student.cgpa) >= 6.0 ? 'text-warning' : 'text-danger');
row.innerHTML = `
<td><div class="student-name-cell">${student.name}</div></td>
<td>${student.enrollment_no}</td>
<td class="${cgpaClass}"><strong>${student.cgpa}</strong></td>
<td>
<div class="skills-tags">
${student.key_skills.map(skill => `<span class="skill-tag-mini">${skill}</span>`).join('')}
</div>
</td>
<td><span class="badge ${reportBadgeClass}">${student.report_status}</span></td>
<td><span class="badge badge-neutral">${student.resume_count} Resumes</span></td>
<td>
<button class="btn btn-sm btn-outline-primary view-profile-btn" data-enrollment="${student.enrollment_no}">
View Profile
</button>
</td>
`;
tableBody.appendChild(row);
});
// Add Event Listeners to "View Profile" buttons
document.querySelectorAll('.view-profile-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const enrollmentNo = e.target.getAttribute('data-enrollment');
// Switch to Reports tab
const reportsTab = document.querySelector('.nav-link[href="#reports"]');
if (reportsTab) reportsTab.click();
// Select student in dropdown
if (studentSelector) {
studentSelector.value = enrollmentNo;
// Trigger change event to load student data
studentSelector.dispatchEvent(new Event('change'));
}
});
});
}
})
.catch(err => console.error("Failed to load dashboard data:", err));
}
// Initial load if dashboard is active
if (document.querySelector('#dashboard.active')) {
loadDashboardData();
}
}); // End of DOMContentLoaded