From c0402132b18c1ed94c3c4d6479129d2ecbe8940d Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Wed, 14 Jan 2026 13:50:54 -0500
Subject: [PATCH 01/16] add little disclaimer
---
app/src/components/DataVisualizationContainer.jsx | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/app/src/components/DataVisualizationContainer.jsx b/app/src/components/DataVisualizationContainer.jsx
index 2b31d5d..a3b1a91 100644
--- a/app/src/components/DataVisualizationContainer.jsx
+++ b/app/src/components/DataVisualizationContainer.jsx
@@ -297,6 +297,7 @@ const DataVisualizationContainer = () => {
loading={loading}
/>
+
)}
{windowSize.width > 800 && (
@@ -350,6 +351,17 @@ const DataVisualizationContainer = () => {
availablePeakModels={availablePeakModels}
/>
+
+
+ forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+
+
From ac9f9fc5b2ded736b260b2307be8e6bea5cbede2 Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Wed, 14 Jan 2026 14:17:17 -0500
Subject: [PATCH 02/16] turn off irregular whitespace check in lint
(we use an intentional irregular whitespace in InfoOverlay.jsx)
---
app/eslint.config.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/eslint.config.js b/app/eslint.config.js
index ff64412..0053866 100644
--- a/app/eslint.config.js
+++ b/app/eslint.config.js
@@ -35,6 +35,7 @@ export default [
],
'react/prop-types': 'off',
'react/no-unescaped-entities': 'off',
+ "no-irregular-whitespace": "off",
},
},
]
From aed5c7a060e0db7952f204a689da3a3a8609fdce Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Wed, 14 Jan 2026 16:49:13 -0500
Subject: [PATCH 03/16] move disclaimer
---
app/src/components/COVID19View.jsx | 11 +++++++++++
app/src/components/DataVisualizationContainer.jsx | 11 -----------
app/src/components/FluPeak.jsx | 11 +++++++++++
app/src/components/FluView.jsx | 11 +++++++++++
app/src/components/RSVView.jsx | 11 +++++++++++
5 files changed, 44 insertions(+), 11 deletions(-)
diff --git a/app/src/components/COVID19View.jsx b/app/src/components/COVID19View.jsx
index be61ce6..03163e9 100644
--- a/app/src/components/COVID19View.jsx
+++ b/app/src/components/COVID19View.jsx
@@ -300,6 +300,17 @@ const COVID19View = ({ data, metadata, selectedDates, selectedModels, models, se
onRelayout={handlePlotUpdate}
/>
+
+
+ forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+
+
{
availablePeakModels={availablePeakModels}
/>
-
-
- forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
-
-
diff --git a/app/src/components/FluPeak.jsx b/app/src/components/FluPeak.jsx
index cdcf2df..647ca39 100644
--- a/app/src/components/FluPeak.jsx
+++ b/app/src/components/FluPeak.jsx
@@ -332,6 +332,17 @@ const FluPeak = ({
useResizeHandler={true}
/>
+
+
+ forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+
+
handlePlotUpdate(figure)}
/>
+
+
+ forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+
+
handlePlotUpdate(figure)}
/>
+
+
+ forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+
+
Date: Wed, 14 Jan 2026 17:21:02 -0500
Subject: [PATCH 04/16] move disclaimer
---
app/src/components/COVID19View.jsx | 4 ++--
app/src/components/FluPeak.jsx | 4 ++--
app/src/components/FluView.jsx | 4 ++--
app/src/components/RSVView.jsx | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/app/src/components/COVID19View.jsx b/app/src/components/COVID19View.jsx
index 03163e9..8daa93e 100644
--- a/app/src/components/COVID19View.jsx
+++ b/app/src/components/COVID19View.jsx
@@ -305,10 +305,10 @@ const COVID19View = ({ data, metadata, selectedDates, selectedModels, models, se
fontStyle: 'italic',
fontSize: '12px',
color: '#868e96',
- textAlign: 'center',
+ textAlign: 'right',
margin: 0
}}>
- forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+ Note that forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
- forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+ Note that forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
diff --git a/app/src/components/FluView.jsx b/app/src/components/FluView.jsx
index bc4c8c4..a33f5da 100644
--- a/app/src/components/FluView.jsx
+++ b/app/src/components/FluView.jsx
@@ -412,10 +412,10 @@ const FluView = ({ data, metadata, selectedDates, selectedModels, models, setSel
fontStyle: 'italic',
fontSize: '12px',
color: '#868e96',
- textAlign: 'center',
+ textAlign: 'right',
margin: 0
}}>
- forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+ Note that forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
- forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
+ Note that forecasts should be interpreted with great caution and may not reliably predict rapid changes in disease trends.
Date: Thu, 15 Jan 2026 13:50:37 -0500
Subject: [PATCH 05/16] ci for hosp burden
---
app/src/components/FluPeak.jsx | 112 ++++++++++++++++++++++++++-------
1 file changed, 91 insertions(+), 21 deletions(-)
diff --git a/app/src/components/FluPeak.jsx b/app/src/components/FluPeak.jsx
index 647ca39..2ff71c5 100644
--- a/app/src/components/FluPeak.jsx
+++ b/app/src/components/FluPeak.jsx
@@ -190,10 +190,20 @@ const FluPeak = ({
const intensityData = dateData['peak inc flu hosp']?.[model];
if (!intensityData || !intensityData.predictions) return;
+ // extract confidence intervals
const iPreds = intensityData.predictions;
- const qIdx05 = iPreds.quantiles.indexOf(0.5);
- if (qIdx05 === -1) return;
- const medianVal = iPreds.values[qIdx05];
+ const getVal = (q) => {
+ const idx = iPreds.quantiles.indexOf(q);
+ return idx !== -1 ? iPreds.values[idx] : null;
+ };
+
+ const medianVal = getVal(0.5);
+ const low95 = getVal(0.025);
+ const high95 = getVal(0.975);
+ const low50 = getVal(0.25);
+ const high50 = getVal(0.75);
+
+ if (medianVal === null) return;
const timingData = dateData['peak week inc flu hosp']?.[model];
if (!timingData || !timingData.predictions) return;
@@ -204,18 +214,23 @@ const FluPeak = ({
let bestDateStr = null;
if (dateArray && probArray) {
- let maxProb = -1;
- let maxIdx = -1;
- probArray.forEach((p, i) => {
- if (p > maxProb) { maxProb = p; maxIdx = i; }
- });
- if (maxIdx !== -1) bestDateStr = dateArray[maxIdx];
+ let cumulativeProb = 0;
+ let medianIdx = -1;
+ for (let i = 0; i < probArray.length; i++) {
+ cumulativeProb += probArray[i];
+ if (cumulativeProb >= 0.5) {
+ medianIdx = i;
+ break;
+ }
+ }
+ if (medianIdx === -1) medianIdx = probArray.length - 1;
+ bestDateStr = dateArray[medianIdx];
} else if (dateArray && dateArray.length > 0) {
bestDateStr = dateArray[Math.floor(dateArray.length / 2)];
}
-
if (!bestDateStr) return;
+ const normalizedDate = getNormalizedDate(bestDateStr);
// Gradient Opacity Calculation
const minOpacity = 0.4;
const alpha = datesToCheck.length === 1
@@ -224,20 +239,71 @@ const FluPeak = ({
const dynamicColor = hexToRgba(baseColorHex, alpha);
+ // 95% interval (thin line)
+ if (low95 !== null && high95 !== null) {
+ traces.push({
+ x: [normalizedDate, normalizedDate],
+ y: [low95, high95],
+ mode: 'lines+markers',
+ line: {
+ color: dynamicColor,
+ width: 1,
+ dash: 'dash'
+ },
+ marker: {
+ symbol: 'line-ew',
+ color: dynamicColor,
+ size: 10,
+ line: {
+ width: 1,
+ color: dynamicColor
+ }
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
+
+ // 50% interval (thick line)
+ if (low50 !== null && high50 !== null) {
+ traces.push({
+ x: [normalizedDate, normalizedDate],
+ y: [low50, high50],
+ mode: 'lines',
+ line: {
+ color: dynamicColor,
+ width: 4,
+ dash: '6px, 3px'
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
+
xValues.push(getNormalizedDate(bestDateStr));
yValues.push(medianVal);
pointColors.push(dynamicColor);
+ const formattedMedian = Math.round(medianVal).toLocaleString();
+ const formatted50 = `${Math.round(low50).toLocaleString()} - ${Math.round(high50).toLocaleString()}`;
+ const formatted95 = `${Math.round(low95).toLocaleString()} - ${Math.round(high95).toLocaleString()}`;
+
hoverTexts.push(
`${model}
` +
- `peak week: ${bestDateStr}
` +
- `peak hosp: ${Math.round(medianVal).toLocaleString()}
` +
- `predicted as of ${refDate}`
+ `Median Peak Week: ${bestDateStr}
` +
+ `` +
+ `Peak Hospitalization Burden:
` +
+ `Median: ${formattedMedian}
` +
+ `50% CI: [${formatted50}]
` +
+ `95% CI: [${formatted95}]
` +
+ `predicted as of ${refDate}`
);
});
+ // actual trace
if (xValues.length > 0) {
- // ACTUAL DATA TRACE (Gradient colors, Hidden from Legend)
traces.push({
x: xValues,
y: yValues,
@@ -246,27 +312,31 @@ const FluPeak = ({
mode: 'markers',
marker: {
color: pointColors,
- size: 10,
- symbol: 'diamond',
+ size: 12,
+ symbol: 'circle',
line: { width: 1, color: 'white' }
},
+ hoverlabel: {
+ font: { color: '#ffffff' },
+ bordercolor: '#ffffff' // maakes border white
+ },
hovertemplate: '%{text}',
text: hoverTexts,
showlegend: false,
legendgroup: model
});
- // DUMMY LEGEND TRACE (Solid color, Visible in Legend)
+ // dummy legend
traces.push({
- x: [null], // No data
+ x: [null],
y: [null],
name: model,
type: 'scatter',
mode: 'markers',
marker: {
color: baseColorHex,
- size: 10,
- symbol: 'diamond',
+ size: 12,
+ symbol: 'circle',
line: { width: 1, color: 'white' }
},
showlegend: true,
@@ -277,7 +347,7 @@ const FluPeak = ({
}
return traces;
- }, [groundTruth, nhsnData, peaks, selectedModels, selectedDates, peakDates, colorScheme]);
+ }, [groundTruth, nhsnData, peaks, selectedModels, selectedDates, peakDates]);
const layout = useMemo(() => ({
width: windowSize ? Math.min(CHART_CONSTANTS.MAX_WIDTH, windowSize.width * CHART_CONSTANTS.WIDTH_RATIO) : undefined,
From 851ba02e09fb4df669493779a48db5d69373b06d Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Fri, 16 Jan 2026 08:51:38 -0500
Subject: [PATCH 06/16] ci for dates
---
app/src/components/FluPeak.jsx | 79 +++++++++++++++++++++++++++++-----
1 file changed, 68 insertions(+), 11 deletions(-)
diff --git a/app/src/components/FluPeak.jsx b/app/src/components/FluPeak.jsx
index 2ff71c5..d2921ec 100644
--- a/app/src/components/FluPeak.jsx
+++ b/app/src/components/FluPeak.jsx
@@ -213,18 +213,30 @@ const FluPeak = ({
const probArray = tPreds['probabilities'];
let bestDateStr = null;
+ let lowDate95 = null, highDate95 = null;
+ let lowDate50 = null, highDate50 = null;
+
if (dateArray && probArray) {
let cumulativeProb = 0;
- let medianIdx = -1;
+ let medianIdx = -1, q025Idx = -1, q975Idx = -1, q25Idx = -1, q75Idx = -1;
for (let i = 0; i < probArray.length; i++) {
cumulativeProb += probArray[i];
- if (cumulativeProb >= 0.5) {
- medianIdx = i;
- break;
- }
+
+ if (q025Idx === -1 && cumulativeProb >= 0.025) q025Idx = i;
+ if (q25Idx === -1 && cumulativeProb >= 0.25) q25Idx = i;
+ if (medianIdx === -1 && cumulativeProb >= 0.5) medianIdx = i;
+ if (q75Idx === -1 && cumulativeProb >= 0.75) q75Idx = i;
+ if (q975Idx === -1 && cumulativeProb >= 0.975) q975Idx = i;
}
- if (medianIdx === -1) medianIdx = probArray.length - 1;
+ if (medianIdx === -1) medianIdx = probArray.length - 1;
+ if (q975Idx === -1) q975Idx = probArray.length - 1;
+ if (q75Idx === -1) q75Idx = probArray.length - 1;
+
bestDateStr = dateArray[medianIdx];
+ lowDate95 = dateArray[q025Idx !== -1 ? q025Idx : 0];
+ highDate95 = dateArray[q975Idx];
+ lowDate50 = dateArray[q25Idx !== -1 ? q25Idx : 0];
+ highDate50 = dateArray[q75Idx];
} else if (dateArray && dateArray.length > 0) {
bestDateStr = dateArray[Math.floor(dateArray.length / 2)];
}
@@ -239,7 +251,7 @@ const FluPeak = ({
const dynamicColor = hexToRgba(baseColorHex, alpha);
- // 95% interval (thin line)
+ // 95% vertical whisker (hosp)
if (low95 !== null && high95 !== null) {
traces.push({
x: [normalizedDate, normalizedDate],
@@ -265,7 +277,7 @@ const FluPeak = ({
});
}
- // 50% interval (thick line)
+ // 50% vertical whisker (hosp)
if (low50 !== null && high50 !== null) {
traces.push({
x: [normalizedDate, normalizedDate],
@@ -282,19 +294,64 @@ const FluPeak = ({
});
}
+ // 95% horizontal whisker (dates)
+ if (lowDate95 && highDate95) {
+ traces.push({
+ x: [getNormalizedDate(lowDate95), getNormalizedDate(highDate95)],
+ y: [medianVal, medianVal],
+ mode: 'lines+markers',
+ line: {
+ color: dynamicColor,
+ width: 1,
+ dash: 'dash'
+ },
+ marker: {
+ symbol: 'line-ns',
+ color: dynamicColor,
+ size: 10,
+ line: { width: 1, color: dynamicColor }
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
+
+ // 50% horizontal whisker (dates)
+ if (lowDate50 && highDate50) {
+ traces.push({
+ x: [getNormalizedDate(lowDate50), getNormalizedDate(highDate50)],
+ y: [medianVal, medianVal],
+ mode: 'lines',
+ line: {
+ color: dynamicColor,
+ width: 4,
+ dash: '6px, 3px'
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
+
xValues.push(getNormalizedDate(bestDateStr));
yValues.push(medianVal);
pointColors.push(dynamicColor);
-
+
+ const timing50 = `${lowDate50} - ${highDate50}`;
+ const timing95 = `${lowDate95} - ${highDate95}`;
const formattedMedian = Math.round(medianVal).toLocaleString();
const formatted50 = `${Math.round(low50).toLocaleString()} - ${Math.round(high50).toLocaleString()}`;
const formatted95 = `${Math.round(low95).toLocaleString()} - ${Math.round(high95).toLocaleString()}`;
hoverTexts.push(
`${model}
` +
- `Median Peak Week: ${bestDateStr}
` +
+ `Peak timing:
` +
+ `Median Week: ${bestDateStr}
` +
+ `50% CI: [${timing50}]
` +
+ `95% CI: [${timing95}]
` +
`` +
- `Peak Hospitalization Burden:
` +
+ `Peak hospitalization:
` +
`Median: ${formattedMedian}
` +
`50% CI: [${formatted50}]
` +
`95% CI: [${formatted95}]
` +
From 5b959c7fac729d948a56069461dd0a71b05f53aa Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Fri, 16 Jan 2026 08:59:17 -0500
Subject: [PATCH 07/16] Bold median hosp
---
app/src/components/FluPeak.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/components/FluPeak.jsx b/app/src/components/FluPeak.jsx
index d2921ec..c88b186 100644
--- a/app/src/components/FluPeak.jsx
+++ b/app/src/components/FluPeak.jsx
@@ -352,7 +352,7 @@ const FluPeak = ({
`95% CI: [${timing95}]
` +
`` +
`Peak hospitalization:
` +
- `Median: ${formattedMedian}
` +
+ `Median: ${formattedMedian}
` +
`50% CI: [${formatted50}]
` +
`95% CI: [${formatted95}]
` +
`predicted as of ${refDate}`
From acf9b8bbc3d1f2f14af9e837eda0f6bd29449e4e Mon Sep 17 00:00:00 2001
From: Emily Przykucki
Date: Fri, 16 Jan 2026 09:56:39 -0500
Subject: [PATCH 08/16] add switch to toggle uncertainty intervals on/off
---
app/src/components/FluPeak.jsx | 170 +++++++++++++++++----------------
1 file changed, 90 insertions(+), 80 deletions(-)
diff --git a/app/src/components/FluPeak.jsx b/app/src/components/FluPeak.jsx
index c88b186..ad07d5f 100644
--- a/app/src/components/FluPeak.jsx
+++ b/app/src/components/FluPeak.jsx
@@ -1,5 +1,5 @@
import { useState, useEffect, useMemo } from 'react';
-import { Stack, useMantineColorScheme } from '@mantine/core';
+import { Stack, useMantineColorScheme, Switch, Group } from '@mantine/core';
import Plot from 'react-plotly.js';
import ModelSelector from './ModelSelector';
import { MODEL_COLORS } from '../config/datasets';
@@ -34,6 +34,7 @@ const FluPeak = ({
const { colorScheme } = useMantineColorScheme();
const groundTruth = data?.ground_truth;
const [nhsnData, setNhsnData] = useState(null);
+ const [showUncertainty, setShowUncertainty] = useState(true);
const getNormalizedDate = (dateStr) => {
const d = new Date(dateStr);
@@ -250,90 +251,91 @@ const FluPeak = ({
: minOpacity + ((index / (datesToCheck.length - 1)) * (1 - minOpacity));
const dynamicColor = hexToRgba(baseColorHex, alpha);
-
- // 95% vertical whisker (hosp)
- if (low95 !== null && high95 !== null) {
- traces.push({
- x: [normalizedDate, normalizedDate],
- y: [low95, high95],
- mode: 'lines+markers',
- line: {
- color: dynamicColor,
- width: 1,
- dash: 'dash'
- },
- marker: {
- symbol: 'line-ew',
- color: dynamicColor,
- size: 10,
+
+ if (showUncertainty) {
+ // 95% vertical whisker (hosp)
+ if (low95 !== null && high95 !== null) {
+ traces.push({
+ x: [normalizedDate, normalizedDate],
+ y: [low95, high95],
+ mode: 'lines+markers',
line: {
+ color: dynamicColor,
width: 1,
- color: dynamicColor
- }
- },
- legendgroup: model,
- showlegend: false,
- hoverinfo: 'skip'
- });
- }
+ dash: 'dash'
+ },
+ marker: {
+ symbol: 'line-ew',
+ color: dynamicColor,
+ size: 10,
+ line: {
+ width: 1,
+ color: dynamicColor
+ }
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
- // 50% vertical whisker (hosp)
- if (low50 !== null && high50 !== null) {
- traces.push({
- x: [normalizedDate, normalizedDate],
- y: [low50, high50],
- mode: 'lines',
- line: {
- color: dynamicColor,
- width: 4,
- dash: '6px, 3px'
- },
- legendgroup: model,
- showlegend: false,
- hoverinfo: 'skip'
- });
- }
+ // 50% vertical whisker (hosp)
+ if (low50 !== null && high50 !== null) {
+ traces.push({
+ x: [normalizedDate, normalizedDate],
+ y: [low50, high50],
+ mode: 'lines',
+ line: {
+ color: dynamicColor,
+ width: 4,
+ dash: '6px, 3px'
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
- // 95% horizontal whisker (dates)
- if (lowDate95 && highDate95) {
- traces.push({
- x: [getNormalizedDate(lowDate95), getNormalizedDate(highDate95)],
- y: [medianVal, medianVal],
- mode: 'lines+markers',
- line: {
- color: dynamicColor,
- width: 1,
- dash: 'dash'
- },
- marker: {
- symbol: 'line-ns',
- color: dynamicColor,
- size: 10,
- line: { width: 1, color: dynamicColor }
- },
- legendgroup: model,
- showlegend: false,
- hoverinfo: 'skip'
- });
- }
+ // 95% horizontal whisker (dates)
+ if (lowDate95 && highDate95) {
+ traces.push({
+ x: [getNormalizedDate(lowDate95), getNormalizedDate(highDate95)],
+ y: [medianVal, medianVal],
+ mode: 'lines+markers',
+ line: {
+ color: dynamicColor,
+ width: 1,
+ dash: 'dash'
+ },
+ marker: {
+ symbol: 'line-ns',
+ color: dynamicColor,
+ size: 10,
+ line: { width: 1, color: dynamicColor }
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
- // 50% horizontal whisker (dates)
- if (lowDate50 && highDate50) {
- traces.push({
- x: [getNormalizedDate(lowDate50), getNormalizedDate(highDate50)],
- y: [medianVal, medianVal],
- mode: 'lines',
- line: {
- color: dynamicColor,
- width: 4,
- dash: '6px, 3px'
- },
- legendgroup: model,
- showlegend: false,
- hoverinfo: 'skip'
- });
+ // 50% horizontal whisker (dates)
+ if (lowDate50 && highDate50) {
+ traces.push({
+ x: [getNormalizedDate(lowDate50), getNormalizedDate(highDate50)],
+ y: [medianVal, medianVal],
+ mode: 'lines',
+ line: {
+ color: dynamicColor,
+ width: 4,
+ dash: '6px, 3px'
+ },
+ legendgroup: model,
+ showlegend: false,
+ hoverinfo: 'skip'
+ });
+ }
}
-
xValues.push(getNormalizedDate(bestDateStr));
yValues.push(medianVal);
pointColors.push(dynamicColor);
@@ -404,7 +406,7 @@ const FluPeak = ({
}
return traces;
- }, [groundTruth, nhsnData, peaks, selectedModels, selectedDates, peakDates]);
+ }, [groundTruth, nhsnData, peaks, selectedModels, selectedDates, peakDates, showUncertainty]);
const layout = useMemo(() => ({
width: windowSize ? Math.min(CHART_CONSTANTS.MAX_WIDTH, windowSize.width * CHART_CONSTANTS.WIDTH_RATIO) : undefined,
@@ -471,6 +473,14 @@ const FluPeak = ({
+
+ setShowUncertainty(event.currentTarget.checked)}
+ size="sm"
+ />
+
Date: Fri, 16 Jan 2026 11:57:19 -0500
Subject: [PATCH 09/16] remove multi date select buttons on flu peak view
---
.../components/DataVisualizationContainer.jsx | 11 ++++++++
app/src/components/DateSelector.jsx | 26 ++++++++++---------
2 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/app/src/components/DataVisualizationContainer.jsx b/app/src/components/DataVisualizationContainer.jsx
index 723ed2b..502e1ef 100644
--- a/app/src/components/DataVisualizationContainer.jsx
+++ b/app/src/components/DataVisualizationContainer.jsx
@@ -244,6 +244,15 @@ const DataVisualizationContainer = () => {
const url = window.location.href;
clipboard.copy(url);
};
+
+ // for when switching from one flu view to flu_peak and there are multiple dates selected
+ useEffect(() => {
+ if (viewType === 'flu_peak' && selectedDates.length > 1) {
+ // keep only the active date, or the first date if activeDate isn't set
+ const singleDate = activeDate || selectedDates[0];
+ setSelectedDates([singleDate]);
+ }
+ }, [viewType, selectedDates, activeDate, setSelectedDates]);
return (
window.location.reload()}>
@@ -295,6 +304,7 @@ const DataVisualizationContainer = () => {
activeDate={activeDate}
setActiveDate={setActiveDate}
loading={loading}
+ multi={viewType !== 'flu_peak'}
/>
@@ -322,6 +332,7 @@ const DataVisualizationContainer = () => {
activeDate={activeDate}
setActiveDate={setActiveDate}
loading={loading}
+ multi={viewType !== 'flu_peak'} //disable multi date select if flu peak
/>
)}
diff --git a/app/src/components/DateSelector.jsx b/app/src/components/DateSelector.jsx
index dac3328..8f40bbe 100644
--- a/app/src/components/DateSelector.jsx
+++ b/app/src/components/DateSelector.jsx
@@ -1,7 +1,7 @@
import { Group, Text, ActionIcon, Button } from '@mantine/core';
import { IconChevronLeft, IconChevronRight, IconX, IconPlus } from '@tabler/icons-react';
-const DateSelector = ({ availableDates, selectedDates, setSelectedDates, activeDate, setActiveDate }) => {
+const DateSelector = ({ availableDates, selectedDates, setSelectedDates, activeDate, setActiveDate, multi = true }) => {
return (
{selectedDates.map((date) => (
@@ -43,16 +43,18 @@ const DateSelector = ({ availableDates, selectedDates, setSelectedDates, activeD
>
{date}
- setSelectedDates(dates => dates.filter(d => d !== date))}
- disabled={selectedDates.length === 1}
- variant="subtle"
- size="xs"
- color="red"
- aria-label={`Remove date ${date}`}
- >
-
-
+ {multi && ( // only show `x` icon when multi == True
+ setSelectedDates(dates => dates.filter(d => d !== date))}
+ disabled={selectedDates.length === 1}
+ variant="subtle"
+ size="xs"
+ color="red"
+ aria-label={`Remove date ${date}`}
+ >
+
+
+ )}
))}
- {selectedDates.length < 5 && (
+ {multi && selectedDates.length < 5 && ( // only show add button if multi == True