diff --git a/public/js/appChart.js b/public/js/appChart.js
index f78f42eef122adf0a07953a053f6cb58a2eced65..5fffe09ad7a2840eb87351772b3f3f2ebe505373 100644
--- a/public/js/appChart.js
+++ b/public/js/appChart.js
@@ -24,6 +24,8 @@ import { drawColumnChartHighcharts } from "./src_modules/chartColumn.mjs";
 import {
   showLoadingSpinner,
   hideLoadingSpinner,
+  disableDrawChartButton,
+  enableDrawChartButton,
 } from "./src_modules/loadingIndicator.mjs";
 
 import { vanillaSelectBox } from "./thirdparty/vanillaSelectBox.mjs";
@@ -138,7 +140,7 @@ const drawChartUsingSelectedOptions = async function () {
     const selectedOptionsAllDropDownLists =
       getSelectedOptionsFromAllDropDownLists();
 
-    // Note: The aggregation type + duration and chart type are the first and
+    // Note: The chart type amd aggregation type + duration are the first and
     // third elements respectively, we have ignored the second and fourth elements
     const [selectedChartTypeArr, , selectedAggregationTypeDurationArr] =
       selectedOptionsAllDropDownLists;
@@ -178,71 +180,74 @@ const drawChartUsingSelectedOptions = async function () {
       );
 
     // Create copies of the arrays of building(s) + data point(s) + sampling rate
-    const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr = [
+    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr = [
       ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
     ];
 
-    const selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr = [
+    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr = [
       ...selectedBuildingsDataPointsSamplingRateAbbrevNestedArr,
     ];
 
-    // Check if we have non-computed
-    const selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr =
+    // Check if we have raw observations
+    const selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr =
       checkIfSelectedOptionsContainTemperatureDifference(
-        selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
+        selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
       )
         ? deleteTemperatureDifferenceOptions(
-            selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr
+            selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr
           )
-        : selectedBuildingsDataPointsSamplingRateAbbrevNonComputedCopyArr;
+        : selectedBuildingsDataPointsSamplingRateAbbrevRawObsCopyArr;
 
-    // Check if we have computed / dT
-    const selectedBuildingsDataPointsSamplingRateAbbrevComputedArr =
+    // Check if we have dT (temperature difference)
+    const selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr =
       checkIfSelectedOptionsContainTemperatureDifference(
-        selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
+        selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
       )
         ? extractTemperatureDifferenceOptions(
-            selectedBuildingsDataPointsSamplingRateAbbrevComputedCopyArr
+            selectedBuildingsDataPointsSamplingRateAbbrevTempDiffCopyArr
           )
         : [];
 
     // Display the loading indicator
     showLoadingSpinner();
 
-    // Fetch the observations + metadata / non-computed
-    const observationsPlusMetadataNonComputedArr =
-      selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr.length === 0
+    // Disable the 'draw chart' button
+    disableDrawChartButton();
+
+    // Fetch the observations + metadata / raw observations
+    const observationsRawPlusMetadataArr =
+      selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr.length === 0
         ? [[], []]
         : await getMetadataPlusObservationsFromSingleOrMultipleDatastreams(
             BASE_URL,
             QUERY_PARAMS_COMBINED,
-            selectedBuildingsDataPointsSamplingRateAbbrevNonComputedArr
+            selectedBuildingsDataPointsSamplingRateAbbrevRawObsArr
           );
 
-    // Fetch the observations + metadata / computed (dT)
-    const observationsPlusMetadataComputedArr =
-      selectedBuildingsDataPointsSamplingRateAbbrevComputedArr.length === 0
+    // Fetch the observations + metadata / temperature difference (dT)
+    const observationsTempDiffPlusMetadataArr =
+      selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr.length === 0
         ? [[], []]
         : await calculateVorlaufMinusRuecklaufTemperature(
             BASE_URL,
             QUERY_PARAMS_COMBINED,
             extractBuildingPlusSamplingRate(
-              selectedBuildingsDataPointsSamplingRateAbbrevComputedArr
+              selectedBuildingsDataPointsSamplingRateAbbrevTempDiffArr
             )
           );
 
-    // Extract the combined arrays for observations and metadata / non-computed
-    const [observationsNestedNonComputedArr, metadataNestedNonComputedArr] =
-      observationsPlusMetadataNonComputedArr;
+    // Extract the combined arrays for observations and metadata / raw observations
+    const [observationsRawNestedArr, metadataTempDiffNestedArr] =
+      observationsRawPlusMetadataArr;
 
-    // Extract the combined arrays for observations and metadata / computed (dT)
+    // Extract the combined arrays for observations and metadata / temperature difference (dT)
     const [observationsNestedComputedArr, metadataNestedComputedArr] =
-      observationsPlusMetadataComputedArr;
+      observationsTempDiffPlusMetadataArr;
 
     // Create a combined array of observations and metadata
     const observationsPlusMetadataCombined = [
-      [...observationsNestedNonComputedArr, ...observationsNestedComputedArr],
-      [...metadataNestedNonComputedArr, ...metadataNestedComputedArr],
+      [...observationsRawNestedArr, ...observationsNestedComputedArr],
+      [...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
     ];
 
     const [observationsComboNestedArr, metadataComboNestedArr] =
@@ -253,8 +258,8 @@ const drawChartUsingSelectedOptions = async function () {
       (metadataObj) => formatDatastreamMetadataForChart(metadataObj)
     );
 
-    // Extract the formatted metadata properties - used by ALL chart types
-    const extractedFormattedDatastreamProperties =
+    // Extract the formatted metadata properties for the raw observations - used by ALL chart types
+    const rawObservationsExtractedFormattedDatastreamProperties =
       extractPropertiesFromFormattedDatastreamMetadata(
         formattedMetadataNestedArr,
         false
@@ -272,111 +277,125 @@ const drawChartUsingSelectedOptions = async function () {
     const aggregationStartDate = "2020-01-01";
     const aggregationEndDate = "2020-12-31";
 
-    // Extract observations within the user-specified start and end date - used by MULTIPLE chart types
-    const observationsAggregationNestedArr = observationsComboNestedArr.map(
-      (obsArr) =>
-        extractObservationsWithinDatesInterval(
-          obsArr,
-          "60min",
-          aggregationStartDate,
-          aggregationEndDate
-        )
-    );
+    // Create final array of observations- used by MULTIPLE chart types
+    // If we are performing aggregation, it was envisioned that the user would
+    // select observations that fall within a start and end date
+    const observationsComboNestedFinalArr =
+      selectedAggregationType === "None (raw data)"
+        ? observationsComboNestedArr
+        : observationsComboNestedArr.map((observationsArr) =>
+            extractObservationsWithinDatesInterval(
+              observationsArr,
+              selectedSamplingRateAbbrev,
+              aggregationStartDate,
+              aggregationEndDate
+            )
+          );
 
     // Unique calendar dates - used by MULTIPLE chart types
-    const uniqueCalendarDatesNestedArr = observationsAggregationNestedArr.map(
+    const uniqueCalendarDatesNestedArr = observationsComboNestedFinalArr.map(
       (observationsArr) =>
         extractUniqueCalendarDatesFromTimestamp(observationsArr)
     );
 
     for (const selectedChartType of selectedChartTypeArr) {
-      if (selectedChartType === "Heatmap") {
-        // We are interested in raw observations
-        if (
-          selectedAggregationOptionsAreValid &&
-          selectedBuildingDataPointsOptionsAreValid
-        ) {
-          drawHeatmapBasedOnSelectedOptions(
-            observationsComboNestedArr,
-            extractedFormattedDatastreamProperties
-          );
-        }
-      }
-
-      if (selectedChartType === "Scatter Plot") {
-        // We are interested in raw observations
-        if (
-          selectedAggregationOptionsAreValid &&
-          selectedBuildingDataPointsOptionsAreValid
-        ) {
-          drawScatterPlotFromChartSelection(
-            observationsComboNestedArr,
-            extractedFormattedDatastreamProperties
-          );
-        }
-      }
-
-      if (selectedChartType === "Line") {
-        // We are interested in raw observations or aggregated observations
-
-        // Raw observations
-        if (selectedAggregationType === "None (raw data)") {
-          // Create formatted array(s) for observations
-          const formattedRawObservationsLineChartNestedArr =
-            observationsComboNestedArr.map((observationsArr) =>
-              formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
+      switch (selectedChartType) {
+        // We are interested in raw observations ONLY
+        case "Heatmap":
+          if (
+            selectedAggregationOptionsAreValid &&
+            selectedBuildingDataPointsOptionsAreValid
+          ) {
+            drawHeatmapBasedOnSelectedOptions(
+              observationsComboNestedFinalArr,
+              rawObservationsExtractedFormattedDatastreamProperties
             );
-
-          drawLineChartHighcharts(
-            formattedRawObservationsLineChartNestedArr,
-            extractedFormattedDatastreamProperties
-          );
-        }
-        // Aggregated observations
-        else {
-          drawLineChartBasedOnSelectedAggregationOptions(
-            selectedAggregationType,
-            selectedAggregationDuration,
-            observationsAggregationNestedArr,
-            selectedSamplingRateAbbrev,
-            uniqueCalendarDatesNestedArr,
-            formattedMetadataNestedArr
-          );
-        }
-      }
-
-      if (selectedChartType === "Column") {
-        // We are interested in raw observations or aggregated observations
-
-        // Raw observations
-        if (selectedAggregationType === "None (raw data)") {
-          // Create formatted array(s) for observations
-          const formattedRawObservationsColumnChartNestedArr =
-            observationsComboNestedArr.map((observationsArr) =>
-              formatSensorThingsApiResponseForLineOrColumnChart(observationsArr)
+          }
+          break;
+
+        // We are interested in raw observations ONLY
+        case "Scatter Plot":
+          if (
+            selectedAggregationOptionsAreValid &&
+            selectedBuildingDataPointsOptionsAreValid
+          ) {
+            drawScatterPlotFromChartSelection(
+              observationsComboNestedFinalArr,
+              rawObservationsExtractedFormattedDatastreamProperties
+            );
+          }
+          break;
+
+        // We are interested in BOTH raw observations and aggregated observations
+        case "Line":
+          // Raw observations
+          if (selectedAggregationType === "None (raw data)") {
+            // Create formatted array(s) for observations
+            const formattedRawObservationsLineChartNestedArr =
+              observationsComboNestedFinalArr.map((observationsArr) =>
+                formatSensorThingsApiResponseForLineOrColumnChart(
+                  observationsArr
+                )
+              );
+
+            drawLineChartHighcharts(
+              formattedRawObservationsLineChartNestedArr,
+              rawObservationsExtractedFormattedDatastreamProperties
             );
+          }
+          // Aggregated observations
+          else {
+            drawLineChartBasedOnSelectedAggregationOptions(
+              selectedAggregationType,
+              selectedAggregationDuration,
+              observationsComboNestedFinalArr,
+              selectedSamplingRateAbbrev,
+              uniqueCalendarDatesNestedArr,
+              formattedMetadataNestedArr
+            );
+          }
+          break;
+
+        // We are interested in BOTH raw observations and aggregated observations
+        case "Column":
+          // Raw observations
+          if (selectedAggregationType === "None (raw data)") {
+            // Create formatted array(s) for observations
+            const formattedRawObservationsColumnChartNestedArr =
+              observationsComboNestedFinalArr.map((observationsArr) =>
+                formatSensorThingsApiResponseForLineOrColumnChart(
+                  observationsArr
+                )
+              );
+
+            drawColumnChartHighcharts(
+              formattedRawObservationsColumnChartNestedArr,
+              rawObservationsExtractedFormattedDatastreamProperties
+            );
+          }
+          // Aggregated observations
+          else {
+            drawColumnChartBasedOnSelectedAggregationOptions(
+              selectedAggregationType,
+              selectedAggregationDuration,
+              observationsComboNestedFinalArr,
+              selectedSamplingRateAbbrev,
+              uniqueCalendarDatesNestedArr,
+              formattedMetadataNestedArr
+            );
+          }
+          break;
 
-          drawColumnChartHighcharts(
-            formattedRawObservationsColumnChartNestedArr,
-            extractedFormattedDatastreamProperties
-          );
-        }
-        // Aggregated observations
-        else {
-          drawColumnChartBasedOnSelectedAggregationOptions(
-            selectedAggregationType,
-            selectedAggregationDuration,
-            observationsAggregationNestedArr,
-            selectedSamplingRateAbbrev,
-            uniqueCalendarDatesNestedArr,
-            formattedMetadataNestedArr
-          );
-        }
+        default:
+          throw new Error("None of the chart type options were selected");
       }
     }
   } catch (err) {
     console.error(err);
   } finally {
+    // Enable the 'draw chart' button
+    enableDrawChartButton();
+
     // Hide the loading indicator
     hideLoadingSpinner();
   }
diff --git a/public/js/src_modules/aggregate.mjs b/public/js/src_modules/aggregate.mjs
index 324716daf646328d6ac8a25ef40ec38a9a799b5a..02f5998067cca546ae6434e59558a19fe5b7f6be 100644
--- a/public/js/src_modules/aggregate.mjs
+++ b/public/js/src_modules/aggregate.mjs
@@ -5,6 +5,16 @@ import {
   extractObservationValuesWithinMonthInterval,
 } from "./aggregateHelpers.mjs";
 
+/**
+ * Remove `null` observation values from an array of observation values
+ *
+ * @param {Array} obsValuesArr An array of observation values
+ * @returns {Array} An array with `null` observation values removed
+ */
+const filterOutNullObservationValues = function (obsValuesArr) {
+  return obsValuesArr.filter((obs) => obs !== null);
+};
+
 /**
  * Calculate the minimum observation values that fall within a time interval delimited by a start date and end date
  * @param {Array} obsValuesForDaysIntervalArr An array of observation values that fall within our time interval
@@ -13,7 +23,10 @@ import {
 const calculateMinimumObservationValuesWithinDatesInterval = function (
   obsValuesForDaysIntervalArr
 ) {
-  return Math.min(...obsValuesForDaysIntervalArr);
+  // If the observation value is `null`, skip the calculation
+  return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
+    obsValue === null ? null : Math.min(previousValue, obsValue)
+  );
 };
 
 /**
@@ -24,7 +37,10 @@ const calculateMinimumObservationValuesWithinDatesInterval = function (
 const calculateMaximumObservationValuesWithinDatesInterval = function (
   obsValuesForDaysIntervalArr
 ) {
-  return Math.max(...obsValuesForDaysIntervalArr);
+  // If the observation value is `null`, skip the calculation
+  return obsValuesForDaysIntervalArr.reduce((previousValue, obsValue) =>
+    obsValue === null ? null : Math.max(previousValue, obsValue)
+  );
 };
 
 /**
@@ -35,8 +51,10 @@ const calculateMaximumObservationValuesWithinDatesInterval = function (
 const calculateSumOfObservationValuesWithinDatesInterval = function (
   obsValuesForDaysIntervalArr
 ) {
-  return obsValuesForDaysIntervalArr.reduce(
-    (accumulator, obsValue) => accumulator + obsValue
+  // Remove the `null` observation values before calculating the sum
+  return filterOutNullObservationValues(obsValuesForDaysIntervalArr).reduce(
+    (previousValue, obsValue) => previousValue + obsValue,
+    0
   );
 };
 
@@ -48,10 +66,11 @@ const calculateSumOfObservationValuesWithinDatesInterval = function (
 const calculateAverageOfObservationValuesWithinDatesInterval = function (
   obsValuesForDaysIntervalArr
 ) {
+  // The observation values array should only include non-`null` values
   return (
     calculateSumOfObservationValuesWithinDatesInterval(
       obsValuesForDaysIntervalArr
-    ) / obsValuesForDaysIntervalArr.length
+    ) / filterOutNullObservationValues(obsValuesForDaysIntervalArr).length
   );
 };
 
@@ -63,7 +82,10 @@ const calculateAverageOfObservationValuesWithinDatesInterval = function (
 const calculateMinimumObservationValuesWithinMonthInterval = function (
   obsValuesForMonthIntervalArr
 ) {
-  return Math.min(...obsValuesForMonthIntervalArr);
+  // If the observation value is `null`, skip the calculation
+  return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
+    obsValue === null ? null : Math.min(previousValue, obsValue)
+  );
 };
 
 /**
@@ -74,7 +96,10 @@ const calculateMinimumObservationValuesWithinMonthInterval = function (
 const calculateMaximumObservationValuesWithinMonthInterval = function (
   obsValuesForMonthIntervalArr
 ) {
-  return Math.max(...obsValuesForMonthIntervalArr);
+  // If the observation value is `null`, skip the calculation
+  return obsValuesForMonthIntervalArr.reduce((previousValue, obsValue) =>
+    obsValue === null ? null : Math.max(previousValue, obsValue)
+  );
 };
 
 /**
@@ -85,8 +110,10 @@ const calculateMaximumObservationValuesWithinMonthInterval = function (
 const calculateSumOfObservationValuesWithinMonthInterval = function (
   obsValuesForMonthIntervalArr
 ) {
-  return obsValuesForMonthIntervalArr.reduce(
-    (accumulator, obsValue) => accumulator + obsValue
+  // Remove the `null` observation values before calculating the sum
+  return filterOutNullObservationValues(obsValuesForMonthIntervalArr).reduce(
+    (previousValue, obsValue) => previousValue + obsValue,
+    0
   );
 };
 
@@ -98,10 +125,11 @@ const calculateSumOfObservationValuesWithinMonthInterval = function (
 const calculateAverageOfObservationValuesWithinMonthInterval = function (
   obsValuesForMonthIntervalArr
 ) {
+  // The observation values array should only include non-`null` values
   return (
     calculateSumOfObservationValuesWithinMonthInterval(
       obsValuesForMonthIntervalArr
-    ) / obsValuesForMonthIntervalArr.length
+    ) / filterOutNullObservationValues(obsValuesForMonthIntervalArr).length
   );
 };
 
diff --git a/public/js/src_modules/chartColumn.mjs b/public/js/src_modules/chartColumn.mjs
index 8c6de145fc19df5f711ea814670f90fc88e72428..35fc3d5203968d2f5f2e1338f99fad622f3771e7 100644
--- a/public/js/src_modules/chartColumn.mjs
+++ b/public/js/src_modules/chartColumn.mjs
@@ -131,10 +131,12 @@ const drawColumnChartHighcharts = function (
 
     title: {
       text: textChartTitle,
+      "align": "center",
     },
 
     subtitle: {
       text: textChartSubtitle,
+      "align": "center",
     },
 
     xAxis: {
@@ -143,7 +145,6 @@ const drawColumnChartHighcharts = function (
     },
 
     yAxis: {
-      min: 0,
       title: {
         text: `${phenomenonName} [${unitOfMeasurementSymbol}]`,
       },
@@ -155,10 +156,10 @@ const drawColumnChartHighcharts = function (
         // this.x -- common for all points
         // this.points -- an array containing properties for each series
         // Note that our `reduce` method is in this format:
-        // ((accumulator, currentValue, currentIndex) => {...}, initialValue)
+        // ((previousValue, currentValue, currentIndex) => {...}, initialValue)
         return this.points.reduce(
-          (accumulator, point, i) =>
-            `${accumulator} <br/> <span style="color:${point.color}">${
+          (previousValue, point, i) =>
+            `${previousValue} <br/> <span style="color:${point.color}">${
               point.series.name
             }</span>: <b>${point.y.toFixed(2)} ${
               unitOfMeasurementSymbolsArr[i]
diff --git a/public/js/src_modules/chartHeatmap.mjs b/public/js/src_modules/chartHeatmap.mjs
index 69d77f55038a4342d778596988377d3efebc5553..87884e0af04ab2f6e1124e33ac437c6a9ff942ab 100644
--- a/public/js/src_modules/chartHeatmap.mjs
+++ b/public/js/src_modules/chartHeatmap.mjs
@@ -48,7 +48,8 @@ const calculateMinMaxValuesForHeatmapColorAxis = function (
   const maxValue = Math.trunc(Math.max(...obsValueArr));
 
   // Calculate the closest multiple of 5
-  const minObsValue = minValue - (minValue % 5);
+  const minObsValue =
+    minValue > 0 ? minValue - (minValue % 5) : minValue + (minValue % 5);
   const maxObsValue = maxValue + (5 - (maxValue % 5));
 
   return { minObsValue, maxObsValue };
@@ -98,13 +99,13 @@ const drawHeatMapHighcharts = function (
 
     title: {
       text: TEXT_CHART_TITLE,
-      align: "left",
+      align: "center",
       x: 40,
     },
 
     subtitle: {
       text: TEXT_CHART_SUBTITLE,
-      align: "left",
+      align: "center",
       x: 40,
     },
 
diff --git a/public/js/src_modules/chartLine.mjs b/public/js/src_modules/chartLine.mjs
index ba9a2e157d65af2f5aee6da8379e4d027f57cd6f..2538662fd06e9f6e517d4cc5a89b52f57564e000 100644
--- a/public/js/src_modules/chartLine.mjs
+++ b/public/js/src_modules/chartLine.mjs
@@ -134,12 +134,12 @@ const drawLineChartHighcharts = function (
 
     title: {
       text: textChartTitle,
-      "align": "left",
+      "align": "center",
     },
 
     subtitle: {
       text: textChartSubtitle,
-      align: "left",
+      align: "center",
     },
 
     tooltip: {
diff --git a/public/js/src_modules/chartScatterPlot.mjs b/public/js/src_modules/chartScatterPlot.mjs
index 0cc2bdb3ceaffe731cdc0922e08f7cd038c02efc..e84e6118f365ecbb91b62defe8d9838c10dea958 100644
--- a/public/js/src_modules/chartScatterPlot.mjs
+++ b/public/js/src_modules/chartScatterPlot.mjs
@@ -231,12 +231,12 @@ const drawScatterPlotHighcharts = function (
 
     title: {
       text: CHART_TITLE,
-      "align": "left",
+      "align": "center",
     },
 
     subtitle: {
       text: CHART_SUBTITLE,
-      "align": "left",
+      "align": "center",
     },
 
     xAxis: {
diff --git a/public/js/src_modules/dropDownListHelpers.mjs b/public/js/src_modules/dropDownListHelpers.mjs
index f087cdd06569cbedcc16a9940e250bf018f70678..b505d7e198d17c8eacb630f6ac737ef89e442204 100644
--- a/public/js/src_modules/dropDownListHelpers.mjs
+++ b/public/js/src_modules/dropDownListHelpers.mjs
@@ -1,3 +1,5 @@
+"use strict";
+
 /**
  * Get the selected option(s) from a dropdown list
  *
diff --git a/public/js/src_modules/dropDownListProcessing.mjs b/public/js/src_modules/dropDownListProcessing.mjs
index 47715ca69cbd55c46012d6b032b02742e309cd28..123db15692211ef5bd3ae1f03bc595156d0232e5 100644
--- a/public/js/src_modules/dropDownListProcessing.mjs
+++ b/public/js/src_modules/dropDownListProcessing.mjs
@@ -1,3 +1,5 @@
+"use strict";
+
 import { drawLineChartHighcharts } from "./chartLine.mjs";
 
 import {
diff --git a/public/js/src_modules/loadingIndicator.mjs b/public/js/src_modules/loadingIndicator.mjs
index c8465f1339fe71c88fc3c7936b3174ebfe1cee5f..c1964b2c02c2d2e713c26f5d1b35b87bcda86fb6 100644
--- a/public/js/src_modules/loadingIndicator.mjs
+++ b/public/js/src_modules/loadingIndicator.mjs
@@ -2,7 +2,7 @@
 
 /**
  * Show a loading indicator at the start of an async task. The indicator consists of a spinner and a transluscent mask placed on top of page elements
- * @returns {undefined}
+ * @returns {undefined} undefined
  */
 const showLoadingSpinner = function () {
   const loadingIndicatorMask = document.querySelector("#loadingIndicator");
@@ -14,7 +14,7 @@ const showLoadingSpinner = function () {
 
 /**
  * Hide the loading indicator after completion of the async tasks
- * @returns {undefined}
+ * @returns {undefined} undefined
  */
 const hideLoadingSpinner = function () {
   const loadingIndicatorMask = document.querySelector("#loadingIndicator");
@@ -24,4 +24,27 @@ const hideLoadingSpinner = function () {
   loadingIconSpinner.style.display = "none";
 };
 
-export { showLoadingSpinner, hideLoadingSpinner };
+/**
+ * Disable the button used to trigger the drawing of charts
+ *
+ * @returns {undefined} undefined
+ */
+const disableDrawChartButton = function () {
+  document.querySelector("#btn-draw-chart").disabled = true;
+};
+
+/**
+ * Enable the button used to trigger the drawing of charts
+ *
+ * @returns {undefined} undefined
+ */
+const enableDrawChartButton = function () {
+  document.querySelector("#btn-draw-chart").disabled = false;
+};
+
+export {
+  showLoadingSpinner,
+  hideLoadingSpinner,
+  disableDrawChartButton,
+  enableDrawChartButton,
+};
diff --git a/public/js/thirdparty/scripts.js b/public/js/thirdparty/scripts.js
index 0b2fd5c5ed6032910542854db6b28d81dfa80be8..82e16ea4385b253a0567468f7e29f478c7080bf2 100644
--- a/public/js/thirdparty/scripts.js
+++ b/public/js/thirdparty/scripts.js
@@ -1,22 +1,27 @@
 /*!
-    * Start Bootstrap - SB Admin v6.0.2 (https://startbootstrap.com/template/sb-admin)
-    * Copyright 2013-2020 Start Bootstrap
-    * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
-    */
-    (function($) {
-    "use strict";
+ * Start Bootstrap - SB Admin v7.0.3 (https://startbootstrap.com/template/sb-admin)
+ * Copyright 2013-2021 Start Bootstrap
+ * Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-sb-admin/blob/master/LICENSE)
+ */
+//
+// Scripts
+//
 
-    // Add active state to sidbar nav links
-    var path = window.location.href; // because the 'href' property of the DOM element is the absolute path
-        $("#layoutSidenav_nav .sb-sidenav a.nav-link").each(function() {
-            if (this.href === path) {
-                $(this).addClass("active");
-            }
-        });
-
-    // Toggle the side navigation
-    $("#sidebarToggle").on("click", function(e) {
-        e.preventDefault();
-        $("body").toggleClass("sb-sidenav-toggled");
+window.addEventListener("DOMContentLoaded", (event) => {
+  // Toggle the side navigation
+  const sidebarToggle = document.body.querySelector("#sidebarToggle");
+  if (sidebarToggle) {
+    // Uncomment Below to persist sidebar toggle between refreshes
+    // if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
+    //     document.body.classList.toggle('sb-sidenav-toggled');
+    // }
+    sidebarToggle.addEventListener("click", (event) => {
+      event.preventDefault();
+      document.body.classList.toggle("sb-sidenav-toggled");
+      localStorage.setItem(
+        "sb|sidebar-toggle",
+        document.body.classList.contains("sb-sidenav-toggled")
+      );
     });
-})(jQuery);
+  }
+});