Prototype Testing Log Generator

Prototype Testing Log Generator

Project Comet - Prototype Test Log

2025-10-28
Jane Doe (UX Researcher)
3

Observations Log

Task Severity Observation/Issue Suggested Fix

Session Setup

Add New Observation

Current Observations

No observations logged yet.

"; } appState.observations.forEach(obs => { const item = document.createElement("div"); item.className = "ptl-observation-item"; item.innerHTML = ` Task: ${obs.task} (${obs.severity}) `; observationsList.appendChild(item); }); } function renderDashboardTab() { projectTitleDisplay.textContent = appState.projectName; displayDate.textContent = appState.sessionDate; displayTester.textContent = appState.tester; totalObs.textContent = appState.observations.length; const severityFilter = filterSeveritySelect.value; const sortBy = sortBySelect.value; let filteredAndSortedObs = appState.observations.filter(obs => { return severityFilter === 'all' || obs.severity === severityFilter; }); // Sorting Logic if (sortBy === 'task') { filteredAndSortedObs.sort((a, b) => a.task.localeCompare(b.task)); } else if (sortBy === 'severity') { filteredAndSortedObs.sort((a, b) => severityMap[b.severity] - severityMap[a.severity]); } // Render Table tableBody.innerHTML = ""; if (filteredAndSortedObs.length === 0) { tableBody.innerHTML = "No observations match the current filter."; return; } filteredAndSortedObs.forEach(obs => { const tr = document.createElement("tr"); const severityClass = `tag-${obs.severity.toLowerCase().replace(' ', '')}`; tr.innerHTML = ` ${obs.task} ${obs.severity} ${obs.details} ${obs.fix || 'N/A'} `; tableBody.appendChild(tr); }); } // --- Event Handlers --- // Update Dashboard Button updateBtn.addEventListener("click", () => { appState.projectName = configProject.value; appState.sessionDate = configDate.value; appState.tester = configTester.value; saveState(); renderDashboardTab(); showTab(0); }); // Add Observation addObsBtn.addEventListener("click", () => { const task = obsTaskInput.value.trim(); const details = obsDetailsInput.value.trim(); const severity = obsSeveritySelect.value; const fix = obsFixInput.value.trim(); if (!task || !details) { alert("Please enter the Task and Observation details."); return; } const newObs = { id: Date.now(), task: task, details: details, severity: severity, fix: fix }; appState.observations.push(newObs); saveState(); renderConfigTab(); // Clear form obsTaskInput.value = ""; obsDetailsInput.value = ""; obsFixInput.value = ""; obsSeveritySelect.value = "Medium"; }); // Remove Observation (Event Delegation) observationsList.addEventListener("click", (e) => { if (e.target.classList.contains("ptl-remove-btn")) { const id = parseInt(e.target.dataset.id); appState.observations = appState.observations.filter(obs => obs.id !== id); saveState(); renderConfigTab(); } }); // Filters Change filterSeveritySelect.addEventListener("change", renderDashboardTab); sortBySelect.addEventListener("change", renderDashboardTab); // PDF Download pdfBtn.addEventListener("click", () => { const { jsPDF } = window.jspdf; const fileName = `${appState.projectName.replace(/ /g, '_')}_Test_Log.pdf`; // Ensure filters are not visible in the PDF const filtersDiv = toolContainer.querySelector('.ptl-filters'); const filtersDisplayStyle = filtersDiv.style.display; filtersDiv.style.display = 'none'; html2canvas(exportArea, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { // Restore filters display filtersDiv.style.display = filtersDisplayStyle; const imgData = canvas.toDataURL('image/png'); const doc = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const pdfWidth = doc.internal.pageSize.getWidth(); const pdfHeight = doc.internal.pageSize.getHeight(); const imgProps = doc.getImageProperties(imgData); const imgWidth = imgProps.width; const imgHeight = imgProps.height; const margin = 40; const usableWidth = pdfWidth - (2 * margin); const ratio = usableWidth / imgWidth; let scaledHeight = imgHeight * ratio; let position = margin; // Handle multi-page if needed if (scaledHeight > pdfHeight - (2 * margin)) { const pageHeight = pdfHeight - (2 * margin); let heightLeft = scaledHeight; while (heightLeft > 0) { const pageImageHeight = Math.min(pageHeight / ratio, imgHeight - (scaledHeight - heightLeft) / ratio); const imageOffsetY = (scaledHeight - heightLeft) / ratio; const tempCanvas = document.createElement('canvas'); tempCanvas.width = imgWidth; tempCanvas.height = pageImageHeight; const tempCtx = tempCanvas.getContext('2d'); tempCtx.drawImage(canvas, 0, imageOffsetY, imgWidth, pageImageHeight, 0, 0, imgWidth, pageImageHeight); const pageImgData = tempCanvas.toDataURL('image/png'); if (position !== margin) { doc.addPage(); } doc.addImage(pageImgData, 'PNG', margin, margin, usableWidth, pageHeight); heightLeft -= pageHeight; position += pageHeight; } } else { // Single page doc.addImage(imgData, 'PNG', margin, margin, usableWidth, scaledHeight); } doc.save(fileName); }).catch(err => { filtersDiv.style.display = filtersDisplayStyle; // Restore on error console.error("PTL PDF Error:", err); // alert("An error occurred while generating the PDF."); // Per spec }); }); // --- Local Storage --- function saveState() { try { localStorage.setItem("ptlAppState", JSON.stringify(appState)); } catch (e) { console.warn("PTL: Could not save state."); } } function loadState() { try { const storedState = localStorage.getItem("ptlAppState"); if (storedState) appState = JSON.parse(storedState); } catch (e) { console.warn("PTL: Could not load state."); } } // --- Initial Load --- loadState(); renderConfigTab(); renderDashboardTab(); showTab(0); });
Scroll to Top