From 530c21dac088ce76e2b66c7ed8bce3ff9ae69149 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Tue, 17 Feb 2026 21:55:35 -0800 Subject: [PATCH] add limit 50 to event chart, metrics table, and UTM report --- .../[websiteId]/events/EventsPage.tsx | 3 +- .../[websiteId]/events/series/route.ts | 4 +- .../queries/useWebsiteEventsSeriesQuery.ts | 20 +++++++-- src/components/metrics/EventsChart.tsx | 5 ++- src/queries/sql/events/getEventStats.ts | 44 ++++++++++++++++++- src/queries/sql/reports/getUTM.ts | 2 + 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx index f75f735ad..dc46fe41e 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -89,13 +89,14 @@ export function EventsPage({ websiteId }) { - + diff --git a/src/app/api/websites/[websiteId]/events/series/route.ts b/src/app/api/websites/[websiteId]/events/series/route.ts index 977e9c813..6060105c2 100644 --- a/src/app/api/websites/[websiteId]/events/series/route.ts +++ b/src/app/api/websites/[websiteId]/events/series/route.ts @@ -14,6 +14,7 @@ export async function GET( endAt: z.coerce.number().int(), unit: unitParam.optional(), timezone: timezoneParam, + limit: z.coerce.number().optional(), ...filterParams, }); @@ -29,9 +30,10 @@ export async function GET( return unauthorized(); } + const { limit } = query; const filters = await getQueryFilters(query, websiteId); - const data = await getEventStats(websiteId, filters); + const data = await getEventStats(websiteId, { limit }, filters); return json(data); } diff --git a/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts b/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts index 6c1d112dd..5318d0d9e 100644 --- a/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts +++ b/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts @@ -3,15 +3,29 @@ import { useApi } from '../useApi'; import { useDateParameters } from '../useDateParameters'; import { useFilterParameters } from '../useFilterParameters'; -export function useWebsiteEventsSeriesQuery(websiteId: string, options?: ReactQueryOptions) { +export function useWebsiteEventsSeriesQuery( + websiteId: string, + params?: { limit?: number }, + options?: ReactQueryOptions, +) { const { get, useQuery } = useApi(); const { startAt, endAt, unit, timezone } = useDateParameters(); const filters = useFilterParameters(); return useQuery({ - queryKey: ['websites:events:series', { websiteId, startAt, endAt, unit, timezone, ...filters }], + queryKey: [ + 'websites:events:series', + { websiteId, startAt, endAt, unit, timezone, ...filters, ...params }, + ], queryFn: () => - get(`/websites/${websiteId}/events/series`, { startAt, endAt, unit, timezone, ...filters }), + get(`/websites/${websiteId}/events/series`, { + startAt, + endAt, + unit, + timezone, + ...filters, + ...params, + }), enabled: !!websiteId, ...options, }); diff --git a/src/components/metrics/EventsChart.tsx b/src/components/metrics/EventsChart.tsx index 3a53ba9a6..db23dcc47 100644 --- a/src/components/metrics/EventsChart.tsx +++ b/src/components/metrics/EventsChart.tsx @@ -15,15 +15,16 @@ import { generateTimeSeries } from '@/lib/date'; export interface EventsChartProps extends BarChartProps { websiteId: string; focusLabel?: string; + limit?: number; } -export function EventsChart({ websiteId, focusLabel }: EventsChartProps) { +export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps) { const { timezone } = useTimezone(); const { dateRange: { startDate, endDate, unit }, } = useDateRange({ timezone: timezone }); const { locale, dateLocale } = useLocale(); - const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId); + const { data, isLoading, error } = useWebsiteEventsSeriesQuery(websiteId, { limit }); const [label, setLabel] = useState(focusLabel); const chartData: any = useMemo(() => { diff --git a/src/queries/sql/events/getEventStats.ts b/src/queries/sql/events/getEventStats.ts index 81d12a0ff..2caba3268 100644 --- a/src/queries/sql/events/getEventStats.ts +++ b/src/queries/sql/events/getEventStats.ts @@ -6,6 +6,10 @@ import type { QueryFilters } from '@/lib/types'; const FUNCTION_NAME = 'getEventStats'; +export interface EventStatsParameters { + limit?: number | string; +} + interface WebsiteEventMetric { x: string; t: string; @@ -13,7 +17,7 @@ interface WebsiteEventMetric { } export async function getEventStats( - ...args: [websiteId: string, filters: QueryFilters] + ...args: [websiteId: string, parameters: EventStatsParameters, filters: QueryFilters] ): Promise { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -21,7 +25,12 @@ export async function getEventStats( }); } -async function relationalQuery(websiteId: string, filters: QueryFilters) { +async function relationalQuery( + websiteId: string, + parameters: EventStatsParameters, + filters: QueryFilters, +) { + const { limit } = parameters; const { timezone = 'utc', unit = 'day' } = filters; const { rawQuery, getDateSQL, parseFilters } = prisma; const { filterQuery, cohortQuery, joinSessionQuery, queryParams } = parseFilters({ @@ -30,6 +39,19 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { eventType: EVENT_TYPE.customEvent, }); + const limitQuery = limit + ? `and event_name in ( + select event_name + from website_event + where website_id = {{websiteId::uuid}} + and created_at between {{startDate}} and {{endDate}} + and event_type = 2 + group by event_name + order by count(*) desc + limit ${limit} + )` + : ''; + return rawQuery( ` select @@ -42,6 +64,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} ${filterQuery} + ${limitQuery} group by 1, 2 order by 2 `, @@ -52,8 +75,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { async function clickhouseQuery( websiteId: string, + parameters: EventStatsParameters, filters: QueryFilters, ): Promise<{ x: string; t: string; y: number }[]> { + const { limit } = parameters; const { timezone = 'UTC', unit = 'day' } = filters; const { rawQuery, getDateSQL, parseFilters } = clickhouse; const { filterQuery, cohortQuery, queryParams } = parseFilters({ @@ -62,6 +87,19 @@ async function clickhouseQuery( eventType: EVENT_TYPE.customEvent, }); + const limitQuery = limit + ? `and event_name in ( + select event_name + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = {eventType:UInt32} + group by event_name + order by count(*) desc + limit ${limit} + )` + : ''; + let sql = ''; if (filterQuery || cohortQuery) { @@ -75,6 +113,7 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} + ${limitQuery} group by x, t order by t `; @@ -91,6 +130,7 @@ async function clickhouseQuery( where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type = {eventType:UInt32} + ${limitQuery} ) as g group by x, t order by t diff --git a/src/queries/sql/reports/getUTM.ts b/src/queries/sql/reports/getUTM.ts index 4d43eb45b..f470e954c 100644 --- a/src/queries/sql/reports/getUTM.ts +++ b/src/queries/sql/reports/getUTM.ts @@ -47,6 +47,7 @@ async function relationalQuery( ${filterQuery} group by 1 order by 2 desc + limit 50 `, queryParams, ); @@ -78,6 +79,7 @@ async function clickhouseQuery( ${filterQuery} group by 1 order by 2 desc + limit 50 `, queryParams, );