Technical SEO Dashboard

Technical SEO Dashboard

Monitor website health, track performance, and identify critical SEO issues.

Core Web Vitals Trend (LCP)

HTTP Status Codes

Top SEO Issues

${fixedIssues}

`; }; const renderCoreVitalsChart = () => { const ctx = document.getElementById('coreVitalsChart').getContext('2d'); if (coreVitalsChart) coreVitalsChart.destroy(); coreVitalsChart = new Chart(ctx, { type: 'line', data: { labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"], datasets: [{ label: 'LCP (s)', data: [2.8, 2.6, 2.7, 2.5, 2.4, 2.2, 2.1], borderColor: '#0d9488', backgroundColor: 'rgba(13, 148, 136, 0.1)', fill: true, tension: 0.3 }] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { beginAtZero: false, reverse: true, title: { display: true, text: 'Seconds (lower is better)' } } } } }); }; const renderStatusCodesChart = () => { const ctx = document.getElementById('statusCodesChart').getContext('2d'); if (statusCodesChart) statusCodesChart.destroy(); statusCodesChart = new Chart(ctx, { type: 'bar', data: { labels: ['2xx (Success)', '3xx (Redirect)', '4xx (Client Err)', '5xx (Server Err)'], datasets: [{ label: 'Page Count', data: [1250, 85, seoIssues.filter(i => i.type.includes('404')).length, seoIssues.filter(i => i.type.includes('5xx')).length], backgroundColor: ['#34d399', '#60a5fa', '#fbbf24', '#f87171'], }] }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins: { legend: { display: false } } } }); }; const renderIssuesTable = () => { const container = document.getElementById('issues-table-container'); const sortedIssues = [...seoIssues].sort((a, b) => { const priorityOrder = { 'High': 1, 'Medium': 2, 'Low': 3 }; return priorityOrder[a.priority] - priorityOrder[b.priority]; }); let tableHtml = ``; sortedIssues.forEach(issue => { let priorityClass = issue.priority === 'High' ? 'text-red-600' : issue.priority === 'Medium' ? 'text-amber-600' : 'text-gray-500'; let statusClass = issue.status === 'Fixed' ? 'bg-green-100 text-green-800' : issue.status === 'In Progress' ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800'; tableHtml += ``; }); tableHtml += `
URL Issue Priority Status
${issue.url} ${issue.type} ${issue.priority} ${issue.status}
`; container.innerHTML = tableHtml; }; const renderIssueListEditor = () => { const container = document.getElementById('issue-list-editor').querySelector('.space-y-2'); container.innerHTML = seoIssues.map(issue => `

${issue.url}

${issue.type} | Priority: ${issue.priority} | Status: ${issue.status}

