`).join('');
form.appendChild(panel);
});
const reportTab = document.createElement('button');
reportTab.type = 'button';
reportTab.className = 'tab-btn flex-1 py-3 px-4 font-semibold text-center text-sm sm:text-base';
reportTab.dataset.index = assessmentData.length;
reportTab.innerHTML = `${assessmentData.length + 1}. Report`;
tabContainer.appendChild(reportTab);
};
const showTab = (index) => {
currentTabIndex = index;
const isReportTab = index === assessmentData.length;
// Toggle visibility
form.classList.toggle('hidden', isReportTab);
reportContent.classList.toggle('hidden', !isReportTab);
document.querySelectorAll('[id^="panel-"]').forEach((panel, i) => panel.classList.toggle('hidden', i !== index));
// Update tab buttons
tabContainer.querySelectorAll('.tab-btn').forEach((btn, i) => btn.classList.toggle('active', i === index));
// Update nav buttons
prevBtn.disabled = index === 0;
nextBtn.disabled = isReportTab;
nextBtn.textContent = index === assessmentData.length - 1 ? 'View Report' : 'Next';
if (isReportTab) {
calculateAndRenderReport();
}
};
const updateProgress = () => {
const answeredQuestions = form.querySelectorAll('input[type="radio"]:checked').length;
const progress = totalQuestions > 0 ? Math.round((answeredQuestions / totalQuestions) * 100) : 0;
progressBar.style.width = `${progress}%`;
progressText.textContent = `${progress}%`;
};
// --- CALCULATION & REPORTING ---
const calculateAndRenderReport = () => {
const answers = new FormData(form);
let sectionResults = assessmentData.map(section => ({
title: section.title, compliant: 0, totalApplicable: 0, nonCompliantItems: []
}));
answers.forEach((value, key) => {
const sectionIndex = assessmentData.findIndex(s => s.questions.some(q => q.id === key));
if (sectionIndex > -1) {
if (value !== 'na') {
sectionResults[sectionIndex].totalApplicable++;
if (value === 'yes') {
sectionResults[sectionIndex].compliant++;
} else {
const questionText = assessmentData[sectionIndex].questions.find(q => q.id === key).text;
sectionResults[sectionIndex].nonCompliantItems.push(questionText);
}
}
}
});
const totalCompliant = sectionResults.reduce((acc, r) => acc + r.compliant, 0);
const totalApplicable = sectionResults.reduce((acc, r) => acc + r.totalApplicable, 0);
const overallScore = totalApplicable > 0 ? Math.round((totalCompliant / totalApplicable) * 100) : 100;
// Render summary
document.getElementById('overall-score-display').textContent = `${overallScore}%`;
const statusEl = document.getElementById('compliance-status');
const statusContainer = document.getElementById('compliance-status-container');
let status, colorClass;
if (overallScore === 100) { status = 'COMPLIANT'; colorClass = 'bg-green-100 text-green-800'; }
else if (overallScore >= 75) { status = 'NEEDS IMPROVEMENT'; colorClass = 'bg-yellow-100 text-yellow-800'; }
else { status = 'NON-COMPLIANT'; colorClass = 'bg-red-100 text-red-800'; }
statusEl.textContent = status;
statusContainer.className = `p-3 rounded-lg inline-block ${colorClass}`;
// Render remediation items
const remediationContainer = document.getElementById('remediation-items');
const allNonCompliant = sectionResults.flatMap(r => r.nonCompliantItems);
if(allNonCompliant.length > 0) {
remediationContainer.innerHTML = allNonCompliant.map(item => `
${item}
`).join('');
} else {
remediationContainer.innerHTML = `Excellent! No non-compliant items found.
`;
}
// Render chart
const sectionScores = sectionResults.map(r => r.totalApplicable > 0 ? (r.compliant / r.totalApplicable) * 100 : 100);
if (sectionScoreChart) sectionScoreChart.destroy();
sectionScoreChart = new Chart(document.getElementById('section-score-chart').getContext('2d'), {
type: 'bar',
data: { labels: sectionResults.map(r => r.title), datasets: [{ label: 'Compliance %', data: sectionScores, backgroundColor: '#60a5fa' }] },
options: { scales: { y: { beginAtZero: true, max: 100 } }, plugins: { legend: { display: false } } }
});
};
// --- EVENT HANDLERS ---
const handleNavClick = (direction) => {
const newIndex = direction === 'next' ? currentTabIndex + 1 : currentTabIndex - 1;
if (newIndex >= 0 && newIndex <= assessmentData.length) {
showTab(newIndex);
}
};
const generatePDF = () => {
const pdfContent = document.getElementById('pdf-content');
const pdfBtnContainer = document.getElementById('pdf-button-container');
if (!pdfContent || !pdfBtnContainer) return;
pdfBtnContainer.style.display = 'none';
html2canvas(pdfContent, { scale: 2, useCORS: true }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgWidth = pdfWidth - 20;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
pdf.setFontSize(22);
pdf.setFont('helvetica', 'bold');
pdf.text('PCI DSS Compliance Report', pdfWidth / 2, 15, { align: 'center' });
pdf.addImage(imgData, 'PNG', 10, 25, imgWidth, imgHeight);
pdf.save('pci-compliance-report.pdf');
pdfBtnContainer.style.display = 'block';
}).catch(err => {
console.error("PDF generation error:", err);
pdfBtnContainer.style.display = 'block';
});
};
// --- ATTACH LISTENERS ---
nextBtn.addEventListener('click', () => handleNavClick('next'));
prevBtn.addEventListener('click', () => handleNavClick('prev'));
tabContainer.addEventListener('click', (e) => {
if(e.target.closest('.tab-btn')) showTab(parseInt(e.target.closest('.tab-btn').dataset.index));
});
form.addEventListener('change', updateProgress);
downloadPdfBtn.addEventListener('click', generatePDF);
// --- INITIALIZATION ---
renderTabsAndPanels();
updateProgress();
});
