diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/components/metrics/EventsChart.tsx | |
| download | umami-main.tar.xz umami-main.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/components/metrics/EventsChart.tsx')
| -rw-r--r-- | src/components/metrics/EventsChart.tsx | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx new file mode 100644 index 0000000..3a53ba9 --- /dev/null +++ b/src/components/metrics/EventsChart.tsx @@ -0,0 +1,93 @@ +import { colord } from 'colord'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { BarChart, type BarChartProps } from '@/components/charts/BarChart'; +import { LoadingPanel } from '@/components/common/LoadingPanel'; +import { + useDateRange, + useLocale, + useTimezone, + useWebsiteEventsSeriesQuery, +} from '@/components/hooks'; +import { renderDateLabels } from '@/lib/charts'; +import { CHART_COLORS } from '@/lib/constants'; +import { generateTimeSeries } from '@/lib/date'; + +export interface EventsChartProps extends BarChartProps { + websiteId: string; + focusLabel?: string; +} + +export function EventsChart({ websiteId, focusLabel }: EventsChartProps) { + const { timezone } = useTimezone(); + const { + dateRange: { startDate, endDate, unit }, + } = useDateRange({ timezone: timezone }); + const { locale, dateLocale } = useLocale(); + const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId); + const [label, setLabel] = useState<string>(focusLabel); + + const chartData: any = useMemo(() => { + if (!data) return; + + const map = (data as any[]).reduce((obj, { x, t, y }) => { + if (!obj[x]) { + obj[x] = []; + } + + obj[x].push({ x: t, y }); + + return obj; + }, {}); + + if (!map || Object.keys(map).length === 0) { + return { + datasets: [ + { + data: generateTimeSeries([], startDate, endDate, unit, dateLocale), + lineTension: 0, + borderWidth: 1, + }, + ], + }; + } else { + return { + datasets: Object.keys(map).map((key, index) => { + const color = colord(CHART_COLORS[index % CHART_COLORS.length]); + return { + label: key, + data: generateTimeSeries(map[key], startDate, endDate, unit, dateLocale), + lineTension: 0, + backgroundColor: color.alpha(0.6).toRgbString(), + borderColor: color.alpha(0.7).toRgbString(), + borderWidth: 1, + }; + }), + focusLabel, + }; + } + }, [data, startDate, endDate, unit, focusLabel]); + + useEffect(() => { + if (label !== focusLabel) { + setLabel(focusLabel); + } + }, [focusLabel]); + + const renderXLabel = useCallback(renderDateLabels(unit, locale), [unit, locale]); + + return ( + <LoadingPanel isLoading={isLoading} error={error} minHeight="400px"> + {chartData && ( + <BarChart + chartData={chartData} + minDate={startDate} + maxDate={endDate} + unit={unit} + stacked={true} + renderXLabel={renderXLabel} + height="400px" + /> + )} + </LoadingPanel> + ); +} |