`).join(''); }; // --- EVENT HANDLERS --- document.getElementById('issue-form').addEventListener('submit', e => { e.preventDefault(); const id = document.getElementById('issue-id').value; const newIssue = { url: document.getElementById('issue-url').value, type: document.getElementById('issue-type').value, priority: document.getElementById('issue-priority').value, status: document.getElementById('issue-status').value, }; if (id) { const index = seoIssues.findIndex(i => i.id == id); seoIssues[index] = { ...seoIssues[index], ...newIssue }; } else { newIssue.id = seoIssues.length > 0 ? Math.max(...seoIssues.map(i => i.id)) + 1 : 1; seoIssues.push(newIssue); } resetForm(); renderAll(); }); document.getElementById('issue-list-editor').addEventListener('click', e => { if (e.target.classList.contains('edit-btn')) { const id = e.target.dataset.id; const issue = seoIssues.find(i => i.id == id); if (issue) populateFormForEdit(issue); } if (e.target.classList.contains('delete-btn')) { const id = e.target.dataset.id; if (confirm('Are you sure you want to delete this issue?')) { seoIssues = seoIssues.filter(i => i.id != id); renderAll(); } } }); document.getElementById('cancel-edit-btn').addEventListener('click', resetForm); document.getElementById('download-pdf-btn').addEventListener('click', () => { const exportArea = document.getElementById('dashboard-export-area'); const mainTitleEl = document.querySelector('#seo-tool-container h1'); if (!exportArea || !mainTitleEl) return; const btn = document.getElementById('download-pdf-btn'); const originalBtnText = btn.innerHTML; btn.disabled = true; btn.innerHTML = `Processing...`; html2canvas(exportArea, { scale: 2, useCORS: true, windowWidth: exportArea.scrollWidth, windowHeight: exportArea.scrollHeight }) .then(canvas => { const { jsPDF } = window.jspdf; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' }); const pageMargin = 15; const pdfPageWidth = pdf.internal.pageSize.getWidth(); const pdfPageHeight = pdf.internal.pageSize.getHeight(); const pdfContentWidth = pdfPageWidth - (2 * pageMargin); const canvasAspectRatio = canvas.width / canvas.height; const pdfImgHeight = pdfContentWidth / canvasAspectRatio; let yPos = pageMargin; pdf.setFontSize(18).setFont('helvetica', 'bold'); pdf.text(mainTitleEl.innerText, pdfPageWidth / 2, yPos, { align: 'center' }); yPos += 12; let heightLeft = pdfImgHeight; let positionOnCanvas = 0; pdf.addImage(imgData, 'PNG', pageMargin, yPos, pdfContentWidth, pdfImgHeight); heightLeft -= (pdfPageHeight - yPos - pageMargin); while (heightLeft > 0) { positionOnCanvas -= (pdfPageHeight - (2 * pageMargin)); pdf.addPage(); pdf.addImage(imgData, 'PNG', pageMargin, positionOnCanvas + pageMargin, pdfContentWidth, pdfImgHeight); heightLeft -= (pdfPageHeight - (2 * pageMargin)); } pdf.save(`technical-seo-dashboard-${new Date().toISOString().slice(0,10)}.pdf`); }) .catch(err => { console.error("PDF generation failed:", err); alert("Error generating PDF."); }) .finally(() => { btn.disabled = false; btn.innerHTML = originalBtnText; }); }); // --- HELPER FUNCTIONS --- function populateFormForEdit(issue) { document.getElementById('issue-id').value = issue.id; document.getElementById('issue-url').value = issue.url; document.getElementById('issue-type').value = issue.type; document.getElementById('issue-priority').value = issue.priority; document.getElementById('issue-status').value = issue.status; document.getElementById('issue-form').querySelector('button[type="submit"]').textContent = 'Update Issue'; document.getElementById('cancel-edit-btn').classList.remove('hidden'); document.getElementById('issue-form').scrollIntoView({ behavior: 'smooth' }); } function resetForm() { document.getElementById('issue-form').reset(); document.getElementById('issue-id').value = ''; document.getElementById('issue-form').querySelector('button[type="submit"]').textContent = 'Add Issue'; document.getElementById('cancel-edit-btn').classList.add('hidden'); } // --- INITIALIZATION --- const switchTab = (tabName) => { const tabDashboardBtn = document.getElementById('tab-dashboard-btn'); const tabConfigBtn = document.getElementById('tab-config-btn'); const tabDashboardContent = document.getElementById('tab-dashboard-content'); const tabConfigContent = document.getElementById('tab-config-content'); const prevTabBtn = document.getElementById('prev-tab-btn'); const nextTabBtn = document.getElementById('next-tab-btn'); if (tabName === 'dashboard') { tabDashboardBtn.classList.add('active'); tabConfigBtn.classList.remove('active'); tabDashboardContent.classList.remove('hidden'); tabConfigContent.classList.add('hidden'); prevTabBtn.disabled = true; nextTabBtn.disabled = false; } else { tabDashboardBtn.classList.remove('active'); tabConfigBtn.classList.add('active'); tabDashboardContent.classList.add('hidden'); tabConfigContent.classList.remove('hidden'); prevTabBtn.disabled = false; nextTabBtn.disabled = true; } }; document.getElementById('tab-dashboard-btn').addEventListener('click', () => switchTab('dashboard')); document.getElementById('tab-config-btn').addEventListener('click', () => switchTab('config')); document.getElementById('prev-tab-btn').addEventListener('click', () => switchTab('dashboard')); document.getElementById('next-tab-btn').addEventListener('click', () => switchTab('config')); switchTab('dashboard'); renderAll(); });
Scroll to Top