`).join('');
}
function renderLoginChart(data) {
const ctx = document.getElementById('loginChart').getContext('2d');
const successful = {}; const failed = {};
data.forEach(e => {
const hour = new Date(e.timestamp.setMinutes(0, 0, 0));
if (e.type === 'Successful Login') successful[hour] = (successful[hour] || 0) + 1;
if (e.type === 'Failed Login') failed[hour] = (failed[hour] || 0) + 1;
});
if (loginChart) loginChart.destroy();
loginChart = new Chart(ctx, {
type: 'bar',
data: {
datasets: [
{ label: 'Successful', data: successful, backgroundColor: '#22c55e' },
{ label: 'Failed', data: failed, backgroundColor: '#ef4444' }
]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: { x: { type: 'time', time: { unit: 'hour' }, stacked: true }, y: { stacked: true, beginAtZero: true } }
}
});
}
function renderMap(data) {
const mapContainer = document.getElementById('mapContainer');
// Clear old dots
mapContainer.querySelectorAll('circle').forEach(c => c.remove());
const uniqueLocations = [...new Set(data.map(e => e.location))];
uniqueLocations.forEach(loc => {
const coords = LOCATIONS[loc];
if (!coords) return;
const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
dot.setAttribute('cx', `${coords.x}%`);
dot.setAttribute('cy', `${coords.y}%`);
dot.setAttribute('r', '8');
dot.setAttribute('fill', 'rgba(220, 38, 38, 0.8)');
dot.setAttribute('stroke', 'white');
dot.setAttribute('stroke-width', '2');
mapContainer.querySelector('svg').appendChild(dot);
});
}
// --- PDF GENERATION ---
async function generatePdfReport() {
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'in', format: 'letter' });
// Populate PDF template
document.getElementById('pdf-report-date').textContent = new Date().toLocaleDateString();
document.getElementById('pdf-security-score').textContent = document.getElementById('securityScore').textContent;
document.getElementById('pdf-suspicious-events').textContent = document.getElementById('suspiciousEvents').textContent;
document.getElementById('pdf-total-logins').textContent = document.getElementById('totalLogins').textContent;
document.getElementById('pdf-failed-logins').textContent = document.getElementById('failedLogins').textContent;
document.getElementById('pdf-chart-image').src = loginChart.toBase64Image();
const mapCanvas = await html2canvas(document.getElementById('mapContainer'));
document.getElementById('pdf-map-image').src = mapCanvas.toDataURL('image/png');
const criticalEvents = eventLog.filter(e => e.status !== 'SECURE').slice(0, 5);
let tableHtml = '
| Timestamp | Event | IP | Location |
';
tableHtml += criticalEvents.map(e => `| ${e.timestamp.toLocaleString()} | ${e.type} | ${e.ip} | ${e.location} |
`).join('');
tableHtml += '
';
document.getElementById('pdf-data-table').innerHTML = tableHtml;
const reportEl = document.getElementById('pdf-report');
reportEl.style.display = 'block';
const canvas = await html2canvas(reportEl, { scale: 2 });
reportEl.style.display = 'none';
const imgData = canvas.toDataURL('image/png');
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (canvas.height * pdfWidth) / canvas.width;
pdf.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight);
pdf.save('Account-Security-Report.pdf');
}
});