Weather Station / ECOWITT / DNT
0

Configure Feed

Select the types of activity you want to include in your feed.

stat multiselect

+44 -35
+44 -35
src/components/Statistics.tsx
··· 252 252 const [data, setData] = useState<StatisticsPayload | null>(null); 253 253 const [loading, setLoading] = useState(true); 254 254 const [error, setError] = useState<string | null>(null); 255 - const [selectedYear, setSelectedYear] = useState<number | null>(null); 255 + const [selectedYears, setSelectedYears] = useState<number[]>([]); 256 256 257 257 useEffect(() => { 258 258 const ac = new AbortController(); ··· 298 298 return (data?.years || []).slice().sort((a, b) => (b?.year ?? 0) - (a?.year ?? 0)); 299 299 }, [data?.years]); 300 300 301 - // Default selection to latest year when data arrives 301 + // Default selection to latest year when data arrives. 302 302 useEffect(() => { 303 - if (!selectedYear && yearsSorted.length > 0) { 304 - setSelectedYear(yearsSorted[0].year); 303 + if (selectedYears.length === 0 && yearsSorted.length > 0) { 304 + setSelectedYears([yearsSorted[0].year]); 305 305 } 306 - }, [yearsSorted, selectedYear]); 306 + }, [yearsSorted, selectedYears.length]); 307 307 308 - const currentYearStats = useMemo(() => { 309 - if (yearsSorted.length === 0) return null; 310 - if (selectedYear == null) return yearsSorted[0]; 311 - return yearsSorted.find((y) => y.year === selectedYear) || yearsSorted[0]; 312 - }, [yearsSorted, selectedYear]); 308 + const selectedYearStats = useMemo(() => { 309 + const selected = new Set(selectedYears); 310 + return yearsSorted.filter((year) => selected.has(year.year)); 311 + }, [yearsSorted, selectedYears]); 312 + 313 + const toggleYear = (year: number) => { 314 + setSelectedYears((current) => 315 + current.includes(year) 316 + ? current.filter((selected) => selected !== year) 317 + : [...current, year] 318 + ); 319 + }; 313 320 314 321 return ( 315 322 <div> 316 323 <div className="mb-3 flex items-center justify-between"> 317 324 <h2 className="text-lg font-semibold">{t("tabs.statistics", "Statistics")}</h2> 318 325 <div className="flex items-center gap-3"> 319 - <label className="text-xs text-gray-500"> 320 - {t("dashboard.year")}:&nbsp; 321 - <select 322 - className="text-xs bg-white dark:bg-neutral-900 border border-gray-300 dark:border-neutral-700 rounded px-2 py-1" 323 - value={currentYearStats?.year ?? ""} 324 - onChange={(e) => setSelectedYear(Number(e.target.value))} 325 - > 326 - {yearsSorted.map((y) => ( 327 - <option key={y.year} value={y.year}>{y.year}</option> 328 - ))} 329 - </select> 330 - </label> 326 + <fieldset className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-gray-500"> 327 + <legend className="sr-only">{t("dashboard.year")}</legend> 328 + <span aria-hidden>{t("dashboard.year")}:</span> 329 + {yearsSorted.map((y) => ( 330 + <label key={y.year} className="inline-flex items-center gap-1 cursor-pointer"> 331 + <input 332 + type="checkbox" 333 + checked={selectedYears.includes(y.year)} 334 + onChange={() => toggleYear(y.year)} 335 + /> 336 + <span>{y.year}</span> 337 + </label> 338 + ))} 339 + </fieldset> 331 340 <div className="text-xs text-gray-500"> 332 341 {t("statuses.lastUpdate")} {data?.updatedAt ? new Intl.DateTimeFormat(undefined, { dateStyle: "medium", timeStyle: "short" }).format(new Date(data.updatedAt)) : "–"} 333 342 </div> 334 343 </div> 335 344 </div> 336 - {currentYearStats && ( 337 - <div> 338 - <StatisticsKpis y={currentYearStats} /> 345 + {selectedYearStats.map((yearStats) => ( 346 + <section key={yearStats.year} className="mb-8" aria-labelledby={`statistics-year-${yearStats.year}`}> 347 + <h3 id={`statistics-year-${yearStats.year}`} className="text-lg font-semibold mb-3"> 348 + {t("dashboard.year")}: {yearStats.year} 349 + </h3> 350 + <StatisticsKpis y={yearStats} /> 339 351 <StatisticsLegend /> 340 - <CalendarHeatmap year={currentYearStats.year} /> 341 - <CalendarHeatmap 342 - year={currentYearStats.year} 343 - metric="tmin" 344 - /> 345 - <TopExtremes year={currentYearStats.year} /> 346 - </div> 347 - )} 352 + <CalendarHeatmap year={yearStats.year} /> 353 + <CalendarHeatmap year={yearStats.year} metric="tmin" /> 354 + <TopExtremes year={yearStats.year} /> 355 + </section> 356 + ))} 348 357 {loading && <div className="text-sm text-gray-600 dark:text-gray-300">{t("statuses.loading")}</div>} 349 358 {error && <div className="text-sm text-red-600">{t("statuses.error")}: {error}</div>} 350 359 {!loading && !error && data && data.years.length === 0 && ( 351 360 <div className="text-sm text-gray-600 dark:text-gray-300">{t("statuses.noData")}</div> 352 361 )} 353 - {!loading && !error && yearsSorted.length > 0 && ( 362 + {!loading && !error && selectedYearStats.length > 0 && ( 354 363 <div> 355 - {yearsSorted.map((y) => ( 364 + {selectedYearStats.map((y) => ( 356 365 <YearSection key={y.year} y={y} /> 357 366 ))} 358 367 </div>