Requirements Traceability Matrix Generator

Requirements Traceability Matrix Generator

Project Aurora - Traceability Matrix

Req ID Requirement Description Source/Origin Verification ID(s) Verification Status

Project Information

Add New Requirement

Current Requirements

No requirements added yet.

"; } appState.requirements.forEach(req => { const item = document.createElement("div"); item.className = "rtm-req-item"; item.innerHTML = ` ${req.reqId}: ${req.desc.substring(0, 50)}... `; reqList.appendChild(item); }); } function renderDashboardTab() { projectTitleDisplay.textContent = appState.projectName; // Populate Source Filter const uniqueSources = getUniqueSources(); const currentSourceFilter = filterSourceSelect.value; filterSourceSelect.innerHTML = ''; uniqueSources.forEach(source => { const option = document.createElement('option'); option.value = source; option.textContent = source; filterSourceSelect.appendChild(option); }); filterSourceSelect.value = currentSourceFilter; // Filter props based on selections const sourceFilter = filterSourceSelect.value; const statusFilter = filterStatusSelect.value; const filteredReqs = appState.requirements.filter(req => { const sourceMatch = sourceFilter === 'all' || req.source === sourceFilter; const statusMatch = statusFilter === 'all' || req.status === statusFilter; return sourceMatch && statusMatch; }); // Render Table tableBody.innerHTML = ""; if (filteredReqs.length === 0) { tableBody.innerHTML = "No requirements match the current filters."; return; } filteredReqs.forEach(req => { const tr = document.createElement("tr"); let statusClass = ''; if (req.status === 'Verified') { statusClass = 'rtm-verified'; } else if (req.status === 'Unverified') { statusClass = 'rtm-unverified'; } tr.innerHTML = ` ${req.reqId} ${req.desc} ${req.source} ${req.verifIds} ${req.status} `; tableBody.appendChild(tr); }); } // --- Event Handlers --- // Update Dashboard Button updateBtn.addEventListener("click", () => { appState.projectName = projectNameInput.value || "Untitled Project"; saveState(); renderDashboardTab(); showTab(0); }); // Add Requirement addReqBtn.addEventListener("click", () => { const reqId = reqIdInput.value.trim(); const desc = reqDescInput.value.trim(); const source = sourceIdInput.value.trim(); const verifIds = verifIdsInput.value.trim(); const status = verifStatusSelect.value; if (!reqId || !desc || !source) { alert("Please enter a Req ID, Description, and Source ID."); return; } const newReq = { id: Date.now(), reqId: reqId, desc: desc, source: source, verifIds: verifIds, status: status }; appState.requirements.push(newReq); saveState(); renderConfigTab(); // Clear form reqIdInput.value = ""; reqDescInput.value = ""; sourceIdInput.value = ""; verifIdsInput.value = ""; verifStatusSelect.value = "Unverified"; }); // Remove Requirement (Event Delegation) reqList.addEventListener("click", (e) => { if (e.target.classList.contains("rtm-remove-btn")) { const id = parseInt(e.target.dataset.id); appState.requirements = appState.requirements.filter(req => req.id !== id); saveState(); renderConfigTab(); } }); // Filters Change filterSourceSelect.addEventListener("change", renderDashboardTab); filterStatusSelect.addEventListener("change", renderDashboardTab); // PDF Download pdfBtn.addEventListener("click", () => { const { jsPDF } = window.jspdf; const fileName = `${appState.projectName.replace(/ /g, '_')}_RTM.pdf`; // Temporarily adjust minimum table width for PDF quality const table = document.getElementById('rtm-matrix-table'); const originalMinWidth = table.style.minWidth; table.style.minWidth = '1100px'; // Hide filters for PDF const filtersDiv = toolContainer.querySelector('.rtm-filters'); const filtersDisplayStyle = filtersDiv.style.display; filtersDiv.style.display = 'none'; html2canvas(exportArea, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { // Restore styles table.style.minWidth = originalMinWidth; filtersDiv.style.display = filtersDisplayStyle; const imgData = canvas.toDataURL('image/png'); const doc = new jsPDF({ orientation: 'l', // Landscape is better for wide tables 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 = 30; const usableWidth = pdfWidth - (2 * margin); const ratio = usableWidth / imgWidth; let scaledHeight = imgHeight * ratio; // Handle multi-page if content exceeds page height if (scaledHeight > pdfHeight - (2 * margin)) { const pageHeight = pdfHeight - (2 * margin); let heightLeft = scaledHeight; let position = 0; while (heightLeft > 0) { doc.addImage(imgData, 'PNG', margin, position + margin, usableWidth, scaledHeight); heightLeft -= pageHeight; position -= pageHeight; if (heightLeft > 0) { doc.addPage(); } } } else { // Single page doc.addImage(imgData, 'PNG', margin, margin, usableWidth, scaledHeight); } doc.save(fileName); }).catch(err => { // Restore styles even if PDF fails table.style.minWidth = originalMinWidth; filtersDiv.style.display = filtersDisplayStyle; console.error("RTM PDF Error:", err); // alert("An error occurred while generating the PDF."); // Per spec }); }); // --- Local Storage --- function saveState() { try { localStorage.setItem("rtmAppState", JSON.stringify(appState)); } catch (e) { console.warn("RTM: Could not save state."); } } function loadState() { try { const storedState = localStorage.getItem("rtmAppState"); if (storedState) appState = JSON.parse(storedState); } catch (e) { console.warn("RTM: Could not load state."); } } // --- Initial Load --- loadState(); renderConfigTab(); renderDashboardTab(); showTab(0); });
Scroll to Top