129 lines
4.6 KiB
TypeScript
129 lines
4.6 KiB
TypeScript
'use client';
|
|
import { Column, useTheme } from '@umami/react-zen';
|
|
import { useCallback, useMemo } from 'react';
|
|
import { BarChart } from '@/components/charts/BarChart';
|
|
import { LoadingPanel } from '@/components/common/LoadingPanel';
|
|
import {
|
|
useDateRange,
|
|
useLocale,
|
|
useMessages,
|
|
usePropertyNumericSeriesQuery,
|
|
usePropertyNumericStatsQuery,
|
|
useTimezone,
|
|
} from '@/components/hooks';
|
|
import type { PropertyDataSource } from '@/components/hooks/queries/usePropertyFieldsQuery';
|
|
import { MetricCard } from '@/components/metrics/MetricCard';
|
|
import { MetricsBar } from '@/components/metrics/MetricsBar';
|
|
import { renderDateLabels } from '@/lib/charts';
|
|
import { getThemeColors } from '@/lib/colors';
|
|
import { generateTimeSeries } from '@/lib/date';
|
|
import { formatLongNumber } from '@/lib/format';
|
|
import type { PropertyFilter } from '@/lib/types';
|
|
|
|
export function PropertyNumericChart({
|
|
source,
|
|
websiteId,
|
|
propertyName,
|
|
propertyFilters = [],
|
|
eventName,
|
|
}: {
|
|
source: PropertyDataSource;
|
|
websiteId: string;
|
|
propertyName: string;
|
|
propertyFilters?: PropertyFilter[];
|
|
eventName?: string;
|
|
}) {
|
|
const { t, labels } = useMessages();
|
|
const { theme } = useTheme();
|
|
const { timezone } = useTimezone();
|
|
const { dateRange: { startDate, endDate, unit } } = useDateRange({ timezone });
|
|
const { locale, dateLocale } = useLocale();
|
|
const { colors } = useMemo(() => getThemeColors(theme), [theme]);
|
|
|
|
const sumQuery = usePropertyNumericSeriesQuery(source, websiteId, propertyName, 'sum', propertyFilters, eventName);
|
|
const avgQuery = usePropertyNumericSeriesQuery(source, websiteId, propertyName, 'avg', propertyFilters, eventName);
|
|
const statsQuery = usePropertyNumericStatsQuery(source, websiteId, propertyName, propertyFilters, eventName);
|
|
|
|
const sumRows = useMemo(() => (sumQuery.data as { t: string; y: number }[] | undefined) ?? [], [sumQuery.data]);
|
|
const avgRows = useMemo(() => (avgQuery.data as { t: string; y: number }[] | undefined) ?? [], [avgQuery.data]);
|
|
const stats = statsQuery.data;
|
|
|
|
const chartData: any = useMemo(() => {
|
|
if (!sumQuery.data && !avgQuery.data) return;
|
|
|
|
return {
|
|
datasets: [
|
|
{
|
|
type: 'bar',
|
|
label: t(labels.sum),
|
|
data: generateTimeSeries(
|
|
sumRows.map(({ t, y }) => ({ x: t, y })),
|
|
startDate,
|
|
endDate,
|
|
unit,
|
|
dateLocale,
|
|
),
|
|
barPercentage: 0.9,
|
|
categoryPercentage: 0.9,
|
|
...colors.chart.visitors,
|
|
borderWidth: 1,
|
|
},
|
|
{
|
|
type: 'bar',
|
|
label: t(labels.average),
|
|
data: generateTimeSeries(
|
|
avgRows.map(({ t, y }) => ({ x: t, y })),
|
|
startDate,
|
|
endDate,
|
|
unit,
|
|
dateLocale,
|
|
),
|
|
barPercentage: 0.9,
|
|
categoryPercentage: 0.9,
|
|
...colors.chart.views,
|
|
borderWidth: 1,
|
|
},
|
|
],
|
|
};
|
|
}, [avgQuery.data, avgRows, colors, dateLocale, endDate, labels.average, labels.sum, startDate, sumQuery.data, sumRows, t, unit]);
|
|
|
|
const renderXLabel = useCallback(renderDateLabels(unit, locale), [unit, locale]);
|
|
|
|
return (
|
|
<Column gap="4">
|
|
<LoadingPanel
|
|
data={stats}
|
|
isLoading={sumQuery.isLoading || avgQuery.isLoading || statsQuery.isLoading}
|
|
isFetching={sumQuery.isFetching || avgQuery.isFetching || statsQuery.isFetching}
|
|
error={sumQuery.error || avgQuery.error || statsQuery.error}
|
|
minHeight="100px"
|
|
>
|
|
<MetricsBar padding="2">
|
|
<MetricCard label={t(labels.total)} value={stats?.total ?? 0} formatValue={formatLongNumber} />
|
|
<MetricCard label={t(labels.average)} value={stats?.average ?? 0} formatValue={formatLongNumber} />
|
|
<MetricCard label="Median" value={stats?.median ?? 0} formatValue={formatLongNumber} />
|
|
<MetricCard label={t(labels.max)} value={stats?.max ?? 0} formatValue={formatLongNumber} />
|
|
<MetricCard label={t(labels.min)} value={stats?.min ?? 0} formatValue={formatLongNumber} />
|
|
</MetricsBar>
|
|
</LoadingPanel>
|
|
<LoadingPanel
|
|
isLoading={sumQuery.isLoading || avgQuery.isLoading || statsQuery.isLoading}
|
|
error={sumQuery.error || avgQuery.error || statsQuery.error}
|
|
minHeight="400px"
|
|
>
|
|
{chartData && (
|
|
<BarChart
|
|
chartData={chartData}
|
|
minDate={startDate}
|
|
maxDate={endDate}
|
|
unit={unit}
|
|
stacked={false}
|
|
renderXLabel={renderXLabel}
|
|
height="400px"
|
|
/>
|
|
)}
|
|
</LoadingPanel>
|
|
</Column>
|
|
);
|
|
}
|