Biggest Gainer
${biggestMover.name} (+${biggestMover.stats.change})
Biggest Drop
${biggestLoser.name} (${biggestLoser.stats.change})
`;
// Main Table
const dashboardTableBody = document.getElementById('dashboard-table-body');
dashboardTableBody.innerHTML = processedKeywords.map(k => {
const changeHtml = k.stats.change === 0 ? `
0` :
k.stats.change > 0 ? `
↑ ${k.stats.change}` :
`
↓ ${Math.abs(k.stats.change)}`;
return `
| ${k.name} |
${k.stats.currentPos} |
${changeHtml} |
${k.stats.bestPos} |
${k.stats.lastChecked} |
`;
}).join('');
// Charts
const historySelect = document.getElementById('history-keyword-select');
historySelect.innerHTML = keywords.map(k => `
`).join('');
renderHistoryChart(parseInt(historySelect.value));
historySelect.onchange = (e) => renderHistoryChart(parseInt(e.target.value));
const sortedByPos = [...processedKeywords].sort((a,b) => a.stats.currentPos - b.stats.currentPos);
if (positionsChart) positionsChart.destroy();
positionsChart = new Chart(document.getElementById('positions-chart').getContext('2d'), {
type: 'bar',
data: { labels: sortedByPos.map(k => k.name), datasets: [{ label: 'Position', data: sortedByPos.map(k => k.stats.currentPos), backgroundColor: '#a78bfa' }] },
options: { indexAxis: 'y', reversed: true, plugins: { legend: { display: false } }, scales: { x: { reverse: true, beginAtZero: true } } }
});
};
const renderHistoryChart = (keywordId) => {
const keyword = keywords.find(k => k.id === keywordId);
if (!keyword) return;
if (historyChart) historyChart.destroy();
historyChart = new Chart(document.getElementById('history-chart').getContext('2d'), {
type: 'line',
data: {
datasets: [{
label: 'Position',
data: keyword.history.map(h => ({ x: h.date, y: h.pos })),
borderColor: '#3b82f6',
tension: 0.1
}]
},
options: {
scales: {
y: { reverse: true, beginAtZero: true },
x: { type: 'time', time: { unit: 'day' } }
},
plugins: { legend: { display: false } }
}
});
};
const renderConfigTable = () => {
const configTableBody = document.getElementById('config-table-body');
configTableBody.innerHTML = keywords.map(k => `
| ${k.name} |
|
`).join('');
};
// --- EVENT HANDLERS ---
const handleAddUpdate = () => {
const nameInput = document.getElementById('new-keyword-name');
const posInput = document.getElementById('new-keyword-position');
const name = nameInput.value.trim().toLowerCase();
const position = parseInt(posInput.value);
if (!name || !position) { alert('Please enter both keyword and position.'); return; }
const existingKeyword = keywords.find(k => k.name === name);
const today = new Date().toISOString().split('T')[0];
if (existingKeyword) { // Update
existingKeyword.history.push({ date: today, pos: position });
} else { // Add new
const newId = keywords.length > 0 ? Math.max(...keywords.map(k => k.id)) + 1 : 1;
keywords.push({ id: newId, name, history: [{ date: today, pos: position }] });
}
nameInput.value = '';
posInput.value = '';
renderConfigTable();
};
const handleDelete = (e) => {
if(e.target.classList.contains('delete-btn')) {
const id = parseInt(e.target.closest('tr').dataset.id);
keywords = keywords.filter(k => k.id !== id);
renderConfigTable();
}
};
const generatePDF = () => {
const pdfContent = document.getElementById('pdf-content');
html2canvas(pdfContent, { scale: 2, useCORS: true }).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF({ orientation: 'l', unit: 'mm', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const imgWidth = pdfWidth - 20;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
pdf.setFontSize(22).setFont('helvetica', 'bold').text('SERP Position Report', pdfWidth / 2, 15, { align: 'center' });
pdf.addImage(imgData, 'PNG', 10, 25, imgWidth, imgHeight);
pdf.save('serp-position-report.pdf');
});
};
// --- ATTACH LISTENERS ---
prevBtn.addEventListener('click', () => showTab('dashboard'));
nextBtn.addEventListener('click', () => showTab('config'));
tabButtons.dashboard.addEventListener('click', () => showTab('dashboard'));
tabButtons.config.addEventListener('click', () => showTab('config'));
document.getElementById('add-update-btn').addEventListener('click', handleAddUpdate);
document.getElementById('config-table-body').addEventListener('click', handleDelete);
document.getElementById('download-pdf-btn').addEventListener('click', generatePDF);
// --- INITIAL SETUP ---
showTab('dashboard');
});