No risks added yet.
";
}
appState.risks.forEach(function(risk) {
var item = document.createElement("div");
item.className = "ram-risk-item";
var score = calculateScore(risk.prob, risk.impact);
var levelData = getRiskLevel(score);
item.innerHTML = `
${risk.riskId}: ${risk.desc.substring(0, 50)}...
(${levelData.level})
`;
riskList.appendChild(item);
});
};
var renderHeatMap = function() {
heatMapContainer.innerHTML = '';
var scoreCounts = {};
// Initialize score counts for 3x3 matrix (1-9)
for (var p = 1; p <= 3; p++) {
for (var i = 1; i <= 3; i++) {
scoreCounts[p + '-' + i] = 0;
}
}
appState.risks.forEach(function(risk) {
var key = risk.prob + '-' + risk.impact;
scoreCounts[key]++;
});
for (var p = 3; p >= 1; p--) { // Probability (rows: High to Low)
for (var i = 1; i <= 3; i++) { // Impact (columns: Low to High)
var score = p * i;
var count = scoreCounts[p + '-' + i];
var levelData = getRiskLevel(score);
var cell = document.createElement('div');
cell.className = 'ram-matrix-cell';
cell.setAttribute('data-risk', score);
var top = (3 - p) * 33.33;
var left = (i - 1) * 33.33;
cell.style.top = top + '%';
cell.style.left = left + '%';
// Display the count and the risk score
cell.innerHTML = '
' + count + '' + '
(Score ' + score + ')';
heatMapContainer.appendChild(cell);
}
}
};
var renderDashboardTab = function() {
projectTitleDisplay.textContent = appState.projectName;
renderHeatMap();
var levelFilter = filterLevelSelect.value;
var sortBy = sortBySelect.value;
// 1. Filter
var filteredRisks = appState.risks.filter(function(risk) {
var score = calculateScore(risk.prob, risk.impact);
var levelData = getRiskLevel(score);
if (levelFilter === 'all') return true;
if (levelFilter === 'Low') return levelData.level === 'Low' || levelData.level === 'Medium';
return levelData.level === levelFilter;
});
// 2. Sort
filteredRisks.sort(function(a, b) {
var scoreA = calculateScore(a.prob, a.impact);
var scoreB = calculateScore(b.prob, b.impact);
if (sortBy === 'score_desc') {
return scoreB - scoreA;
} else if (sortBy === 'name_asc') {
return a.riskId.localeCompare(b.riskId);
}
return 0;
});
// 3. Render Table
tableBody.innerHTML = "";
if (filteredRisks.length === 0) {
tableBody.innerHTML = "
| No risks match the current filter. |
";
return;
}
filteredRisks.forEach(function(risk) {
var score = calculateScore(risk.prob, risk.impact);
var levelData = getRiskLevel(score);
var tagClass = 'tag-' + levelData.color;
var tr = document.createElement("tr");
tr.innerHTML = `
${risk.riskId} |
${risk.desc} |
${risk.prob} |
${risk.impact} |
${levelData.level} (${score})
|
${risk.mitigation} |
`;
tableBody.appendChild(tr);
});
};
// --- Event Handlers ---
// Update Dashboard Button
updateBtn.addEventListener("click", function() {
appState.projectName = projectNameInput.value || "Untitled Risk Assessment";
saveState();
renderDashboardTab();
showTab(0);
});
// Add Risk
addRiskBtn.addEventListener("click", function() {
var riskId = riskIdInput.value.trim();
var desc = riskDescInput.value.trim();
var prob = parseInt(riskProbSelect.value);
var impact = parseInt(riskImpactSelect.value);
var mitigation = riskMitigationTextarea.value.trim();
if (!riskId || !desc) {
alert("Please enter a Risk ID and Description.");
return;
}
var newRisk = {
id: Date.now(),
riskId: riskId,
desc: desc,
prob: prob,
impact: impact,
mitigation: mitigation
};
appState.risks.push(newRisk);
saveState();
renderConfigTab();
// Clear form
riskIdInput.value = "";
riskDescInput.value = "";
riskMitigationTextarea.value = "";
riskProbSelect.value = "2";
riskImpactSelect.value = "2";
});
// Remove Risk (Event Delegation)
riskList.addEventListener("click", function(e) {
if (e.target.classList.contains("ram-remove-btn")) {
var id = parseInt(e.target.dataset.id);
appState.risks = appState.risks.filter(function(risk) { return risk.id !== id; });
saveState();
renderConfigTab();
}
});
// Filters Change
filterLevelSelect.addEventListener("change", renderDashboardTab);
sortBySelect.addEventListener("change", renderDashboardTab);
// PDF Download
pdfBtn.addEventListener("click", function() {
var jsPDF = window.jspdf.jsPDF;
var fileName = `${appState.projectName.replace(/ /g, '_')}_Risk_Report.pdf`;
// Temporarily adjust minimum table width for PDF quality
var table = document.getElementById('ram-register-table');
var originalMinWidth = table.style.minWidth;
table.style.minWidth = '1100px';
// Hide filters for PDF
var filtersDiv = toolContainer.querySelector('.ram-filters');
var filtersDisplayStyle = filtersDiv.style.display;
filtersDiv.style.display = 'none';
html2canvas(exportArea, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff'
}).then(function(canvas) {
// Restore styles
table.style.minWidth = originalMinWidth;
filtersDiv.style.display = filtersDisplayStyle;
var imgData = canvas.toDataURL('image/png');
var doc = new jsPDF({
orientation: 'l', // Landscape is better for wide tables
unit: 'pt',
format: 'a4'
});
var pdfWidth = doc.internal.pageSize.getWidth();
var pdfHeight = doc.internal.pageSize.getHeight();
var imgProps = doc.getImageProperties(imgData);
var imgWidth = imgProps.width;
var imgHeight = imgProps.height;
var margin = 30;
var usableWidth = pdfWidth - (2 * margin);
var ratio = usableWidth / imgWidth;
var scaledHeight = imgHeight * ratio;
// Handle multi-page if content exceeds page height
if (scaledHeight > pdfHeight - (2 * margin)) {
var pageHeight = pdfHeight - (2 * margin);
var heightLeft = scaledHeight;
var 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(function(err) {
// Restore styles even if PDF fails
table.style.minWidth = originalMinWidth;
filtersDiv.style.display = filtersDisplayStyle;
console.error("RAM PDF Error:", err);
// alert("An error occurred while generating the PDF."); // Per spec
});
});
// --- Local Storage ---
var saveState = function() {
try {
localStorage.setItem("ramAppState", JSON.stringify(appState));
} catch (e) { console.warn("RAM: Could not save state."); }
};
var loadState = function() {
try {
var storedState = localStorage.getItem("ramAppState");
if (storedState) appState = JSON.parse(storedState);
} catch (e) { console.warn("RAM: Could not load state."); }
};
// --- Initial Load ---
loadState();
renderConfigTab();
renderDashboardTab();
showTab(0);
});