diff --git a/public/javascripts/chart_utilities.js b/public/javascripts/chart_utilities.js
index f20798f..aa4ce2b 100644
--- a/public/javascripts/chart_utilities.js
+++ b/public/javascripts/chart_utilities.js
@@ -180,17 +180,41 @@ export async function addSensorID2chart(chart, sensor, width) {
}
export const showError = (err) => {
- let e = document.querySelector('#dialogError .modal-dialog .modal-content .modal-body')
- e.innerHTML = err
- let myModal = new bootstrap.Modal(document.getElementById('dialogError'))
- myModal.show()
+ const dialog = document.getElementById('dialogError')
+ const body = dialog.querySelector('.dialog-body')
+ body.innerHTML = err
+ dialog.showModal()
}
export const showReset = () => {
- let myModal = new bootstrap.Modal(document.getElementById('dialogReset'))
- myModal.show()
+ const dialog = document.getElementById('dialogReset')
+ dialog.showModal()
}
+// Setup dialog close buttons
+document.addEventListener('DOMContentLoaded', () => {
+ const errorClose = document.querySelector('#dialogError .dialog-close')
+ if (errorClose) {
+ errorClose.addEventListener('click', () => {
+ document.getElementById('dialogError').close()
+ })
+ }
+
+ const resetClose = document.querySelector('#dialogReset .dialog-close')
+ if (resetClose) {
+ resetClose.addEventListener('click', () => {
+ document.getElementById('dialogReset').close()
+ })
+ }
+
+ const resetOk = document.querySelector('#btnResetOk')
+ if (resetOk) {
+ resetOk.addEventListener('click', () => {
+ document.getElementById('dialogReset').close()
+ })
+ }
+})
+
// remove the taps (if shownig the map)
export const removeTabs = () => {
document.querySelector('#navi').style.display = 'none'
diff --git a/public/javascripts/global.js b/public/javascripts/global.js
index 7a2b04d..4b8d122 100644
--- a/public/javascripts/global.js
+++ b/public/javascripts/global.js
@@ -135,37 +135,32 @@ import * as spin from './spinner.js'
// }
})
- $.datepicker.regional['de'] = {
- monthNames: ['Januar','Februar','März','April','Mai','Juni',
- 'Juli','August','September','Oktober','November','Dezember'],
- monthNamesShort: ['Jan','Feb','Mär','Apr','Mai','Jun',
- 'Jul','Aug','Sep','Okt','Nov','Dez'],
- dayNames: ['Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'],
- dayNamesShort: ['Son','Mon','Die','Mit','Don','Fre','Sam'],
- dayNamesMin: ['So','Mo','Di','Mi','Do','Fr','Sa'],
- firstDay: 1};
-
// Btn 'Settings' pressed
document.querySelector('#btnSet').addEventListener('click', () => {
const curtab = getCurrentTab()
- let myModal = new bootstrap.Modal(document.getElementById('dialogSettings'), {backdrop: 'static'})
- const lang = localStorage.getItem('curlang')
- $('#startday').datepicker($.extend(
- {},
- $.datepicker.regional[lang],
- {
- dateFormat: 'yy-mm-dd',
- maxDate: new Date(),
- minDate: minDate,
- })
- )
- document.getElementById('startday').value = params.seldate
+ const dialog = document.getElementById('dialogSettings')
+
+ // Set max and min dates for date input
+ const today = new Date().toISOString().split('T')[0]
+ const minDateStr = minDate.toISOString().split('T')[0]
+ const startdayInput = document.getElementById('startday')
+ startdayInput.max = today
+ startdayInput.min = minDateStr
+
+ // Convert seldate format if needed
+ let seldate = params.seldate
+ if (seldate === 'today' || seldate === 'heute') {
+ seldate = today
+ }
+
+ startdayInput.value = seldate
document.getElementById('nbrofdays').value = params.span
document.getElementById('peaklim').value = params.peak
document.getElementById('olderthan').value = params.weeks
const centercity = JSON.parse(localStorage.getItem('centercity'))
document.getElementById('centercity').value = centercity.name
- // select different infos depending on the selcted tab
+
+ // select different infos depending on the selected tab
// first clear all
document.querySelector('#ccity').style.display = 'none'
document.querySelector('#stday').style.display = 'none'
@@ -175,13 +170,22 @@ import * as spin from './spinner.js'
for(let i = 0; i < setting.length; i++) {
if(setting[i].typ === curtab) {
for(let j = 0; j < setting[i].show.length; j++) {
- let x = `#${setting[i].show[j]}`
document.querySelector(`#${setting[i].show[j]}`).style.display = 'inline'
}
break
}
}
- myModal.show()
+ dialog.showModal()
+ })
+
+ // Close button for settings dialog
+ document.querySelector('#dialogSettings .dialog-close').addEventListener('click', () => {
+ document.getElementById('dialogSettings').close()
+ })
+
+ // Close button (secondary button)
+ document.querySelector('#btnClose').addEventListener('click', () => {
+ document.getElementById('dialogSettings').close()
})
const getSensorType = async (sid) => {
@@ -231,13 +235,20 @@ import * as spin from './spinner.js'
setInterval(() => dt.showDate(false, params), 1000)
// initialise tabs
- const triggerTabList = [].slice.call(document.querySelectorAll('.nav-link'))
+ const triggerTabList = [].slice.call(document.querySelectorAll('.tab-button'))
triggerTabList.forEach(function (triggerEl) {
- let tabTrigger = new bootstrap.Tab(triggerEl)
-
triggerEl.addEventListener('click', function (event) {
event.preventDefault()
- tabTrigger.show()
+
+ // Remove active from all tabs
+ triggerTabList.forEach(tab => tab.classList.remove('active'))
+ document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'))
+
+ // Add active to clicked tab
+ triggerEl.classList.add('active')
+ const targetId = triggerEl.getAttribute('data-target')
+ document.getElementById(targetId).classList.add('active')
+
let newtab = event.currentTarget.id
setCurrentTab(newtab)
let tab = tabtable.find(tab => tab.id === newtab)
@@ -263,8 +274,13 @@ import * as spin from './spinner.js'
params.center.coords = [props.location[0].loc.coordinates[1], props.location[0].loc.coordinates[0]]
params.sid = csid
let t = triggerTabList[1]
- let tt = new bootstrap.Tab(t)
- tt.show()
+
+ // Switch to live tab
+ triggerTabList.forEach(tab => tab.classList.remove('active'))
+ document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'))
+ t.classList.add('active')
+ document.getElementById('t_live').classList.add('active')
+
setCurrentTab('livetab')
let err = await showChart(params, ttIndex.live)
spin.spinner.stop()
diff --git a/public/javascripts/map_noise.js b/public/javascripts/map_noise.js
index 07658c0..8490655 100644
--- a/public/javascripts/map_noise.js
+++ b/public/javascripts/map_noise.js
@@ -151,8 +151,13 @@ async function onMarkerClick(e, click, txts, params, mpp) {
let ok = await showChart(params, utils.ttIndex.live)
spin.spinner.stop()
if (ok) {
+ // Switch to live tab
let triggerEl = document.querySelector(`#livetab`)
- bootstrap.Tab.getInstance(triggerEl).show() // Select tab by name
+ document.querySelectorAll('.tab-button').forEach(tab => tab.classList.remove('active'))
+ document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'))
+ triggerEl.classList.add('active')
+ document.getElementById('t_live').classList.add('active')
+
params.center.coords = [e.latlng.lat, e.latlng.lng]
ev.preventDefault()
await loadAll(params)
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index bf2fdb5..47e7e1f 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -261,15 +261,6 @@ footer #author #versn {
display: none;
}
}
-.nav-tabs .nav-item .nav-link {
- background-color: #2D6AF8;
- color: #FFF;
-}
-
-.nav-tabs .nav-item .nav-link.active {
- color: #2D6AF8;
- background-color: #FFFFFF;
-}
#stday, #nbday, #pklim, #odth {
display: none;
@@ -298,4 +289,199 @@ footer #author #versn {
.errdiv {
display: none;
-}/*# sourceMappingURL=style.css.map */
\ No newline at end of file
+}
+
+/* Button Styles */
+button {
+ padding: 8px 16px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ background-color: #f8f9fa;
+ color: #333;
+ cursor: pointer;
+ font-size: 14px;
+ font-family: inherit;
+}
+
+button:hover {
+ background-color: #e9ecef;
+}
+
+button:active {
+ background-color: #dee2e6;
+}
+
+#btnSet, #btnHelp {
+ background-color: #2D6AF8;
+ color: white;
+ border-color: #2D6AF8;
+}
+
+#btnSet:hover, #btnHelp:hover {
+ background-color: #1e5ae8;
+}
+
+#btnMyLocation {
+ background-color: white;
+}
+
+/* Tabs */
+.tabs {
+ display: flex;
+ gap: 2px;
+ background-color: #f1f1f1;
+ border-bottom: 1px solid #ccc;
+ padding: 0;
+ margin: 0 0.5vw;
+}
+
+.tab-button {
+ background-color: #2D6AF8;
+ color: white;
+ border: none;
+ border-radius: 4px 4px 0 0;
+ padding: 10px 20px;
+ cursor: pointer;
+ font-size: 14px;
+ transition: background-color 0.3s;
+}
+
+.tab-button:hover {
+ background-color: #1e5ae8;
+}
+
+.tab-button.active {
+ background-color: white;
+ color: #2D6AF8;
+ border-bottom: 2px solid white;
+}
+
+.tab-content {
+ display: block;
+}
+
+.tab-pane {
+ display: none;
+}
+
+.tab-pane.active {
+ display: block;
+}
+
+/* Dialog Styles */
+dialog {
+ border: 1px solid #ccc;
+ border-radius: 8px;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ padding: 0;
+ max-width: 600px;
+ width: 90%;
+}
+
+dialog::backdrop {
+ background-color: rgba(0, 0, 0, 0.5);
+}
+
+.dialog-content {
+ display: flex;
+ flex-direction: column;
+}
+
+.dialog-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 20px;
+ border-bottom: 1px solid #dee2e6;
+ background-color: #f8f9fa;
+}
+
+.dialog-title {
+ margin: 0;
+ font-size: 1.25rem;
+ font-weight: 500;
+}
+
+.dialog-close {
+ background: none;
+ border: none;
+ font-size: 24px;
+ cursor: pointer;
+ padding: 0;
+ width: 30px;
+ height: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #000;
+ opacity: 0.5;
+}
+
+.dialog-close:hover {
+ opacity: 1;
+ background-color: rgba(0, 0, 0, 0.05);
+ border-radius: 4px;
+}
+
+.dialog-body {
+ padding: 20px;
+ overflow-y: auto;
+ max-height: 60vh;
+}
+
+.dialog-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 16px 20px;
+ border-top: 1px solid #dee2e6;
+ background-color: #f8f9fa;
+}
+
+.dialog-button {
+ padding: 8px 16px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 14px;
+}
+
+.dialog-button.primary {
+ background-color: #2D6AF8;
+ color: white;
+ border-color: #2D6AF8;
+}
+
+.dialog-button.primary:hover {
+ background-color: #1e5ae8;
+}
+
+.dialog-button.secondary {
+ background-color: #6c757d;
+ color: white;
+ border-color: #6c757d;
+}
+
+.dialog-button.secondary:hover {
+ background-color: #5a6268;
+}
+
+/* Radio buttons */
+.radio-group {
+ display: flex;
+ gap: 15px;
+}
+
+.radio-label {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ cursor: pointer;
+}
+
+.radio-label input[type="radio"] {
+ margin: 0;
+ cursor: pointer;
+}
+
+/*# sourceMappingURL=style.css.map */
\ No newline at end of file
diff --git a/views/index_noise.pug b/views/index_noise.pug
index 4a62496..ffb8b16 100644
--- a/views/index_noise.pug
+++ b/views/index_noise.pug
@@ -5,125 +5,110 @@ block content
#hline1
#h1name #{t("NoiseMeasurement")}
#buttonsRight
- button.btn.btn-primary#btnSet(value='set') #{t("Settings")}
- // button.btn.btn-primary#btnHelp(value='help') #{t("Info")}
+ button#btnSet(value='set') #{t("Settings")}
+ // button#btnHelp(value='help') #{t("Info")}
#h1datum
#fenster
#spinner
#navi
- ul.nav.nav-tabs#tablist
- li.nav-item(role='presentation')
- button.nav-link.active#maptab(data-bs-toggle="tab" data-bs-target="#t_map" type="button" role="tab" aria-controls="map" aria-selected="true") #{t("Map")}
- li.nav-item(role='presentation')
- button.nav-link#livetab(data-bs-toggle="tab" data-bs-target="#t_live" type="button" role="tab" aria-controls="live" aria-selected="true") #{t("Live")}
- li.nav-item(role='presentation')
- button.nav-link#houravgtab(data-bs-toggle="tab" data-bs-target="#t_houravg" type="button" role="tab" aria-controls="houravg" aria-selected="true") #{t("Hour_AVG")}
- li.nav-item(role='presentation')
- button.nav-link#dayavgtab(data-bs-toggle="tab" data-bs-target="#t_dayavg" type="button" role="tab" aria-controls="dayavg" aria-selected="true") #{t("Day_AVG")}
- li.nav-item(role='presentation')
- button.nav-link#daynighttab(data-bs-toggle="tab" data-bs-target="#t_daynight" type="button" role="tab" aria-controls="daynight" aria-selected="true") #{t("Day_Night")}
- li.nav-item(role='presentation')
- button.nav-link#ldentab(data-bs-toggle="tab" data-bs-target="#t_lden" type="button" role="tab" aria-controls="lden" aria-selected="true") LDEN-Index
+ .tabs#tablist
+ button.tab-button.active#maptab(data-target="t_map" type="button" role="tab" aria-controls="map" aria-selected="true") #{t("Map")}
+ button.tab-button#livetab(data-target="t_live" type="button" role="tab" aria-controls="live") #{t("Live")}
+ button.tab-button#houravgtab(data-target="t_houravg" type="button" role="tab" aria-controls="houravg") #{t("Hour_AVG")}
+ button.tab-button#dayavgtab(data-target="t_dayavg" type="button" role="tab" aria-controls="dayavg") #{t("Day_AVG")}
+ button.tab-button#daynighttab(data-target="t_daynight" type="button" role="tab" aria-controls="daynight") #{t("Day_Night")}
+ button.tab-button#ldentab(data-target="t_lden" type="button" role="tab" aria-controls="lden") LDEN-Index
.tab-content#thetabs
- .tab-pane.fade.show.active#t_map(role="tabpanel" aria-labelledby="map-tab")
+ .tab-pane.active#t_map(role="tabpanel")
#map
- button.btn.btn-light#btnMyLocation(type="button" title=t("MyLocation"))
+ button#btnMyLocation(type="button" title=t("MyLocation"))
span 📍
#maptaberr.errdiv
#mapdateactsens
#actsensors
#mapdate
- .tab-pane.fade#t_live(role="tabpanel" aria-labelledby="live-tab")
+ .tab-pane#t_live(role="tabpanel")
#dlive
#livetaberr.errdiv
- .tab-pane.fade#t_houravg(role="tabpanel" aria-labelledby="map-tab")
+ .tab-pane#t_houravg(role="tabpanel")
#dhour
#houravgtaberr.errdiv
- .tab-pane.fade#t_dayavg(role="tabpanel" aria-labelledby="map-tab")
+ .tab-pane#t_dayavg(role="tabpanel")
#dday
#dayavgtaberr.errdiv
- .tab-pane.fade#t_daynight(role="tabpanel" aria-labelledby="map-tab")
+ .tab-pane#t_daynight(role="tabpanel")
#ddaynight
#daynighttaberr.errdiv
- .tab-pane.fade#t_lden(role="tabpanel" aria-labelledby="map-tab")
+ .tab-pane#t_lden(role="tabpanel")
#dlden
#ldentaberr.errdiv
// Error-Dialog
- .modal.fade#dialogError(tabindex="-1" role="dialog" aria-labelledby="Error" aria-hidden="true")
- .modal-dialog.modal-dialog-centered(role="document")
- .modal-content
- .modal-header
- h5.modal-title.dialogErrorTitle #{t("Error")}
- button(type="button" class="close" data-bs-dismiss="modal" aria-label="Close")
- span(aria-hidden="true") ×
- .modal-body
- .modal-footer
- //button(type="button" class="btn btn-secondary" data-bs-dismiss="modal") Close
- // button(type="button" class="btn btn-primary") Save changes
+ dialog#dialogError
+ .dialog-content
+ .dialog-header
+ h3.dialog-title #{t("Error")}
+ button.dialog-close(type="button" aria-label="Close") ×
+ .dialog-body
+ .dialog-footer
// Settings-Dialog
- .modal.fade#dialogSettings(tabindex="-1" role="dialog" aria-labelledby="Settings" aria-hidden="true")
- .modal-dialog.modal-dialog-centered(role="document")
- .modal-content
- .modal-header
- h5.modal-title.dialogErrorTitle #{t("Settings")}
- button(type="button" class="close" data-bs-dismiss="modal" aria-label="Close")
- span(aria-hidden="true") ×
- .modal-body.settings
- .rows
- .comlumn#ccity
- label.thelabels(for='city') #{t("CenterMap")}:
- input.theInputs#centercity(name="centercity")
- .column#stday
- label.thelabels(for='startday') #{t("StartDate")}:
- input.theInputs#startday(name='startday' )
- .column
- .column#nbday
- label.thelabels(for='nbrofdays')
- | #{t("NumberOfDays")}:
- // span.klein (#{t("Only_for_live_chart")})
- input.theInputs#nbrofdays(type = 'number' name = 'nbrofdays', min="1", max="10")
- span.klein (max. 10)
- .column#pklim
- label.thelabels(for='peaklim')
- | #{t("Count_peaks_over")}
- input.theInputs#peaklim(type = 'number' name = 'peaklim', min="10", max="130")
- | dbA
- span.klein (max. 130)
- .column#odth
- label.thelabels(for='olderthan')
- | #{t("RemoveFromMap")}
- input.theInputs#olderthan(type = 'number' name = 'olderthan', min="0", max="52")
- | #{t("weeks")}
- span.klein (max. 52)
- .column
- .column#sellan
- label.thelabels
- | #{t("Language")}:
- .form-check.form-check-inline
- input#de.form-check-input(type="radio" name="lanbut")
- label.form-check-label(for='de') Deutsch
- .form-check.form-check-inline
- input#en.form-check-input(type="radio" name="lanbut", checked)
- label.form-check-label(for="en") English
- .modal-footer
- button.btn.btn-secondary(type="button" data-bs-dismiss="modal") #{t("Close")}
- button#btnSave.btn.btn-primary(type="button" data-bs-dismiss="modal") #{t("Save_changes")}
+ dialog#dialogSettings
+ .dialog-content
+ .dialog-header
+ h3.dialog-title #{t("Settings")}
+ button.dialog-close(type="button" aria-label="Close") ×
+ .dialog-body.settings
+ .rows
+ .column#ccity
+ label.thelabels(for='city') #{t("CenterMap")}:
+ input.theInputs#centercity(name="centercity")
+ .column#stday
+ label.thelabels(for='startday') #{t("StartDate")}:
+ input.theInputs#startday(type='date' name='startday')
+ .column
+ .column#nbday
+ label.thelabels(for='nbrofdays')
+ | #{t("NumberOfDays")}:
+ input.theInputs#nbrofdays(type='number' name='nbrofdays' min="1" max="10")
+ span.klein (max. 10)
+ .column#pklim
+ label.thelabels(for='peaklim')
+ | #{t("Count_peaks_over")}
+ input.theInputs#peaklim(type='number' name='peaklim' min="10" max="130")
+ | dbA
+ span.klein (max. 130)
+ .column#odth
+ label.thelabels(for='olderthan')
+ | #{t("RemoveFromMap")}
+ input.theInputs#olderthan(type='number' name='olderthan' min="0" max="52")
+ | #{t("weeks")}
+ span.klein (max. 52)
+ .column
+ .column#sellan
+ label.thelabels
+ | #{t("Language")}:
+ .radio-group
+ label.radio-label
+ input#de(type="radio" name="lanbut")
+ span Deutsch
+ label.radio-label
+ input#en(type="radio" name="lanbut" checked)
+ span English
+ .dialog-footer
+ button.dialog-button.secondary#btnClose(type="button") #{t("Close")}
+ button.dialog-button.primary#btnSave(type="button") #{t("Save_changes")}
// Reset-Dialog
- .modal.fade#dialogReset(tabindex="-1" role="dialog" aria-labelledby="Error" aria-hidden="true")
- .modal-dialog.modal-dialog-centered(role="document")
- .modal-content
- .modal-header
- h5.modal-title.dialogErrorTitle Info
- button(type="button" class="close" data-bs-dismiss="modal" aria-label="Close")
- span(aria-hidden="true") ×
- .modal-body
- .modal-footer
- //button(type="button" class="btn btn-secondary" data-bs-dismiss="modal") Close
- button(type="button" class="btn btn-primary" data-bs-dismiss="modal") OK
+ dialog#dialogReset
+ .dialog-content
+ .dialog-header
+ h3.dialog-title Info
+ button.dialog-close(type="button" aria-label="Close") ×
+ .dialog-body
+ .dialog-footer
+ button.dialog-button.primary#btnResetOk(type="button") OK
diff --git a/views/layout.pug b/views/layout.pug
index 984b5b9..2332bff 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -3,14 +3,10 @@ html
head
title= title
meta(name="viewport" content="width=device-width, initial-scale=1" charset="utf-8")
- link(href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css"
- rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65"
- crossorigin="anonymous")
link(rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css"
integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI="
crossorigin="")
link(rel='stylesheet', href='/stylesheets/MarkerCluster.css')
- link(rel="stylesheet" href="https://code.jquery.com/ui/1.14.0/themes/base/jquery-ui.css")
link(rel='stylesheet', href='/spin.js/spin.css')
@@ -33,14 +29,9 @@ html
a(href="mailto:rexfue@gmail.com") mailto:rexfue@gmail.com
#versn #{t('Version')}: #{version} #{t('from')} #{date}
- script(src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"
- integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4"
- crossorigin="anonymous")
script(src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"
integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM="
crossorigin="")
script(src="/javascripts/leaflet.markercluster.js")
script(src="https://code.highcharts.com/highcharts.js")
- script(src="https://code.jquery.com/jquery-3.7.1.min.js")
- script(src="https://code.jquery.com/ui/1.14.0/jquery-ui.min.js")
script(type="module" src="/javascripts/global.js")
\ No newline at end of file