diff --git a/frontend/src/component/insights/impact-metrics/ImpactMetrics.tsx b/frontend/src/component/insights/impact-metrics/ImpactMetrics.tsx index 86347f5241..816cb4521e 100644 --- a/frontend/src/component/insights/impact-metrics/ImpactMetrics.tsx +++ b/frontend/src/component/insights/impact-metrics/ImpactMetrics.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react'; import { useMemo, useState } from 'react'; -import { useTheme, Box, Typography, Alert } from '@mui/material'; +import { Box, Typography, Alert } from '@mui/material'; import { LineChart, NotEnoughData, @@ -15,18 +15,11 @@ import { useImpactMetricsMetadata } from 'hooks/api/getters/useImpactMetricsMeta import { useImpactMetricsData } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; import { usePlaceholderData } from '../hooks/usePlaceholderData.js'; import { ImpactMetricsControls } from './ImpactMetricsControls.tsx'; -import { - getDisplayFormat, - getSeriesLabel, - getTimeUnit, - formatLargeNumbers, -} from './utils.ts'; +import { getDisplayFormat, getTimeUnit, formatLargeNumbers } from './utils.ts'; import { fromUnixTime } from 'date-fns'; -import { useSeriesColor } from './hooks/useSeriesColor.ts'; +import { useChartData } from './hooks/useChartData.ts'; export const ImpactMetrics: FC = () => { - const theme = useTheme(); - const getSeriesColor = useSeriesColor(); const [selectedSeries, setSelectedSeries] = useState(''); const [selectedRange, setSelectedRange] = useState< 'hour' | 'day' | 'week' | 'month' @@ -78,78 +71,7 @@ export const ImpactMetrics: FC = () => { })); }, [metadata]); - const data = useMemo(() => { - if (!timeSeriesData || timeSeriesData.length === 0) { - return { - labels: [], - datasets: [ - { - data: [], - borderColor: theme.palette.primary.main, - backgroundColor: theme.palette.primary.light, - }, - ], - }; - } - - if (timeSeriesData.length === 1) { - const series = timeSeriesData[0]; - const timestamps = series.data.map( - ([epochTimestamp]) => new Date(epochTimestamp * 1000), - ); - const values = series.data.map(([, value]) => value); - - return { - labels: timestamps, - datasets: [ - { - data: values, - borderColor: theme.palette.primary.main, - backgroundColor: theme.palette.primary.light, - label: getSeriesLabel(series.metric), - }, - ], - }; - } else { - const allTimestamps = new Set(); - timeSeriesData.forEach((series) => { - series.data.forEach(([timestamp]) => { - allTimestamps.add(timestamp); - }); - }); - const sortedTimestamps = Array.from(allTimestamps).sort( - (a, b) => a - b, - ); - const labels = sortedTimestamps.map( - (timestamp) => new Date(timestamp * 1000), - ); - - const datasets = timeSeriesData.map((series) => { - const seriesLabel = getSeriesLabel(series.metric); - const color = getSeriesColor(seriesLabel); - - const dataMap = new Map(series.data); - - const data = sortedTimestamps.map( - (timestamp) => dataMap.get(timestamp) ?? null, - ); - - return { - label: seriesLabel, - data, - borderColor: color, - backgroundColor: color, - fill: false, - spanGaps: false, // Don't connect nulls - }; - }); - - return { - labels, - datasets, - }; - } - }, [timeSeriesData, theme, getSeriesColor]); + const data = useChartData(timeSeriesData); const hasError = metadataError || dataError; const isLoading = metadataLoading || dataLoading; diff --git a/frontend/src/component/insights/impact-metrics/ImpactMetricsControls.tsx b/frontend/src/component/insights/impact-metrics/ImpactMetricsControls.tsx index 3c9e9dc681..e658970d7c 100644 --- a/frontend/src/component/insights/impact-metrics/ImpactMetricsControls.tsx +++ b/frontend/src/component/insights/impact-metrics/ImpactMetricsControls.tsx @@ -143,8 +143,7 @@ export const ImpactMetricsControls: FC = ({ } label='Begin at zero' /> - - {availableLabels && Object.keys(availableLabels).length > 0 && ( + {availableLabels && Object.keys(availableLabels).length > 0 ? ( @@ -198,7 +197,7 @@ export const ImpactMetricsControls: FC = ({ ), )} - )} + ) : null} ); }; diff --git a/frontend/src/component/insights/impact-metrics/hooks/useChartData.ts b/frontend/src/component/insights/impact-metrics/hooks/useChartData.ts new file mode 100644 index 0000000000..ea3e8356db --- /dev/null +++ b/frontend/src/component/insights/impact-metrics/hooks/useChartData.ts @@ -0,0 +1,85 @@ +import { useMemo } from 'react'; +import { useTheme } from '@mui/material'; +import type { ImpactMetricsSeries } from 'hooks/api/getters/useImpactMetricsData/useImpactMetricsData'; +import { useSeriesColor } from './useSeriesColor.ts'; +import { getSeriesLabel } from '../utils.ts'; + +export const useChartData = ( + timeSeriesData: ImpactMetricsSeries[] | undefined, +) => { + const theme = useTheme(); + const getSeriesColor = useSeriesColor(); + + return useMemo(() => { + if (!timeSeriesData || timeSeriesData.length === 0) { + return { + labels: [], + datasets: [ + { + data: [], + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.light, + }, + ], + }; + } + + if (timeSeriesData.length === 1) { + const series = timeSeriesData[0]; + const timestamps = series.data.map( + ([epochTimestamp]) => new Date(epochTimestamp * 1000), + ); + const values = series.data.map(([, value]) => value); + + return { + labels: timestamps, + datasets: [ + { + data: values, + borderColor: theme.palette.primary.main, + backgroundColor: theme.palette.primary.light, + label: getSeriesLabel(series.metric), + }, + ], + }; + } else { + const allTimestamps = new Set(); + timeSeriesData.forEach((series) => { + series.data.forEach(([timestamp]) => { + allTimestamps.add(timestamp); + }); + }); + const sortedTimestamps = Array.from(allTimestamps).sort( + (a, b) => a - b, + ); + const labels = sortedTimestamps.map( + (timestamp) => new Date(timestamp * 1000), + ); + + const datasets = timeSeriesData.map((series) => { + const seriesLabel = getSeriesLabel(series.metric); + const color = getSeriesColor(seriesLabel); + + const dataMap = new Map(series.data); + + const data = sortedTimestamps.map( + (timestamp) => dataMap.get(timestamp) ?? null, + ); + + return { + label: seriesLabel, + data, + borderColor: color, + backgroundColor: color, + fill: false, + spanGaps: false, + }; + }); + + return { + labels, + datasets, + }; + } + }, [timeSeriesData, theme, getSeriesColor]); +};