From 3a737e7e2538917fd5e78844f22cd3679e14c5cc Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Wed, 18 Jun 2025 21:31:42 -0700 Subject: [PATCH] Fixed bar chart rendering. --- src/components/charts/BarChart.tsx | 7 ++-- src/components/hooks/useLocale.ts | 2 +- src/components/metrics/PageviewsChart.tsx | 15 ++++++-- src/lib/date.ts | 36 +++++++++++++++---- src/queries/sql/pageviews/getPageviewStats.ts | 2 +- 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/components/charts/BarChart.tsx b/src/components/charts/BarChart.tsx index 29322cab..345197bd 100644 --- a/src/components/charts/BarChart.tsx +++ b/src/components/charts/BarChart.tsx @@ -5,7 +5,7 @@ import { Chart, ChartProps } from '@/components/charts/Chart'; import { useLocale } from '@/components/hooks'; import { renderNumberLabels } from '@/lib/charts'; import { getThemeColors } from '@/lib/colors'; -import { formatDate } from '@/lib/date'; +import { formatDate, formatDateByUnit } from '@/lib/date'; import { formatLongCurrency, formatLongNumber } from '@/lib/format'; const dateFormats = { @@ -57,8 +57,9 @@ export function BarChart({ x: { type: XAxisType, stacked: true, - min: minDate, - max: maxDate, + min: formatDateByUnit(minDate, unit), + max: formatDateByUnit(maxDate, unit), + offset: true, time: { unit, }, diff --git a/src/components/hooks/useLocale.ts b/src/components/hooks/useLocale.ts index d270a944..8d96fdde 100644 --- a/src/components/hooks/useLocale.ts +++ b/src/components/hooks/useLocale.ts @@ -11,7 +11,7 @@ const messages = { 'en-US': enUS, }; -const selector = (state: { locale: any }) => state.locale; +const selector = (state: { locale: string }) => state.locale; export function useLocale() { const locale = useApp(selector); diff --git a/src/components/metrics/PageviewsChart.tsx b/src/components/metrics/PageviewsChart.tsx index 03a86e8d..00a2625a 100644 --- a/src/components/metrics/PageviewsChart.tsx +++ b/src/components/metrics/PageviewsChart.tsx @@ -4,6 +4,7 @@ import { BarChart, BarChartProps } from '@/components/charts/BarChart'; import { useLocale, useMessages } from '@/components/hooks'; import { renderDateLabels } from '@/lib/charts'; import { getThemeColors } from '@/lib/colors'; +import { formatDateByUnit } from '@/lib/date'; export interface PageviewsChartProps extends BarChartProps { data: { @@ -20,7 +21,7 @@ export interface PageviewsChartProps extends BarChartProps { export function PageviewsChart({ data, unit, ...props }: PageviewsChartProps) { const { formatMessage, labels } = useMessages(); const { theme } = useTheme(); - const { locale } = useLocale(); + const { locale, dateLocale } = useLocale(); const { colors } = useMemo(() => getThemeColors(theme), [theme]); const chartData: any = useMemo(() => { @@ -31,14 +32,18 @@ export function PageviewsChart({ data, unit, ...props }: PageviewsChartProps) { datasets: [ { label: formatMessage(labels.visitors), - data: data.sessions, + data: convertDataset(data.sessions, unit, dateLocale), borderWidth: 1, + barPercentage: 0.9, + categoryPercentage: 0.9, ...colors.chart.visitors, order: 3, }, { label: formatMessage(labels.views), - data: data.pageviews, + data: convertDataset(data.pageviews, unit, dateLocale), + barPercentage: 0.9, + categoryPercentage: 0.9, borderWidth: 1, ...colors.chart.views, order: 4, @@ -81,3 +86,7 @@ export function PageviewsChart({ data, unit, ...props }: PageviewsChartProps) { /> ); } + +function convertDataset(data: { x: string; y: number }[], unit: string, locale?: any) { + return data.map(d => ({ ...d, x: formatDateByUnit(d.x, unit, locale) })); +} diff --git a/src/lib/date.ts b/src/lib/date.ts index da5accaf..9a4cc783 100644 --- a/src/lib/date.ts +++ b/src/lib/date.ts @@ -34,6 +34,7 @@ import { subWeeks, endOfMinute, isSameDay, + parseISO, } from 'date-fns'; import { getDateLocale } from '@/lib/lang'; import { DateRange } from '@/lib/types'; @@ -128,7 +129,7 @@ export function parseDateValue(value: string) { } export function parseDateRange(value: string | object, locale = 'en-US'): DateRange { - if (typeof value === 'object') { + if (typeof value !== 'string') { return value as DateRange; } @@ -140,7 +141,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa }; } - if (value?.startsWith?.('range')) { + if (value.startsWith('range')) { const [, startTime, endTime] = value.split(':'); const startDate = new Date(+startTime); @@ -164,7 +165,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa switch (unit) { case 'hour': return { - startDate: num ? subHours(startOfHour(now), num - 1) : startOfHour(now), + startDate: num ? subHours(startOfHour(now), num) : startOfHour(now), endDate: endOfHour(now), offset: 0, num: num || 1, @@ -173,7 +174,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa }; case 'day': return { - startDate: num ? subDays(startOfDay(now), num - 1) : startOfDay(now), + startDate: num ? subDays(startOfDay(now), num) : startOfDay(now), endDate: endOfDay(now), unit: num ? 'day' : 'hour', offset: 0, @@ -183,7 +184,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa case 'week': return { startDate: num - ? subWeeks(startOfWeek(now, { locale: dateLocale }), num - 1) + ? subWeeks(startOfWeek(now, { locale: dateLocale }), num) : startOfWeek(now, { locale: dateLocale }), endDate: endOfWeek(now, { locale: dateLocale }), unit: 'day', @@ -193,7 +194,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa }; case 'month': return { - startDate: num ? subMonths(startOfMonth(now), num - 1) : startOfMonth(now), + startDate: num ? subMonths(startOfMonth(now), num) : startOfMonth(now), endDate: endOfMonth(now), unit: num ? 'month' : 'day', offset: 0, @@ -202,7 +203,7 @@ export function parseDateRange(value: string | object, locale = 'en-US'): DateRa }; case 'year': return { - startDate: num ? subYears(startOfYear(now), num - 1) : startOfYear(now), + startDate: num ? subYears(startOfYear(now), num) : startOfYear(now), endDate: endOfYear(now), unit: 'month', offset: 0, @@ -282,6 +283,27 @@ export function getMinimumUnit(startDate: number | Date, endDate: number | Date) return 'year'; } +export function formatDateByUnit(dateInput: string | Date, unit: string, locale?: any) { + const date = typeof dateInput === 'string' ? parseISO(dateInput) : dateInput; + + switch (unit) { + case 'minute': + return format(startOfMinute(date), 'yyyy-MM-dd HH:mm'); + case 'hour': + return format(startOfHour(date), 'yyyy-MM-dd HH'); + case 'day': + return format(startOfDay(date), 'yyyy-MM-dd'); + case 'week': + return format(startOfWeek(date, { locale }), "yyyy-'W'II"); + case 'month': + return format(startOfMonth(date), 'yyyy-MM'); + case 'year': + return format(startOfYear(date), 'yyyy'); + default: + return format(startOfDay(date), 'yyyy-MM-dd'); + } +} + export function getDateArray(data: any[], startDate: Date, endDate: Date, unit: string) { const arr = []; const { diff, add, start } = DATE_FUNCTIONS[unit]; diff --git a/src/queries/sql/pageviews/getPageviewStats.ts b/src/queries/sql/pageviews/getPageviewStats.ts index f5ace52c..fe696616 100644 --- a/src/queries/sql/pageviews/getPageviewStats.ts +++ b/src/queries/sql/pageviews/getPageviewStats.ts @@ -76,7 +76,7 @@ async function clickhouseQuery( from ( select ${getDateSQL('website_event.created_at', unit, timezone)} as t, - sum(views)as y + sum(views) as y from website_event_stats_hourly website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64}