Firmware Update Log Template

Firmware Update Log Template

Device & Project Metadata

Log New Update

Log History (Editable)

# Version Date Changes/Notes Tester Status Act.

Review the final log and summary statistics.

Summary Statistics

Full Log Preview

Generate the log to see the export preview.

Latest Version: ${escapeHTML(stats.currentVersion)}

Total Updates: ${stats.count}

Success Rate: ${stats.successRate}%

` : `

No data available for statistics.

`}

2. Full Update History

${tableRowsHTML.length > 0 ? tableRowsHTML : ''}
# Version Date Changes/Notes Tester Status
No updates logged.
`; }; const downloadTxt = () => { const data = getReportData(); const stats = data.stats; let content = `FIRMWARE UPDATE LOG: ${data.setup.project.toUpperCase()}\n`; content += `========================================================\n`; content += `Model: ${data.setup.model}\nTeam: ${data.setup.devTeam}\n`; content += `Initial Version: ${data.setup.initialVersion}\n\n`; if (stats) { content += "SUMMARY STATISTICS\n"; content += `Total Updates Logged: ${stats.count}\nLatest Version: ${stats.currentVersion}\nSuccess Rate: ${stats.successRate}%\n\n`; } content += "UPDATE HISTORY\n"; content += "--------------------------------------------------------\n"; content += " # | Version | Date\t\t| Tester\t| Status\t| Changes/Notes\n"; content += "--------------------------------------------------------\n"; data.log.forEach((log, index) => { content += `${(index + 1).toString().padStart(2)} | ${log.newVersion.padEnd(7)} | ${log.date}\t| ${log.tester.padEnd(10)}\t| ${log.status.padEnd(7)}\t| ${log.changes}\n`; }); const blob = new Blob([content], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `${data.setup.project.replace(/ /g, '_')}_firmware_log.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = () => { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined' || typeof window.jspdf.autoTable === 'undefined') { alert('Error: jsPDF libraries not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('l', 'mm', 'a4'); // Landscape for wide table const data = getReportData(); const stats = data.stats; const margin = 10; const pageWidth = doc.internal.pageSize.getWidth(); let yPos = 15; // --- PDF Helper --- const addSectionTitle = (title) => { doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 188, 212); // Teal doc.text(title, margin, yPos); doc.setDrawColor(224, 224, 224); doc.line(margin, yPos + 1, pageWidth - margin, yPos + 1); yPos += 8; }; // 1. Header doc.setFontSize(20); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(`Firmware Update Log: ${data.setup.project}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 8; doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.setTextColor(108, 117, 125); doc.text(`Model: ${data.setup.model} | Team: ${data.setup.devTeam} | Initial Version: ${data.setup.initialVersion}`, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. Summary Statistics if (stats) { addSectionTitle("1. Summary Statistics"); const statsHead = [['Initial Version', 'Latest Version', 'Total Updates', 'Passed Tests', 'Success Rate (%)']]; const statsBody = [[ data.setup.initialVersion, stats.currentVersion, stats.count, stats.passed, `${stats.successRate}%` ]]; doc.autoTable({ startY: yPos, head: statsHead, body: statsBody, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: [52, 73, 94], halign: 'center' }, headStyles: { fillColor: [0, 188, 212], textColor: [255, 255, 255] }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; } // 3. Raw Log Table addSectionTitle("2. Full Update History"); const logHead = [['#', 'Version', 'Date', 'Changes/Notes', 'Tester', 'Status']]; const logBody = data.log.map((log, index) => [ index + 1, log.newVersion, log.date, log.changes, log.tester, log.status ]); doc.autoTable({ startY: yPos, head: logHead, body: logBody, theme: 'grid', styles: { fontSize: 9, cellPadding: 2, textColor: [52, 73, 94], valign: 'top' }, headStyles: { fillColor: [230, 230, 230], textColor: [44, 62, 80] }, columnStyles: { 0: { cellWidth: 8, halign: 'center' }, 3: { cellWidth: 80 } }, // Give notes more space margin: { left: margin, right: margin } }); doc.save(`${data.setup.project.replace(/ /g, '_')}_firmware_log.pdf`); }; // --- Event Listeners --- // Global Navigation tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 2 Actions (Add, Remove, Edit) newUpdateInputs.addBtn.addEventListener('click', addUpdate); updatesTbody.addEventListener('click', (e) => { if (e.target.dataset.removeId) { removeUpdate(parseInt(e.target.dataset.removeId)); } }); updatesTbody.addEventListener('blur', (e) => { if (e.target.tagName === 'TD' && e.target.isContentEditable) { const id = parseInt(e.target.dataset.id); const field = e.target.dataset.field; updateUpdate(id, field, e.target.textContent); } }, true); // Tab 3 Actions refreshBtn.addEventListener('click', calculateAndRenderStats); downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- newUpdateInputs.date.value = new Date().toISOString().substring(0, 10); showTab(1); // Set initial state });
Scroll to Top