No data collected. Add test results in the Configuration tab.
";
} else {
const tableEl = document.createElement("div");
tableEl.innerHTML = `
`;
testData.forEach(item => {
// Add placeholder values for standard and status for editing
const row = document.createElement('div');
row.className = 'rprg-table-row';
row.dataset.id = item.id;
row.innerHTML = `
`;
tableEl.appendChild(row);
});
dashboardDataTable.appendChild(tableEl);
}
showTab("rprg-tab-dashboard");
}
/**
* Handles removal of rows directly from the dashboard table
*/
function handleDashboardRemove(e) {
if (e.target.dataset.action === "remove") {
const rowEl = e.target.closest(".rprg-table-row");
if (rowEl) {
const id = rowEl.dataset.id;
// Remove from master state
testData = testData.filter(item => item.id !== id);
// Remove from dashboard view and config view
rowEl.remove();
updateConfigTestDisplay();
if (testData.length === 0) {
handleGenerateDashboard(); // Re-render to show empty state
}
}
}
}
/**
* Generates a PDF report from the dashboard data (CRITICAL FUNCTION)
*/
function downloadPDF() {
// 1. PDF Library Check
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') {
alert("Error: PDF libraries could not be loaded. Please try again.");
return;
}
// 2. Get potentially edited values from dashboard
const title = dashTitle.value || "River Pollution Report";
const summary = dashSummary.value || "No summary provided.";
const findings = dashFindings.value || "No findings recorded.";
const recommendations = dashRecommendations.value || "No recommendations recorded.";
const river = configRiver.value || "Water Body";
const date = configDate.value || "N/A";
const reporter = configReporter.value || "N/A";
const { jsPDF } = window.jspdf;
const doc = new jsPDF("p", "pt", "a4");
const margin = 40;
let yPos = margin;
const lineHeight = 14;
const usableWidth = doc.internal.pageSize.getWidth() - margin * 2;
// Function to add a structured section
function addSection(title, text) {
// Check page break before adding section
const textLines = doc.splitTextToSize(text, usableWidth);
const textHeight = textLines.length * 10; // Approx height
if (yPos > doc.internal.pageSize.getHeight() - 50) {
doc.addPage();
yPos = margin;
}
if (yPos + 20 + textHeight > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
yPos = margin;
}
doc.setFontSize(14);
doc.setFont('helvetica', 'bold');
doc.text(title, margin, yPos);
yPos += lineHeight * 1.2;
doc.setFontSize(10);
doc.setFont('helvetica', 'normal');
const finalLines = doc.splitTextToSize(text, usableWidth);
doc.text(finalLines, margin, yPos);
yPos += finalLines.length * 10 + 15;
}
// 3. Document Title & Details
doc.setFontSize(20);
doc.setFont('helvetica', 'bold');
doc.text(title, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += lineHeight * 2;
doc.setFontSize(10);
doc.setFont('helvetica', 'normal');
doc.text(`River/Body: ${river}`, margin, yPos);
doc.text(`Date: ${date}`, margin + 200, yPos);
doc.text(`Organization: ${reporter}`, margin + 350, yPos);
yPos += lineHeight * 2;
// 4. Add Report Sections
addSection("Executive Summary", summary);
addSection("Key Findings & Analysis", findings);
addSection("Recommendations", recommendations);
// 5. Add Data Table
const tableHead = [["Parameter", "Measured Value", "Standard Limit", "Status"]];
const tableBody = [];
const dashRows = dashboardDataTable.querySelectorAll(".rprg-table-row");
dashRows.forEach(row => {
const name = row.querySelector(".rprg-dash-name")?.value || 'N/A';
const value = row.querySelector(".rprg-dash-value")?.value || 'N/A';
const limit = row.querySelector(".rprg-dash-standard")?.value || 'N/A';
const status = row.querySelector(".rprg-dash-status")?.value || 'N/A';
tableBody.push([name, value, limit, status]);
});
if (tableBody.length > 0) {
addSection("Water Quality Data Table", `Detailed measurements taken on ${date}.`);
doc.autoTable({
startY: yPos,
head: tableHead,
body: tableBody,
theme: 'striped',
headStyles: {
fillColor: [0, 115, 230],
textColor: [255, 255, 255],
},
columnStyles: {
0: { cellWidth: 150 },
1: { cellWidth: 100 },
2: { cellWidth: 100 },
3: { cellWidth: 'auto' },
},
styles: {
fontSize: 9,
cellPadding: 3,
overflow: 'linebreak'
},
margin: { left: margin, right: margin }
});
}
doc.save(`${configRiver.value.replace(/ /g,"_")}_Pollution_Report.pdf`);
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 4. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (addTestForm) {
addTestForm.addEventListener("submit", handleAddTest);
}
if (configTestList) {
configTestList.addEventListener("click", handleConfigListClick);
}
if (generateBtn) {
generateBtn.addEventListener("click", handleGenerateDashboard);
}
// Dashboard Tab Listeners
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
if (dashboardDataTable) {
dashboardDataTable.addEventListener("click", handleDashboardRemove);
}
// Set default date
configDate.valueAsDate = new Date();
// Load sample data
testData.push({ id: 't1', name: 'Dissolved Oxygen (DO)', value: '3.5 mg/L' });
testData.push({ id: 't2', name: 'pH', value: '5.8' });
testData.push({ id: 't3', name: 'Fecal Coliform', value: '450 CFU/100mL' });
// Initial display of sample data
updateConfigTestDisplay();
// Initial State: Generate dashboard with samples
handleGenerateDashboard();
showTab("rprg-tab-dashboard");
});