Noise Pollution Dashboard

Noise Pollution Dashboard

Monitor and analyze noise levels from various sources and locations.

Key Metrics

Noise Levels Over Time

Average Noise Level by Source

Recent Readings Log

Date & Time Location Source Level (dB)

Enter Noise Readings

${totalReadings}

Average Level

${avgLevel.toFixed(1)} dB

Peak Level

${peakLevel} dB

Most Common Source

${mostCommonSource}

`; }; const renderDataTable = () => { dataTableBody.innerHTML = ''; const sortedReadings = [...state.readings].sort((a, b) => new Date(b.datetime) - new Date(a.datetime)); sortedReadings.forEach(r => { const dt = new Date(r.datetime); const formattedDt = `${dt.toLocaleDateString()} ${dt.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}`; const row = ` ${formattedDt} ${r.location} ${r.source} ${r.level} dB `; dataTableBody.innerHTML += row; }); }; const renderCharts = () => { // Noise Trend Chart (Line) const sortedByTime = [...state.readings].sort((a, b) => new Date(a.datetime) - new Date(b.datetime)); if (noiseTrendChart) noiseTrendChart.destroy(); noiseTrendChart = new Chart(noiseTrendCanvas.getContext('2d'), { type: 'line', data: { datasets: [{ label: 'Noise Level (dB)', data: sortedByTime.map(r => ({ x: r.datetime, y: r.level })), borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', fill: true, tension: 0.1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { type: 'time', time: { tooltipFormat: 'PPpp' } }, y: { beginAtZero: false, title: { display: true, text: 'Noise Level (dB)' } } } } }); // Source Chart (Bar) const avgLevelBySource = state.readings.reduce((acc, r) => { if (!acc[r.source]) acc[r.source] = { total: 0, count: 0 }; acc[r.source].total += r.level; acc[r.source].count++; return acc; }, {}); const sourceLabels = Object.keys(avgLevelBySource); const sourceData = sourceLabels.map(label => avgLevelBySource[label].total / avgLevelBySource[label].count); if (sourceChart) sourceChart.destroy(); sourceChart = new Chart(sourceCanvas.getContext('2d'), { type: 'bar', data: { labels: sourceLabels, datasets: [{ label: 'Average dB', data: sourceData, backgroundColor: ['#ef4444', '#f97316', '#eab308', '#84cc16', '#22c55e', '#10b981', '#06b6d4'], }] }, options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { title: { display: true, text: 'Average Noise Level (dB)' } } } } }); }; const renderConfigRows = () => { configRowsContainer.innerHTML = ''; state.readings.forEach(r => { const row = `
`; configRowsContainer.innerHTML += row; }); addConfigEventListeners(); }; // --- EVENT HANDLERS --- const handleConfigChange = (e) => { const id = parseInt(e.target.dataset.id); const field = e.target.dataset.field; const value = (field === 'level') ? parseInt(e.target.value, 10) : e.target.value; const reading = state.readings.find(r => r.id === id); if (reading) reading[field] = value; renderAll(); }; const handleAddReading = () => { const newId = state.readings.length > 0 ? Math.max(...state.readings.map(r => r.id)) + 1 : 1; const now = new Date(); now.setMinutes(now.getMinutes() - now.getTimezoneOffset()); // Adjust for local time input const nowISO = now.toISOString().slice(0, 16); state.readings.push({ id: newId, datetime: nowISO, location: "New Location", source: "Unknown", level: 50 }); renderAll(); }; const handleRemoveReading = (e) => { const id = parseInt(e.target.dataset.id); state.readings = state.readings.filter(r => r.id !== id); renderAll(); }; const addConfigEventListeners = () => { document.querySelectorAll('.config-input').forEach(input => input.addEventListener('change', handleConfigChange)); document.querySelectorAll('.remove-reading-btn').forEach(button => button.addEventListener('click', handleRemoveReading)); }; const handleDownloadPdf = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-content'); document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'hidden'); Chart.defaults.animation = false; html2canvas(pdfContent, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'visible'); Chart.defaults.animation = true; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const imgWidth = pdfWidth - 20; const imgHeight = canvas.height * imgWidth / canvas.width; pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight); pdf.save('Noise-Pollution-Dashboard.pdf'); }); }; // --- TABBING LOGIC --- let currentTabIndex = 0; const updateTabButtons = () => { prevTabBtn.disabled = currentTabIndex === 0; nextTabBtn.disabled = currentTabIndex === tabContents.length - 1; }; const switchTab = (index) => { tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); tabButtons[index].classList.add('active'); tabContents[index].classList.add('active'); currentTabIndex = index; updateTabButtons(); }; tabButtons.forEach((button, index) => button.addEventListener('click', () => switchTab(index))); prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); }); nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) switchTab(currentTabIndex + 1); }); // --- INITIALIZATION --- if (kpiCardsContainer && addReadingBtn && downloadPdfBtn) { addReadingBtn.addEventListener('click', handleAddReading); downloadPdfBtn.addEventListener('click', handleDownloadPdf); renderAll(); updateTabButtons(); } else { console.error("Essential dashboard elements could not be found."); } });
Scroll to Top