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