Vendor Comparison
Top Performer
Needs Improvement
Detailed Vendor Data
| Vendor Name | On-Time Delivery | Quality Score | Cost Variance | Response Time |
|---|
${sortedVendors[0].name}
`; getElem('needs-improvement').innerHTML = `${sortedVendors[sortedVendors.length-1].name}
`; downloadSection.classList.remove('hidden'); showTab(1); }; generateBtn.addEventListener('click', generateDashboard); // --- PDF DOWNLOAD --- const downloadPDF = () => { if (!currentData || !performanceChart) return; const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); const btnText = getElem('pdf-btn-text'); const btnSpinner = getElem('pdf-btn-spinner'); btnText.classList.add('hidden'); btnSpinner.classList.remove('hidden'); downloadPdfBtn.disabled = true; try { const pageWidth = pdf.internal.pageSize.getWidth(); const margin = 40; let y = margin; pdf.setFont('helvetica', 'bold'); pdf.setFontSize(22); pdf.text("Vendor Performance Review", pageWidth / 2, y, { align: 'center' }); y += 15; pdf.setFont('helvetica', 'normal'); pdf.setFontSize(10); pdf.text(`Report Date: ${today.toLocaleDateString('en-US')}`, pageWidth / 2, y, { align: 'center' }); y += 30; const chartImage = performanceChart.toBase64Image('image/png', 1.0); const chartStartY = y + 15; const chartHeight = 200; const chartEndY = chartStartY + chartHeight; // --- Left Column: Chart --- pdf.setFont('helvetica', 'bold'); pdf.setFontSize(14); pdf.text("Performance Chart", margin, y); pdf.addImage(chartImage, 'PNG', margin, chartStartY, (pageWidth/2) - margin*1.5, chartHeight); // --- Right Column: KPIs --- let kpiTableEndY = 0; // This will be set by the hook or fallback pdf.setFont('helvetica', 'bold'); pdf.text("Key Performance Indicators", (pageWidth/2), y); pdf.autoTable({ startY: chartStartY, startX: (pageWidth/2), theme: 'grid', head: [['Metric', 'Value']], body: [ ['Avg. On-Time Delivery', `${currentData.avgDelivery.toFixed(1)}%`], ['Avg. Quality Score', `${currentData.avgQuality.toFixed(1)}/100`], ['Overall Cost Variance', `${currentData.overallCostVar.toFixed(1)}%`], ], didDrawPage: function(data) { kpiTableEndY = data.cursor.y; } }); // If the hook didn't fire, use the finalY property as a fallback. if (kpiTableEndY === 0) { kpiTableEndY = pdf.autoTable.previous ? pdf.autoTable.previous.finalY : chartStartY; } // Determine the starting Y for the next element by finding which column was taller. y = Math.max(chartEndY, kpiTableEndY) + 30; const tableData = currentData.vendors.map(v => [v.name, `${v.onTimeDelivery.toFixed(1)}%`, `${v.qualityScore.toFixed(1)}/100`, `${v.costVariance.toFixed(1)}%`, `${v.avgResponseTime} hrs`]); pdf.autoTable({ startY: y, head: [['Vendor', 'On-Time Delivery', 'Quality Score', 'Cost Variance', 'Response Time']], body: tableData, theme: 'striped', headStyles: { fillColor: [29, 78, 216] }, }); pdf.save(`Vendor-Performance-Review.pdf`); } catch(e) { console.error("PDF Generation failed:", e); } finally { btnText.classList.remove('hidden'); btnSpinner.classList.remove('hidden'); downloadPdfBtn.disabled = false; } }; downloadPdfBtn.addEventListener('click', downloadPDF); initialize(); });