`;
document.getElementById('config-tab').innerHTML = content;
document.getElementById('data-config-input').value = JSON.stringify(dashboardData, null, 2);
document.getElementById('update-dashboard-btn').addEventListener('click', handleUpdateDashboard);
};
// --- CHART RENDERING (on tab switch) ---
const renderChartsForTab = (tabId) => {
destroyCharts();
if (tabId === 'overview') renderOverviewTab(); // Re-render overview to get its charts
if (tabId === 'financials') {
const revExpData = dashboardData.financials.revenueVsExpense;
chartInstances.financialsRevExp = new Chart(document.getElementById('financials-rev-exp-chart').getContext('2d'), {
type: 'line',
data: {
labels: revExpData.labels,
datasets: [
{ label: 'Revenue', data: revExpData.revenue, borderColor: 'rgba(59, 130, 246, 1)', tension: 0.1 },
{ label: 'Expenses', data: revExpData.expenses, borderColor: 'rgba(244, 63, 94, 1)', tension: 0.1 }
]
},
options: { responsive: true, maintainAspectRatio: true }
});
const profitData = dashboardData.financials.profitMargins;
chartInstances.financialsProfit = new Chart(document.getElementById('financials-profit-chart').getContext('2d'), {
type: 'pie',
data: {
labels: profitData.labels,
datasets: [{ data: profitData.data, backgroundColor: ['#3b82f6', '#16a34a', '#f97316', '#9333ea'] }]
},
options: { responsive: true, maintainAspectRatio: true }
});
}
if (tabId === 'sales') {
const pipelineData = dashboardData.sales.pipeline;
chartInstances.salesPipeline = new Chart(document.getElementById('sales-pipeline-chart').getContext('2d'), {
type: 'bar', data: { labels: pipelineData.labels, datasets: [{ label: 'Count', data: pipelineData.data, backgroundColor: 'rgba(34, 197, 94, 0.6)' }] },
options: { responsive: true, indexAxis: 'y', maintainAspectRatio: true }
});
const funnelData = dashboardData.sales.conversionFunnel;
chartInstances.salesFunnel = new Chart(document.getElementById('sales-funnel-chart').getContext('2d'), {
type: 'funnel',
data: { labels: funnelData.labels, datasets: [{ data: funnelData.data }] },
options: { responsive: true, maintainAspectRatio: true, sort: 'desc', }
});
}
if (tabId === 'hr') {
const headcountData = dashboardData.hr.headcount;
chartInstances.hrHeadcount = new Chart(document.getElementById('hr-headcount-chart').getContext('2d'), {
type: 'doughnut', data: { labels: headcountData.labels, datasets: [{ data: headcountData.data, backgroundColor: ['#3b82f6', '#16a34a', '#f97316', '#9333ea', '#64748b'] }] },
options: { responsive: true, maintainAspectRatio: true }
});
const turnoverData = dashboardData.hr.turnover;
chartInstances.hrTurnover = new Chart(document.getElementById('hr-turnover-chart').getContext('2d'), {
type: 'line', data: { labels: turnoverData.labels, datasets: [{ label: 'Turnover Rate (%)', data: turnoverData.data, borderColor: 'rgba(244, 63, 94, 1)', tension: 0.1, fill: true, backgroundColor: 'rgba(244, 63, 94, 0.2)' }] },
options: { responsive: true, maintainAspectRatio: true }
});
}
if (tabId === 'operations') {
const outputData = dashboardData.operations.output;
chartInstances.opsOutput = new Chart(document.getElementById('ops-output-chart').getContext('2d'), {
type: 'bar',
data: {
labels: outputData.labels,
datasets: [
{ label: 'Actual', data: outputData.actual, backgroundColor: 'rgba(59, 130, 246, 0.6)' },
{ label: 'Target', data: outputData.target, type: 'line', borderColor: 'rgba(244, 63, 94, 1)', fill: false }
]
},
options: { responsive: true, maintainAspectRatio: true }
});
const oeeData = dashboardData.operations.oee;
chartInstances.opsOee = new Chart(document.getElementById('ops-oee-chart').getContext('2d'), {
type: 'radar', data: { labels: oeeData.labels, datasets: [{ label: 'OEE (%)', data: oeeData.data, backgroundColor: 'rgba(34, 197, 94, 0.2)', borderColor: 'rgba(34, 197, 94, 1)' }] },
options: { responsive: true, maintainAspectRatio: true, scales: { r: { beginAtZero: true, max: 100 } } }
});
}
};
// --- NAVIGATION & EVENT HANDLING ---
const showTab = (tabIndex) => {
currentTab = tabIndex;
const tabId = tabs[tabIndex];
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.add('hidden'));
document.getElementById(`${tabId}-tab`).classList.remove('hidden');
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelector(`.tab-btn[data-tab='${tabId}']`).classList.add('active');
prevBtn.style.visibility = (currentTab === 0) ? 'hidden' : 'visible';
nextBtn.style.visibility = (currentTab >= tabs.length - 2) ? 'hidden' : 'visible'; // Hide on operations and config
downloadPdfBtn.style.visibility = (tabId === 'config') ? 'hidden' : 'visible';
renderChartsForTab(tabId);
};
tabNavigation.addEventListener('click', (e) => {
if (e.target.matches('.tab-btn')) {
const tabId = e.target.dataset.tab;
const tabIndex = tabs.indexOf(tabId);
showTab(tabIndex);
}
});
nextBtn.addEventListener('click', () => {
if (currentTab < tabs.length - 1) showTab(currentTab + 1);
});
prevBtn.addEventListener('click', () => {
if (currentTab > 0) showTab(currentTab - 1);
});
const handleUpdateDashboard = () => {
const configInput = document.getElementById('data-config-input');
const errorDiv = document.getElementById('json-error');
const errorMessage = document.getElementById('json-error-message');
try {
const newData = JSON.parse(configInput.value);
dashboardData = newData;
errorDiv.classList.add('hidden');
renderAllTabs(); // Re-render content for all tabs
alert('Dashboard updated successfully!');
showTab(0);
} catch (error) {
errorMessage.textContent = 'Invalid JSON format. ' + error.message;
errorDiv.classList.remove('hidden');
}
};
// --- PDF GENERATION ---
downloadPdfBtn.addEventListener('click', () => {
const { jsPDF } = window.jspdf;
const pdfContainer = document.getElementById('pdf-content-container');
const activeTabId = tabs[currentTab];
const activeTabElement = document.getElementById(`${activeTabId}-tab`);
if (!pdfContainer || !activeTabElement) return;
const clone = activeTabElement.cloneNode(true);
pdfContainer.innerHTML = '';
pdfContainer.appendChild(clone);
html2canvas(pdfContainer, { scale: 2, useCORS: true, logging: false, width: pdfContainer.scrollWidth, height: pdfContainer.scrollHeight })
.then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgWidth = canvas.width;
const imgHeight = canvas.height;
const ratio = imgWidth / imgHeight;
let finalImgHeight = pdfWidth / ratio;
let heightLeft = finalImgHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, finalImgHeight);
heightLeft -= pdfHeight;
while (heightLeft > 0) {
position = heightLeft - finalImgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, finalImgHeight);
heightLeft -= pdfHeight;
}
pdf.save(`Management_Cockpit_${activeTabId}.pdf`);
pdfContainer.innerHTML = '';
}).catch(err => {
console.error("PDF generation failed:", err);
alert("Sorry, there was an error creating the PDF.");
});
});
// --- INITIALIZATION ---
createTabs();
renderAllTabs();
showTab(0);
});
