From 28c4c39d81d315e991498d7de38d0ab460566336 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 13:31:39 -0400 Subject: [PATCH 01/14] Added email list signup in feedback --- frontend/src/app/about/contact/page.tsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/frontend/src/app/about/contact/page.tsx b/frontend/src/app/about/contact/page.tsx index 5e09092e..dfa48d70 100644 --- a/frontend/src/app/about/contact/page.tsx +++ b/frontend/src/app/about/contact/page.tsx @@ -7,6 +7,7 @@ import { Box, Button, Card, + Checkbox, Container, Group, Select, @@ -44,6 +45,7 @@ interface FeedbackFormValues { email: string; feedbackType: string; message: string; + joinEmailList: boolean; } function FeedbackForm() { @@ -56,6 +58,7 @@ function FeedbackForm() { email: '', feedbackType: '', message: '', + joinEmailList: false, }, validate: { name: isNotEmpty('Please enter your name'), @@ -81,6 +84,7 @@ function FeedbackForm() { email: values.email, feedback_type: values.feedbackType, message: values.message, + email_list_opt_in: values.joinEmailList ? 'Yes' : 'No', }), }); @@ -184,6 +188,25 @@ function FeedbackForm() { }} /> + + + + {status === 'success' && ( } From f62be41b5cab01548bc465a1d6f923fd1d61ef37 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 16:37:29 -0400 Subject: [PATCH 02/14] Removing unused imports + variables --- frontend/src/app/about/contact/page.tsx | 2 +- frontend/src/app/about/page.tsx | 6 +----- .../app/data-comparison/reports-by-topic/ReportsByTopic.tsx | 3 +-- .../src/app/data-comparison/variable-comparison/page.tsx | 2 +- frontend/src/app/data-viewer/FieldLabel.tsx | 2 +- frontend/src/app/layout.tsx | 2 +- frontend/src/app/page.tsx | 1 - frontend/src/app/resources/page.tsx | 1 - frontend/src/components/Charts/Bar.tsx | 1 - frontend/src/components/Charts/EmploymentAreaChart.tsx | 2 +- frontend/src/components/Charts/TableView.tsx | 3 +-- frontend/src/components/Charts/index.tsx | 2 -- frontend/src/components/Charts/saving.tsx | 3 +-- .../src/components/Reports/dashboards/EconomicDashboard.tsx | 2 +- .../components/Reports/dashboards/EducationDashboard.tsx | 2 +- .../src/components/Reports/dashboards/HousingDashboard.tsx | 2 +- .../src/components/Reports/dashboards/LandUseDashboard.tsx | 2 +- .../Reports/demographics/AgeDistributionChart.tsx | 1 - .../src/components/Reports/demographics/MedianAgeCard.tsx | 1 - .../src/components/Reports/demographics/PopulationCard.tsx | 1 - 20 files changed, 13 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/about/contact/page.tsx b/frontend/src/app/about/contact/page.tsx index dfa48d70..dcafc759 100644 --- a/frontend/src/app/about/contact/page.tsx +++ b/frontend/src/app/about/contact/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState } from 'react'; +import { useState } from 'react'; import { Alert, diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index e575d7db..d298f1d0 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -2,15 +2,12 @@ import { Accordion, - Badge, Box, Button, Card, Container, - Divider, Grid, Group, - Paper, SimpleGrid, Stack, Text, @@ -42,7 +39,6 @@ import Link from 'next/link'; import * as motion from 'motion/react-client'; import { COLORS, FONTS } from '@/app/theme'; -import { color } from 'd3'; // ----------------------------------------------------------------------------- // Hero @@ -693,7 +689,7 @@ function WhoItsFor() { /> - {audiences.map((audience, index, icon) => ( + {audiences.map((audience, index) => ( { chart: ChartItem; diff --git a/frontend/src/components/Charts/index.tsx b/frontend/src/components/Charts/index.tsx index d32d7843..f00c56f3 100644 --- a/frontend/src/components/Charts/index.tsx +++ b/frontend/src/components/Charts/index.tsx @@ -44,8 +44,6 @@ import { SimpleGrid, ActionIcon, Modal, - Container, - ScrollArea, TextInput, Textarea, Tooltip, diff --git a/frontend/src/components/Charts/saving.tsx b/frontend/src/components/Charts/saving.tsx index e5613878..b9c67878 100644 --- a/frontend/src/components/Charts/saving.tsx +++ b/frontend/src/components/Charts/saving.tsx @@ -1,8 +1,7 @@ -import { useEffect, useState } from 'react'; import { useItems } from '../ItemsProvider'; import { useProfile } from '@/components/profile/profileStore'; import Link from 'next/link'; -import { Anchor, Button, Group, Transition } from '@mantine/core'; +import { Anchor, Button, Group } from '@mantine/core'; import { CheckIcon, XIcon } from '@phosphor-icons/react'; import { ChartItem, DataRow } from '@/types/cachedCharts'; import { COLORS } from '@/app/theme'; diff --git a/frontend/src/components/Reports/dashboards/EconomicDashboard.tsx b/frontend/src/components/Reports/dashboards/EconomicDashboard.tsx index d79fedf0..7c5e86bf 100644 --- a/frontend/src/components/Reports/dashboards/EconomicDashboard.tsx +++ b/frontend/src/components/Reports/dashboards/EconomicDashboard.tsx @@ -1,4 +1,4 @@ -import { Table, ScrollArea, Paper, Text, Stack, Grid } from '@mantine/core'; +import { Grid } from '@mantine/core'; import { DataRow } from '@/types/cachedCharts'; import { UnemploymentRateCard, diff --git a/frontend/src/components/Reports/dashboards/EducationDashboard.tsx b/frontend/src/components/Reports/dashboards/EducationDashboard.tsx index 0daa1e49..9e0f0038 100644 --- a/frontend/src/components/Reports/dashboards/EducationDashboard.tsx +++ b/frontend/src/components/Reports/dashboards/EducationDashboard.tsx @@ -1,4 +1,4 @@ -import { Table, ScrollArea, Paper, Text, Stack, Grid } from '@mantine/core'; +import { Grid } from '@mantine/core'; import { DataRow } from '@/types/cachedCharts'; import { EducationalAttainment } from '@/components/Reports/education'; diff --git a/frontend/src/components/Reports/dashboards/HousingDashboard.tsx b/frontend/src/components/Reports/dashboards/HousingDashboard.tsx index f829f322..7a88561c 100644 --- a/frontend/src/components/Reports/dashboards/HousingDashboard.tsx +++ b/frontend/src/components/Reports/dashboards/HousingDashboard.tsx @@ -1,4 +1,4 @@ -import { Table, ScrollArea, Paper, Text, Stack, Grid } from '@mantine/core'; +import { Grid } from '@mantine/core'; import { DataRow } from '@/types/cachedCharts'; import { MedianHomeValueCard, diff --git a/frontend/src/components/Reports/dashboards/LandUseDashboard.tsx b/frontend/src/components/Reports/dashboards/LandUseDashboard.tsx index a88c8115..9ea26825 100644 --- a/frontend/src/components/Reports/dashboards/LandUseDashboard.tsx +++ b/frontend/src/components/Reports/dashboards/LandUseDashboard.tsx @@ -1,4 +1,4 @@ -import { Table, ScrollArea, Paper, Text, Stack, Grid } from '@mantine/core'; +import { Grid } from '@mantine/core'; import { DataRow } from '@/types/cachedCharts'; import { AcreageByDistrictType, diff --git a/frontend/src/components/Reports/demographics/AgeDistributionChart.tsx b/frontend/src/components/Reports/demographics/AgeDistributionChart.tsx index f099dac4..45072ef0 100644 --- a/frontend/src/components/Reports/demographics/AgeDistributionChart.tsx +++ b/frontend/src/components/Reports/demographics/AgeDistributionChart.tsx @@ -10,7 +10,6 @@ import { ResponsiveContainer, } from 'recharts'; import { DataRow } from '@/types/cachedCharts'; -import { COLORS } from '@/app/theme'; interface AgeDistributionChartProps { primary: DataRow[]; diff --git a/frontend/src/components/Reports/demographics/MedianAgeCard.tsx b/frontend/src/components/Reports/demographics/MedianAgeCard.tsx index 5ff6056b..7fd17b19 100644 --- a/frontend/src/components/Reports/demographics/MedianAgeCard.tsx +++ b/frontend/src/components/Reports/demographics/MedianAgeCard.tsx @@ -1,7 +1,6 @@ import { Card, Group, Stack, Text, ThemeIcon, Title } from '@mantine/core'; import { IconCalendarStats } from '@tabler/icons-react'; import { DataRow } from '@/types/cachedCharts'; -import { COLORS, FONTS } from '@/app/theme'; interface MedianAgeCardProps { primary: DataRow[]; diff --git a/frontend/src/components/Reports/demographics/PopulationCard.tsx b/frontend/src/components/Reports/demographics/PopulationCard.tsx index 868559e2..ff04c677 100644 --- a/frontend/src/components/Reports/demographics/PopulationCard.tsx +++ b/frontend/src/components/Reports/demographics/PopulationCard.tsx @@ -19,7 +19,6 @@ function findValue(data: DataRow[], variable: string): number | null { export default function PopulationCard({ primary, comparison, - primaryName, comparisonName, }: PopulationCardProps) { const primaryPopulation = findValue(primary, 'Population (ACS)'); From dd0262e909c9a0f8811d4abf1d131bb0657df315 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 16:46:03 -0400 Subject: [PATCH 03/14] Specifying type warnings from ESLint --- .../reports-by-topic/ReportsByTopic.tsx | 8 ++- frontend/src/app/data-viewer/page.tsx | 10 ++-- frontend/src/app/mapping/geoUtils.ts | 2 +- frontend/src/app/page.tsx | 6 +- frontend/src/components/Charts/Bar.tsx | 57 +++++++++++-------- .../components/Charts/EmploymentAreaChart.tsx | 16 +++--- .../src/components/Charts/TrendCharts.tsx | 47 +++++++-------- .../components/Charts/configs/ChartDefs.tsx | 2 +- frontend/src/components/mapping/index.tsx | 10 ++-- 9 files changed, 85 insertions(+), 73 deletions(-) diff --git a/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx b/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx index 7c8214a9..9a4f69c3 100644 --- a/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx +++ b/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx @@ -312,7 +312,13 @@ function HeroSection({ year, setYear, availableYears, -}: any) { +}: { + section: string; + setSection: (value: string) => void; + year: number; + setYear: (value: number) => void; + availableYears: number[]; +}) { return ( - >({}); + const [chartData, setChartData] = useState>({}); const [compareChartData, setCompareChartData] = useState< Record >({}); diff --git a/frontend/src/app/mapping/geoUtils.ts b/frontend/src/app/mapping/geoUtils.ts index d820c02b..3953ff9f 100644 --- a/frontend/src/app/mapping/geoUtils.ts +++ b/frontend/src/app/mapping/geoUtils.ts @@ -10,7 +10,7 @@ export function getFeatureBBox( let maxLng = -Infinity; let maxLat = -Infinity; - const processCoords = (coords: any) => { + const processCoords = (coords: unknown) => { if (!Array.isArray(coords) || coords.length === 0) return; // Check if we reached a coordinate pair [lng, lat] diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index b20a39a3..c812613c 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -34,7 +34,7 @@ import { NewspaperIcon, } from '@phosphor-icons/react'; -import { useProfile } from '@/components/profile/profileStore'; +import { useProfile, type Location } from '@/components/profile/profileStore'; import { COLORS, FONTS } from '@/app/theme'; @@ -244,8 +244,8 @@ function HeroSection({ yearMax, openProfileModal, }: { - myLocation: any; - comparison: any; + myLocation: Location; + comparison: Location; interests: string[]; yearMin: number; yearMax: number; diff --git a/frontend/src/components/Charts/Bar.tsx b/frontend/src/components/Charts/Bar.tsx index 708f598f..53d02c3a 100644 --- a/frontend/src/components/Charts/Bar.tsx +++ b/frontend/src/components/Charts/Bar.tsx @@ -24,6 +24,9 @@ import { Tooltip as TooltipJS, Legend as LegendJS, TooltipItem, + ChartEvent, + LegendItem, + LegendElement, } from 'chart.js'; ChartJS.register(CategoryScale, LinearScale, BarElement, TooltipJS, LegendJS); @@ -212,14 +215,14 @@ const CompareDiffPerXBarChartSVG = ({ const includeCategories = chart.chartParams?.includeCategories; const filteredData = includeCategories - ? chart.data.filter((entry: any) => - includeCategories.includes(entry[chart.xField]), + ? chart.data.filter((entry) => + includeCategories.includes(entry[chart.xField] as string), ) : chart.data; const filteredCompareData = includeCategories && chart.compareData - ? chart.compareData.filter((entry: any) => - includeCategories.includes(entry[chart.xField]), + ? chart.compareData.filter((entry) => + includeCategories.includes(entry[chart.xField] as string), ) : (chart.compareData ?? []); @@ -282,8 +285,8 @@ const CompareDiffPerXBarChart = ({ // Filter primary dataset based on categories if specified const filteredData = useMemo(() => { return includeCategories - ? rows.filter((entry: any) => - includeCategories.includes(entry[chart.xField]), + ? rows.filter((entry) => + includeCategories.includes(entry[chart.xField] as string), ) : rows; }, [rows, includeCategories, chart.xField]); @@ -291,15 +294,15 @@ const CompareDiffPerXBarChart = ({ // Filter comparison dataset based on categories if specified const filteredCompareData = useMemo(() => { return includeCategories - ? compareRows.filter((entry: any) => - includeCategories.includes(entry[chart.xField]), + ? compareRows.filter((entry) => + includeCategories.includes(entry[chart.xField] as string), ) : compareRows; }, [compareRows, includeCategories, chart.xField]); // Derive exact plottable rows and report back to ChartCard for TableView const plotData = useMemo(() => { - return filteredData.map((entry: any, i: number) => { + return filteredData.map((entry, i) => { const cmpEntry = filteredCompareData[i]; return { [chart.xField]: entry[chart.xField], @@ -324,26 +327,26 @@ const CompareDiffPerXBarChart = ({ const data = useMemo(() => { let colors: string[]; - if (colorField && (rows[0] as any)?.[colorField]) { - colors = filteredData.map((entry: any) => entry[colorField]); + if (colorField && rows[0]?.[colorField]) { + colors = filteredData.map((entry) => entry[colorField] as string); } else { const colorScale = d3.scaleOrdinal( - (d3 as any)[colorScheme], + d3Schemes[colorScheme], ); colors = filteredData.map((_, index) => colorScale(index.toString())); } return { - labels: filteredData.map((entry: any) => entry[xField]), + labels: filteredData.map((entry) => entry[xField]), datasets: [ { label: primaryLabel, - data: filteredData.map((entry: any) => entry[yField]), + data: filteredData.map((entry) => entry[yField] as number), backgroundColor: colors, }, { label: compareLabel, - data: filteredCompareData.map((entry: any) => entry[yField]), + data: filteredCompareData.map((entry) => entry[yField] as number), backgroundColor: filteredCompareData.map(() => '#D3D3D3'), }, ], @@ -370,8 +373,8 @@ const CompareDiffPerXBarChart = ({ legend: { display: true }, tooltip: { callbacks: { - label: (context: any) => { - const value = context.parsed.y; + label: (context: TooltipItem<'bar'>) => { + const value = context.parsed.y ?? 0; return percentFormat ? `${value}%` : value.toLocaleString(); }, }, @@ -380,7 +383,7 @@ const CompareDiffPerXBarChart = ({ scales: { y: { ticks: { - callback: (value: any) => + callback: (value: string | number) => percentFormat ? `${value}%` : value.toLocaleString(), }, }, @@ -585,13 +588,13 @@ const ZoningAllowanceStackedBarChart = ({ legend: { display: true, labels: { - generateLabels: (chart: any) => + generateLabels: (chart: ChartJS) => stackKeys.map((key) => { const mainIndex = chart.data.datasets.findIndex( - (ds: any) => ds.label === `${key}`, + (ds) => ds.label === `${key}`, ); const compareIndex = chart.data.datasets.findIndex( - (ds: any) => ds.label === `${key}`, + (ds) => ds.label === `${key}`, ); const mainMeta = chart.getDatasetMeta(mainIndex); @@ -612,11 +615,15 @@ const ZoningAllowanceStackedBarChart = ({ }; }), }, - onClick: (_e: any, legendItem: any, legend: any) => { + onClick: ( + _e: ChartEvent, + legendItem: LegendItem, + legend: LegendElement<'bar'>, + ) => { const chart = legend.chart; const key = legendItem.text; - chart.data.datasets.forEach((ds: any, idx: number) => { + chart.data.datasets.forEach((ds, idx) => { if (ds.label?.startsWith(key)) { const meta = chart.getDatasetMeta(idx); meta.hidden = !(meta.hidden ?? false); @@ -628,8 +635,8 @@ const ZoningAllowanceStackedBarChart = ({ }, tooltip: { callbacks: { - label: (ctx: any) => - `${ctx.dataset.label}: ${ctx.raw?.toLocaleString?.() ?? ctx.raw}`, + label: (ctx: TooltipItem<'bar'>) => + `${ctx.dataset.label}: ${(ctx.raw as number | undefined)?.toLocaleString() ?? ctx.raw}`, }, }, }, diff --git a/frontend/src/components/Charts/EmploymentAreaChart.tsx b/frontend/src/components/Charts/EmploymentAreaChart.tsx index 3236d39b..59f3c4b2 100644 --- a/frontend/src/components/Charts/EmploymentAreaChart.tsx +++ b/frontend/src/components/Charts/EmploymentAreaChart.tsx @@ -12,7 +12,7 @@ import { XAxis, YAxis, } from 'recharts'; -import { ChartItem } from '@/types/cachedCharts'; +import { ChartItem, DataRow } from '@/types/cachedCharts'; import { ScrollArea, SegmentedControl, Table } from '@mantine/core'; import { useState } from 'react'; import { usePdfMode } from '@/contexts/PdfModeContext'; @@ -28,8 +28,8 @@ const SECTOR_COLORS: Record = { 'Other Services': '#c0c0c0', }; -const yAxisFmt = (v: any) => `${(v / 1000).toFixed(0)}k`; -const tooltipFmt = (v: any) => `${v.toLocaleString(0)}`; +const yAxisFmt = (v: number) => `${(v / 1000).toFixed(0)}k`; +const tooltipFmt = (v: unknown) => (v as number).toLocaleString(); type EmpView = 'stacked' | 'trend' | 'table'; @@ -57,7 +57,7 @@ export const EmploymentAreaChart = ({ chart, view = 'report', }: { - chart: ChartItem; + chart: ChartItem; view?: 'gallery' | 'report'; }) => { const isPdfMode = usePdfMode(); @@ -67,7 +67,7 @@ export const EmploymentAreaChart = ({ // Gallery tiles always show the stacked view — no controls, no state to manage. const activeView: EmpView = isPdfMode || isGallery ? 'stacked' : localView; - const data = chart.data as any[]; + const data = chart.data; if (!data?.length) return null; const sectors = Object.keys(data[0]).filter((k) => k !== 'quarter_label'); @@ -78,7 +78,7 @@ export const EmploymentAreaChart = ({ const galleryTicks = computeGalleryTicks(q1Ticks, 4); - const dataWithTotal = data.map((row) => ({ + const dataWithTotal: DataRow[] = data.map((row) => ({ ...row, Total: sectors.reduce((sum, s) => { const v = row[s]; @@ -186,8 +186,8 @@ export const EmploymentAreaChart = ({ paddingBottom: '8px', }} > - {payload?.map((entry: any) => { - const key = entry.value; + {payload?.map((entry) => { + const key = entry.value as string; const isHidden = hidden.has(key); return ( string; - tooltip: (v: any, decimals?: number) => string; + axisFormatter?: (v: number) => string; + tooltip: (v: unknown, decimals?: number) => string; } > = { currency: { @@ -145,8 +145,8 @@ export const SingleSeriesTrendChart = ({ const { hidden, toggleSeries, legendFormatter } = useToggle(); - const data = chart.data as any[]; - const compareData = chart.compareData as any[]; + const data = chart.data as TrendRow[]; + const compareData = chart.compareData as TrendRow[]; const seriesName = displayName ?? seriesKey ?? valueField; const labels = chart.chartParams?.legendLabels as @@ -174,7 +174,8 @@ export const SingleSeriesTrendChart = ({ const otherKey = (i: number) => `${lineKey(seriesName, i)} (cmp)`; const years = useMemo( - () => (data ? Array.from(new Set(data.map((r) => r.year))).sort() : []), + () => + data ? Array.from(new Set(data.map((r) => r.year as number))).sort() : [], [data], ); @@ -238,14 +239,14 @@ export const SingleSeriesTrendChart = ({ tickFormatter={fmt.axisFormatter} /> {!isGallery && ( - fmt.tooltip(v, decimals)} /> + fmt.tooltip(v, decimals)} /> )} {!isGallery && ( toggleSeries(e.dataKey)} + onClick={(e) => toggleSeries(String(e.dataKey))} formatter={legendFormatter} wrapperStyle={{ fontSize: isGallery ? 12 : 16 }} /> @@ -351,8 +352,8 @@ export const MultiSeriesTrendChart = ({ const { hidden, toggleSeries, legendFormatter } = useToggle(); - const data = chart.data as any[]; - const compareData = chart.compareData as any[]; + const data = chart.data as TrendRow[]; + const compareData = chart.compareData as TrendRow[]; const labels = chart.chartParams?.legendLabels as [string, string] | undefined; @@ -384,13 +385,12 @@ export const MultiSeriesTrendChart = ({ // Lines past a series' first, when it split; the first keeps s.key. const extraKey = (s: SeriesDef, i: number) => `${s.key} (${i + 1})`; - const getValue = (rows: any[], year: number, s: SeriesDef) => { + const getValue = (rows: TrendRow[], year: number, s: SeriesDef) => { if (s.aggregateFrom) { const sum = s.aggregateFrom.reduce((acc, label) => { - const v = - rows.find((r) => r.year === year && r.Variable === label)?.[ - valueField - ] ?? 0; + const v = (rows.find((r) => r.year === year && r.Variable === label)?.[ + valueField + ] ?? 0) as number; return acc + v; }, 0); return sum > 0 ? Math.round(sum * 10) / 10 : null; @@ -403,14 +403,15 @@ export const MultiSeriesTrendChart = ({ }; const years = useMemo( - () => (data ? Array.from(new Set(data.map((r) => r.year))).sort() : []), + () => + data ? Array.from(new Set(data.map((r) => r.year as number))).sort() : [], [data], ); const plotData = useMemo(() => { if (!data || data.length === 0) return []; return years.map((year) => { - const pt: Record = { year }; + const pt: DataRow = { year }; series.forEach((s, si) => { pt[s.key] = getValue(data, year, s); if (compareData && compareData.length > 0) @@ -479,13 +480,13 @@ export const MultiSeriesTrendChart = ({ domain={['auto', 'auto']} tickFormatter={fmt.axisFormatter} /> - {!isGallery && fmt.tooltip(v)} />} + {!isGallery && fmt.tooltip(v)} />} {!isGallery && ( toggleSeries(e.dataKey)} + onClick={(e) => toggleSeries(String(e.dataKey))} formatter={legendFormatter} wrapperStyle={{ fontSize: isGallery ? 12 : 16 }} /> @@ -570,8 +571,8 @@ export const MultiSeriesTrendChart = ({ }; // SINGLE CHARTS -const single = ( - chart: ChartItem, +const single = ( + chart: ChartItem, config: SingleSeriesConfig, view?: 'gallery' | 'report', onPlotData?: (rows: DataRow[]) => void, @@ -863,8 +864,8 @@ export const HousingIncomeBurdenChart = ({ ); // MULTI CHARTS -const multi = ( - chart: ChartItem, +const multi = ( + chart: ChartItem, config: MultiSeriesConfig, view?: 'gallery' | 'report', onPlotData?: (rows: DataRow[]) => void, @@ -998,7 +999,7 @@ export const DPTrendChart = ({ chart }: { chart: ChartItem }) => { ).sort((a, b) => Number(a) - Number(b)); const plotData = allYears.map((year) => { - const pt: Record = { year }; + const pt: DataRow = { year }; primary.lines.forEach((line, i) => { pt[`primary${i}`] = line.rows.find((r) => r.year === year)?.Value ?? null; }); diff --git a/frontend/src/components/Charts/configs/ChartDefs.tsx b/frontend/src/components/Charts/configs/ChartDefs.tsx index 413e203e..70f9f60a 100644 --- a/frontend/src/components/Charts/configs/ChartDefs.tsx +++ b/frontend/src/components/Charts/configs/ChartDefs.tsx @@ -12,7 +12,7 @@ export interface TableRowDef { export interface TableConfig { variable?: string; - extraParams?: Record; // year_min, year_max, etc. + extraParams?: Record; // year_min, year_max, etc. } export interface ChartDef { diff --git a/frontend/src/components/mapping/index.tsx b/frontend/src/components/mapping/index.tsx index 4f38e80e..c9cfb370 100644 --- a/frontend/src/components/mapping/index.tsx +++ b/frontend/src/components/mapping/index.tsx @@ -6,7 +6,7 @@ import { GeoJsonLayer } from '@deck.gl/layers'; import DeckGL from '@deck.gl/react'; import { FlyToInterpolator } from '@deck.gl/core'; import { WebMercatorViewport } from '@math.gl/web-mercator'; -import type { LayersList } from '@deck.gl/core'; +import type { LayersList, MapViewState } from '@deck.gl/core'; import type { FeatureCollection } from 'geojson'; import { Paper, @@ -209,7 +209,7 @@ export default function VTMap({ const mapRef = useRef(null); const containerRef = useRef(null); - const [viewState, setViewState] = useState({ + const [viewState, setViewState] = useState({ ...INITIAL_VIEW_STATE, zoom: initialZoom, }); @@ -238,7 +238,7 @@ export default function VTMap({ }, ); - setViewState((prev: any) => ({ + setViewState((prev) => ({ ...prev, longitude, latitude, @@ -277,8 +277,8 @@ export default function VTMap({ .catch(() => {}); }, []); - const onViewStateChange = useCallback((params: { viewState: any }) => { - const vs = params.viewState; + const onViewStateChange = useCallback((params: { viewState: unknown }) => { + const vs = params.viewState as MapViewState; setViewState({ ...vs, zoom: clamp(vs.zoom, VERMONT_BOUNDS.zoom.min, VERMONT_BOUNDS.zoom.max), From df7666e1ee79b1cebf251752b933afc67587a9d6 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 16:47:40 -0400 Subject: [PATCH 04/14] Fixing ESLint apostrophe warnings --- frontend/src/app/about/page.tsx | 2 +- frontend/src/app/page.tsx | 8 ++++---- frontend/src/components/Maintenance/index.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index d298f1d0..23a30ab4 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -780,7 +780,7 @@ function FAQSection() { Data Sources {' '} - to learn more about what's available. + to learn more about what's available. ), }, diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index c812613c..d70d8623 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -333,8 +333,8 @@ function HeroSection({ style={{ color: 'rgba(246,245,239,0.78)' }} > Zoning, flood risk, housing, demographics, and more - community-level data for all 251 towns and cities. It's free to - explore and open to all. + community-level data for all 251 towns and cities. It's + free to explore and open to all. @@ -742,7 +742,7 @@ function ValueAdds() { }} mb={8} > - What's different here + What's different here Compared to a typical government data portal. @@ -826,7 +826,7 @@ function CommunitySection() { mx="auto" style={{ color: 'rgba(246,245,239,0.78)' }} > - The platform's code and data pipelines are open source. Towns, + The platform's code and data pipelines are open source. Towns, researchers, and volunteers help shape what gets added next — and anyone can inspect exactly how a number was calculated. diff --git a/frontend/src/components/Maintenance/index.tsx b/frontend/src/components/Maintenance/index.tsx index a273a8ef..5b2360eb 100644 --- a/frontend/src/components/Maintenance/index.tsx +++ b/frontend/src/components/Maintenance/index.tsx @@ -36,8 +36,8 @@ export default function Maintenance(): React.JSX.Element { Under Maintenance - We are updating our systems to serve you better. We'll be back - online shortly. + We are updating our systems to serve you better. We'll be + back online shortly. From b5579e087f84c600a25a6a126bf962857463ec38 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 16:48:30 -0400 Subject: [PATCH 05/14] Fixing the useState defined after a return statement --- .../components/Charts/EmploymentAreaChart.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/Charts/EmploymentAreaChart.tsx b/frontend/src/components/Charts/EmploymentAreaChart.tsx index 59f3c4b2..b0179141 100644 --- a/frontend/src/components/Charts/EmploymentAreaChart.tsx +++ b/frontend/src/components/Charts/EmploymentAreaChart.tsx @@ -67,6 +67,19 @@ export const EmploymentAreaChart = ({ // Gallery tiles always show the stacked view — no controls, no state to manage. const activeView: EmpView = isPdfMode || isGallery ? 'stacked' : localView; + const [hidden, setHidden] = useState>(new Set()); + const toggleSeries = (key: string) => { + setHidden((prev) => { + const next = new Set(prev); + if (next.has(key)) { + next.delete(key); + } else { + next.add(key); + } + return next; + }); + }; + const data = chart.data; if (!data?.length) return null; @@ -102,19 +115,6 @@ export const EmploymentAreaChart = ({ const tableRows = [...dataWithTotal].reverse(); - const [hidden, setHidden] = useState>(new Set()); - const toggleSeries = (key: string) => { - setHidden((prev) => { - const next = new Set(prev); - if (next.has(key)) { - next.delete(key); - } else { - next.add(key); - } - return next; - }); - }; - const height = isGallery ? GALLERY_H : INNER_H; return ( From ddc3d056607f99682ef19644caf34ffe5b49a7c5 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 16:57:08 -0400 Subject: [PATCH 06/14] Add in missing dependencies to React hooks (ie. useEffect) --- .../app/data-comparison/dp-explorer/page.tsx | 16 +++-- frontend/src/app/data-viewer/page.tsx | 17 +++-- frontend/src/app/working-report/page.tsx | 15 ++--- .../src/components/Charts/TrendCharts.tsx | 65 ++++++++++--------- .../src/components/FilterRedux/CascadeUI.tsx | 4 +- .../src/components/FilterRedux/CheckboxUI.tsx | 2 +- .../src/components/FilterRedux/RangeUI.tsx | 2 +- .../FilterUI/GetFilterTreeFromAPI.tsx | 2 +- .../components/FilterUI/useApplyFilters.ts | 5 +- 9 files changed, 69 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/data-comparison/dp-explorer/page.tsx b/frontend/src/app/data-comparison/dp-explorer/page.tsx index e8d2f6ef..a6c1d2e1 100644 --- a/frontend/src/app/data-comparison/dp-explorer/page.tsx +++ b/frontend/src/app/data-comparison/dp-explorer/page.tsx @@ -30,7 +30,10 @@ import { MinusIcon, WarningIcon, } from '@phosphor-icons/react'; -import { useProfile } from '@/components/profile/profileStore'; +import { + useProfile, + YEAR_MAX_OVERALL, +} from '@/components/profile/profileStore'; import { BASE_API_URL } from '@/config'; import { ChartStack } from '@/components/Charts'; import { createChartItem } from '@/utils/itemFactory'; @@ -540,7 +543,9 @@ export default function DPExplorerPage() { // Available years derived from fetched data const [availableYears, setAvailableYears] = useState([]); - const YEAR_MAX_OVERALL = new Date().getFullYear() - 2; + // stringified so the effect re-runs on name changes, not object identity + const nameA = makeName(sideA); + const nameB = makeName(sideB); useEffect(() => { if (!isComplete) { @@ -567,7 +572,7 @@ export default function DPExplorerPage() { }) .then((r) => r.data); - Promise.all([post(makeName(sideA)), post(makeName(sideB))]) + Promise.all([post(nameA), post(nameB)]) .then(([aRes, bRes]) => { const aData = aRes.data ?? []; const bData = bRes.data ?? []; @@ -598,9 +603,8 @@ export default function DPExplorerPage() { subcategory, variable, measure, - // stringify name to avoid object ref changes - makeName(sideA), - makeName(sideB), + nameA, + nameB, ]); // --------------------------------------------------------------------------- diff --git a/frontend/src/app/data-viewer/page.tsx b/frontend/src/app/data-viewer/page.tsx index 2b876709..9a47d486 100644 --- a/frontend/src/app/data-viewer/page.tsx +++ b/frontend/src/app/data-viewer/page.tsx @@ -66,7 +66,7 @@ function StatCards() { setData(data as Row[]); }, }); - }, [myLocation, yearMin, yearMax]); + }, [applyFilters, myLocation, yearMin, yearMax]); const metrics = data.reduce>((acc, d) => { acc[d.Variable] = d.Value; @@ -333,6 +333,11 @@ function ProfileField({ ); } +const tableDefs = chartDefs.filter((c) => c.subtype.startsWith('renderTable')); +const nonTableDefs = chartDefs.filter( + (c) => !c.subtype.startsWith('renderTable'), +); + export default function DataViewerPage() { const { myLocation, @@ -354,12 +359,6 @@ export default function DataViewerPage() { const [activeTab, setActiveTab] = useState(null); const applyFilters = useApplyFilters(); - const tableDefs = chartDefs.filter((c) => - c.subtype.startsWith('renderTable'), - ); - const nonTableDefs = chartDefs.filter( - (c) => !c.subtype.startsWith('renderTable'), - ); const categoryIcons: Record = { Housing: HouseLineIcon, @@ -423,7 +422,7 @@ export default function DataViewerPage() { })), }); }); - }, [myLocation, comparison, yearMin, yearMax]); + }, [applyFilters, myLocation, comparison, yearMin, yearMax]); useEffect(() => { const seen = new Set(); @@ -485,7 +484,7 @@ export default function DataViewerPage() { }); } }); - }, [myLocation, comparison, yearMin, yearMax]); + }, [applyFilters, myLocation, comparison, yearMin, yearMax]); // QCEW employment data is county-level only; swap to a note card for town selections const isSubcountyLocation = myLocation.type === 'town'; diff --git a/frontend/src/app/working-report/page.tsx b/frontend/src/app/working-report/page.tsx index f33ce31d..f3e1c777 100644 --- a/frontend/src/app/working-report/page.tsx +++ b/frontend/src/app/working-report/page.tsx @@ -476,6 +476,11 @@ function SortableChartItem({ ); } +const tableDefs = chartDefs.filter((c) => c.subtype.startsWith('renderTable')); +const nonTableDefs = chartDefs.filter( + (c) => !c.subtype.startsWith('renderTable'), +); + export default function WorkingReport() { const chartsRef = useRef(null); const [isPdfMode, setIsPdfMode] = useState(false); @@ -588,12 +593,6 @@ export default function WorkingReport() { >({}); const applyFilters = useApplyFilters(); - const tableDefs = chartDefs.filter((c) => - c.subtype.startsWith('renderTable'), - ); - const nonTableDefs = chartDefs.filter( - (c) => !c.subtype.startsWith('renderTable'), - ); useEffect(() => { nonTableDefs.forEach((chart: ChartDef) => { @@ -641,7 +640,7 @@ export default function WorkingReport() { })), }); }); - }, [myLocation, comparison, yearMin, yearMax]); + }, [applyFilters, myLocation, comparison, yearMin, yearMax]); useEffect(() => { const seen = new Set(); @@ -700,7 +699,7 @@ export default function WorkingReport() { }); } }); - }, [myLocation, comparison, yearMin, yearMax]); // eslint-disable-line react-hooks/exhaustive-deps + }, [applyFilters, myLocation, comparison, yearMin, yearMax]); // ---------- build chart items ---------- const isSubcountyLocation = myLocation.type === 'town'; diff --git a/frontend/src/components/Charts/TrendCharts.tsx b/frontend/src/components/Charts/TrendCharts.tsx index 35f91fcd..2674e1d6 100644 --- a/frontend/src/components/Charts/TrendCharts.tsx +++ b/frontend/src/components/Charts/TrendCharts.tsx @@ -327,6 +327,29 @@ export interface MultiSeriesConfig { nameSuffix?: boolean; } +// The series' value for a year; summed series add up their matches. +const getValue = ( + rows: TrendRow[], + year: number, + s: SeriesDef, + valueField: string, +) => { + if (s.aggregateFrom) { + const sum = s.aggregateFrom.reduce((acc, label) => { + const v = (rows.find((r) => r.year === year && r.Variable === label)?.[ + valueField + ] ?? 0) as number; + return acc + v; + }, 0); + return sum > 0 ? Math.round(sum * 10) / 10 : null; + } + const label = s.matchVariable ?? s.key; + return ( + rows.find((r) => r.year === year && r.Variable === label)?.[valueField] ?? + null + ); +}; + export const MultiSeriesTrendChart = ({ chart, config, @@ -341,7 +364,7 @@ export const MultiSeriesTrendChart = ({ const isGallery = view === 'gallery'; const isPdfMode = usePdfMode(); const { - series, + series: seriesConfig, valueField, format, showHelperText = true, @@ -352,6 +375,14 @@ export const MultiSeriesTrendChart = ({ const { hidden, toggleSeries, legendFormatter } = useToggle(); + // Configs are inline literals (new identity every render), so re-parse by + // content: the memos below re-run only when the series actually change. + const seriesJson = JSON.stringify(seriesConfig); + const series = useMemo( + () => JSON.parse(seriesJson) as SeriesDef[], + [seriesJson], + ); + const data = chart.data as TrendRow[]; const compareData = chart.compareData as TrendRow[]; const labels = chart.chartParams?.legendLabels as @@ -373,7 +404,7 @@ export const MultiSeriesTrendChart = ({ own: split(data, s), other: split(compareData, s), })); - }, [data, compareData, JSON.stringify(series)]); + }, [data, compareData, series]); const maxPerYear = Math.max( 0, ...splitSeries.flatMap(({ own, other }) => [own.maxPerX, other.maxPerX]), @@ -385,23 +416,6 @@ export const MultiSeriesTrendChart = ({ // Lines past a series' first, when it split; the first keeps s.key. const extraKey = (s: SeriesDef, i: number) => `${s.key} (${i + 1})`; - const getValue = (rows: TrendRow[], year: number, s: SeriesDef) => { - if (s.aggregateFrom) { - const sum = s.aggregateFrom.reduce((acc, label) => { - const v = (rows.find((r) => r.year === year && r.Variable === label)?.[ - valueField - ] ?? 0) as number; - return acc + v; - }, 0); - return sum > 0 ? Math.round(sum * 10) / 10 : null; - } - const label = s.matchVariable ?? s.key; - return ( - rows.find((r) => r.year === year && r.Variable === label)?.[valueField] ?? - null - ); - }; - const years = useMemo( () => data ? Array.from(new Set(data.map((r) => r.year as number))).sort() : [], @@ -413,9 +427,9 @@ export const MultiSeriesTrendChart = ({ return years.map((year) => { const pt: DataRow = { year }; series.forEach((s, si) => { - pt[s.key] = getValue(data, year, s); + pt[s.key] = getValue(data, year, s, valueField); if (compareData && compareData.length > 0) - pt[`${s.key} (cmp)`] = getValue(compareData, year, s); + pt[`${s.key} (cmp)`] = getValue(compareData, year, s, valueField); if (s.aggregateFrom) return; const { own, other } = splitSeries[si]; const at = (line: { rows: DataRow[] }) => @@ -431,14 +445,7 @@ export const MultiSeriesTrendChart = ({ }); return pt; }); - }, [ - years, - data, - compareData, - JSON.stringify(series), - valueField, - splitSeries, - ]); + }, [years, data, compareData, series, valueField, splitSeries]); useEffect(() => { onPlotData?.(plotData); diff --git a/frontend/src/components/FilterRedux/CascadeUI.tsx b/frontend/src/components/FilterRedux/CascadeUI.tsx index 77d03536..49fd7fa3 100644 --- a/frontend/src/components/FilterRedux/CascadeUI.tsx +++ b/frontend/src/components/FilterRedux/CascadeUI.tsx @@ -113,7 +113,7 @@ export function CascadeFilter(params: apiFilterParams) { setBadges(r.data.badges || {}); }) .catch((e) => console.error('tree fetch failed', e)); - }, [filterURL]); + }, [spec.filter_table, filterURL]); // Hand the current level set up to the caller whenever it changes -- only // `labels` (not `onLabelsChange`'s identity) should retrigger this. @@ -130,7 +130,7 @@ export function CascadeFilter(params: apiFilterParams) { if (!labels.length) return; const removed = removeStaleFilters(tree, labels, spec.filters ?? {}); if (removed !== spec.filters) setValue(removed); - }, [tree, labels]); + }, [tree, labels, spec.filters, setValue]); // what happens when we select a value in the box // (we update our filters and push them up to parent) diff --git a/frontend/src/components/FilterRedux/CheckboxUI.tsx b/frontend/src/components/FilterRedux/CheckboxUI.tsx index 1fced627..be14edb3 100644 --- a/frontend/src/components/FilterRedux/CheckboxUI.tsx +++ b/frontend/src/components/FilterRedux/CheckboxUI.tsx @@ -84,7 +84,7 @@ export function CheckboxFilter(params: apiFilterParams) { setOptions(r.data.options); }) .catch((e) => console.error('Labels fetch failed', e)); - }, [filterURL]); + }, [spec.filter_table, filterURL]); // what happens when we toggle a box const handleToggle = (label: string, selections: string[]) => { diff --git a/frontend/src/components/FilterRedux/RangeUI.tsx b/frontend/src/components/FilterRedux/RangeUI.tsx index cc1daa74..c9872a56 100644 --- a/frontend/src/components/FilterRedux/RangeUI.tsx +++ b/frontend/src/components/FilterRedux/RangeUI.tsx @@ -27,7 +27,7 @@ export function RangeFilter(params: apiFilterParams) { .get(rangeURL) .then((r) => setRanges(r.data.ranges)) .catch((e) => console.error('range bounds fetch failed', e)); - }, [rangeURL]); + }, [spec.filter_table, rangeURL]); return ( diff --git a/frontend/src/components/FilterUI/GetFilterTreeFromAPI.tsx b/frontend/src/components/FilterUI/GetFilterTreeFromAPI.tsx index c89cfe44..1ec0f4ec 100644 --- a/frontend/src/components/FilterUI/GetFilterTreeFromAPI.tsx +++ b/frontend/src/components/FilterUI/GetFilterTreeFromAPI.tsx @@ -30,7 +30,7 @@ export default function GenericFilter({ apiURL }: GenericFilterProps) { firstRange ? { ...firstRange, selected: firstRange.bounds } : null, ); }); - }, [apiURL, setLabels, setSelectedFilters]); + }, [apiURL, setLabels, setRange, setSelectedFilters]); const numLevels = labels.length; diff --git a/frontend/src/components/FilterUI/useApplyFilters.ts b/frontend/src/components/FilterUI/useApplyFilters.ts index 1f38facf..9d4ecd43 100644 --- a/frontend/src/components/FilterUI/useApplyFilters.ts +++ b/frontend/src/components/FilterUI/useApplyFilters.ts @@ -1,3 +1,4 @@ +import { useCallback } from 'react'; import axios from 'axios'; import { Location } from '../profile/profileStore'; @@ -10,7 +11,7 @@ type apiFilterParams = { }; export function useApplyFilters() { - return async function apply(params: apiFilterParams) { + return useCallback(async function apply(params: apiFilterParams) { const { dataURL, filters, onData } = params; if (!dataURL) return; try { @@ -24,7 +25,7 @@ export function useApplyFilters() { } catch (err) { console.error('Error fetching filtered data:', err); } - }; + }, []); } type filterRange = { col: string; selected: [number, number] }; From af26f17a06432f81cd0bdbc78f58dc860643ab52 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 17:06:17 -0400 Subject: [PATCH 07/14] More ESLint fixes --- .../reports-by-topic/ReportsByTopic.tsx | 18 ------------------ frontend/src/app/data-viewer/page.tsx | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx b/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx index 9a4f69c3..db1c038a 100644 --- a/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx +++ b/frontend/src/app/data-comparison/reports-by-topic/ReportsByTopic.tsx @@ -30,7 +30,6 @@ import { // import { createChartItem } from '@/utils/itemFactory'; import { DataRow } from '@/types/cachedCharts'; import { IconInfoCircle } from '@tabler/icons-react'; -import { exportReport } from '@/utils/exportReport'; import { DashboardSection, TOPIC_SLUGS, topicPath } from './topics'; // --------------------------------------------------------------------------- @@ -555,7 +554,6 @@ export default function ReportsByTopic({ ); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const [isExporting, setIsExporting] = useState(false); // --------------------------------------------------------------------------- // Fetch both locations whenever section or location names change — plus, @@ -793,22 +791,6 @@ export default function ReportsByTopic({ ]), ); - const handleExportPdf = async () => { - setIsExporting(true); - try { - await exportReport('demographics-dashboard-report', { - title: section, - primaryName: myLocation.name, - comparisonName: comparison.name, - year: year, - }); - } catch (err) { - console.error('PDF export failed:', err); - } finally { - setIsExporting(false); - } - }; - // --------------------------------------------------------------------------- // Render // --------------------------------------------------------------------------- diff --git a/frontend/src/app/data-viewer/page.tsx b/frontend/src/app/data-viewer/page.tsx index 9a47d486..6cbc3342 100644 --- a/frontend/src/app/data-viewer/page.tsx +++ b/frontend/src/app/data-viewer/page.tsx @@ -355,7 +355,7 @@ export default function DataViewerPage() { Record >({}); - const [focusMode, setFocusMode] = useState<'all' | 'focus'>('all'); + const [focusMode] = useState<'all' | 'focus'>('all'); const [activeTab, setActiveTab] = useState(null); const applyFilters = useApplyFilters(); From 2a02935dab4924d174a1cc98491e68fa5bb95db2 Mon Sep 17 00:00:00 2001 From: Isarge05 Date: Thu, 1 Oct 2026 17:12:55 -0400 Subject: [PATCH 08/14] ESLint fixes --- .../src/app/mapping/[slug]/page_content.tsx | 19 +++++-------------- frontend/src/components/Charts/TableView.tsx | 10 +++------- .../src/components/Charts/TrendCharts.tsx | 6 ++---- frontend/src/components/mapping/index.tsx | 16 ---------------- 4 files changed, 10 insertions(+), 41 deletions(-) diff --git a/frontend/src/app/mapping/[slug]/page_content.tsx b/frontend/src/app/mapping/[slug]/page_content.tsx index c276c211..61247540 100644 --- a/frontend/src/app/mapping/[slug]/page_content.tsx +++ b/frontend/src/app/mapping/[slug]/page_content.tsx @@ -68,6 +68,10 @@ const MAP_CONFIG: Record = { export default function MappingContent() { const params = useParams(); const slug = params?.slug as string | undefined; + return ; +} + +function MappingBody({ slug }: { slug?: string }) { const config = slug ? MAP_CONFIG[slug] : undefined; const townBorderDef = config?.townBorder ?? false; @@ -83,16 +87,6 @@ export default function MappingContent() { const [legendData, setLegendData] = useState([]); - /* - * Reset map state when navigating between map pages. - */ - useEffect(() => { - setData(null); - setLegendData([]); - setShowCountyLines(townBorderDef); - setLargeBorder(largeBorderDef); - }, [slug, townBorderDef, largeBorderDef]); - /* * Load initial map data. * @@ -143,10 +137,7 @@ export default function MappingContent() { useEffect(() => { const legendURL = config?.legendURL; - if (!legendURL) { - setLegendData([]); - return; - } + if (!legendURL) return; let cancelled = false; diff --git a/frontend/src/components/Charts/TableView.tsx b/frontend/src/components/Charts/TableView.tsx index 617f1c96..7aa7337d 100644 --- a/frontend/src/components/Charts/TableView.tsx +++ b/frontend/src/components/Charts/TableView.tsx @@ -106,7 +106,7 @@ export const TableView = ({ } // Render the comparison toggle header control - const ComparisonToggleHeader = () => ( + const comparisonToggleHeader = (