From a2b1089e62c0c0a57f9c2d01f3a8ce1e648d5759 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 15 Oct 2025 11:32:19 -0700 Subject: [PATCH 1/3] clean-up event/event-data endpoints. fix for expanded view for mobile --- .../[websiteId]/WebsiteExpandedView.tsx | 8 +++++++- .../[websiteId]/event-data/[eventId]/route.ts | 2 +- .../[websiteId]/event-data/events/route.ts | 2 ++ .../api/websites/[websiteId]/events/route.ts | 9 +++++---- .../hooks/queries/useWebsiteEventsQuery.ts | 12 +++++++++--- .../queries/useWebsiteEventsSeriesQuery.ts | 7 ++++--- src/queries/sql/events/getEventData.ts | 18 +++++++++++------- src/queries/sql/events/getEventDataEvents.ts | 10 ++++++++-- 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx index 3fc6a1de..05de798d 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx @@ -18,7 +18,13 @@ export function WebsiteExpandedView({ } = useNavigation(); return ( - + }, ) { const schema = z.object({ - ...dateRangeParams, + startAt: z.coerce.number().optional(), + endAt: z.coerce.number().optional(), ...filterParams, ...pagingParams, ...searchParams, diff --git a/src/components/hooks/queries/useWebsiteEventsQuery.ts b/src/components/hooks/queries/useWebsiteEventsQuery.ts index 51902d2f..9d89be52 100644 --- a/src/components/hooks/queries/useWebsiteEventsQuery.ts +++ b/src/components/hooks/queries/useWebsiteEventsQuery.ts @@ -15,14 +15,20 @@ export function useWebsiteEventsQuery( options?: ReactQueryOptions, ) { const { get } = useApi(); - const date = useDateParameters(); + const { startAt, endAt, unit, timezone } = useDateParameters(); const filters = useFilterParameters(); return usePagedQuery({ - queryKey: ['websites:events', { websiteId, ...date, ...filters, ...params }], + queryKey: [ + 'websites:events', + { websiteId, startAt, endAt, unit, timezone, ...filters, ...params }, + ], queryFn: pageParams => get(`/websites/${websiteId}/events`, { - ...date, + startAt, + endAt, + unit, + timezone, ...filters, ...pageParams, eventType: EVENT_TYPES[params.view], diff --git a/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts b/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts index 50861cee..5db85278 100644 --- a/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts +++ b/src/components/hooks/queries/useWebsiteEventsSeriesQuery.ts @@ -5,12 +5,13 @@ import { ReactQueryOptions } from '@/lib/types'; export function useWebsiteEventsSeriesQuery(websiteId: string, options?: ReactQueryOptions) { const { get, useQuery } = useApi(); - const date = useDateParameters(); + const { startAt, endAt, unit, timezone } = useDateParameters(); const filters = useFilterParameters(); return useQuery({ - queryKey: ['websites:events:series', { websiteId, ...date, ...filters }], - queryFn: () => get(`/websites/${websiteId}/events/series`, { ...date, ...filters }), + queryKey: ['websites:events:series', { websiteId, startAt, endAt, unit, timezone, ...filters }], + queryFn: () => + get(`/websites/${websiteId}/events/series`, { startAt, endAt, unit, timezone, ...filters }), enabled: !!websiteId, ...options, }); diff --git a/src/queries/sql/events/getEventData.ts b/src/queries/sql/events/getEventData.ts index f8ba2eef..52cb5300 100644 --- a/src/queries/sql/events/getEventData.ts +++ b/src/queries/sql/events/getEventData.ts @@ -5,14 +5,16 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db'; const FUNCTION_NAME = 'getEventData'; -export async function getEventData(...args: [eventId: string]): Promise { +export async function getEventData( + ...args: [websiteId: string, eventId: string] +): Promise { return runQuery({ [PRISMA]: () => relationalQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args), }); } -async function relationalQuery(eventId: string) { +async function relationalQuery(websiteId: string, eventId: string) { const { rawQuery } = prisma; return rawQuery( @@ -29,14 +31,15 @@ async function relationalQuery(eventId: string) { data_type as dataType, created_at as createdAt from event_data - where event_id = {{eventId::uuid}} + website_id = {{websiteId::uuid}} + event_id = {{eventId::uuid}} `, - { eventId }, + { websiteId, eventId }, FUNCTION_NAME, ); } -async function clickhouseQuery(eventId: string): Promise { +async function clickhouseQuery(websiteId: string, eventId: string): Promise { const { rawQuery } = clickhouse; return rawQuery( @@ -53,9 +56,10 @@ async function clickhouseQuery(eventId: string): Promise { data_type as dataType, created_at as createdAt from event_data - where event_id = {eventId:UUID} + where website_id = {websiteId:UUID} + and event_id = {eventId:UUID} `, - { eventId }, + { websiteId, eventId }, FUNCTION_NAME, ); } diff --git a/src/queries/sql/events/getEventDataEvents.ts b/src/queries/sql/events/getEventDataEvents.ts index f7bea46c..c8c1e0a0 100644 --- a/src/queries/sql/events/getEventDataEvents.ts +++ b/src/queries/sql/events/getEventDataEvents.ts @@ -25,7 +25,10 @@ export async function getEventDataEvents( async function relationalQuery(websiteId: string, filters: QueryFilters) { const { rawQuery, parseFilters } = prisma; const { event } = filters; - const { queryParams } = parseFilters(filters); + const { queryParams } = parseFilters({ + ...filters, + websiteId, + }); if (event) { return rawQuery( @@ -75,7 +78,10 @@ async function clickhouseQuery( ): Promise<{ eventName: string; propertyName: string; dataType: number; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { event } = filters; - const { queryParams } = parseFilters(filters); + const { queryParams } = parseFilters({ + ...filters, + websiteId, + }); if (event) { return rawQuery( From a7a7293d73b7d76064555b0042f2199d1784ffbb Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Wed, 15 Oct 2025 12:03:40 -0700 Subject: [PATCH 2/3] dvh height for mobile expanded view --- src/app/(main)/websites/[websiteId]/ExpandedViewModal.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/(main)/websites/[websiteId]/ExpandedViewModal.tsx b/src/app/(main)/websites/[websiteId]/ExpandedViewModal.tsx index 76a17fbb..9e50788a 100644 --- a/src/app/(main)/websites/[websiteId]/ExpandedViewModal.tsx +++ b/src/app/(main)/websites/[websiteId]/ExpandedViewModal.tsx @@ -26,9 +26,11 @@ export function ExpandedViewModal({ } }; + const height = CSS.supports('height', '100dvh') ? 'calc(100dvh - 40px)' : 'calc(100vh - 40px)'; + return ( - + {({ close }) => { return ( Date: Wed, 15 Oct 2025 16:44:14 -0700 Subject: [PATCH 3/3] clean up events/event-data endpoints --- .../[websiteId]/event-data/events/route.ts | 2 -- .../[websiteId]/event-data/fields/route.ts | 2 ++ .../event-data/properties/route.ts | 13 +++++----- .../[websiteId]/event-data/stats/route.ts | 3 ++- .../[websiteId]/event-data/values/route.ts | 12 ++++----- .../hooks/queries/useEventDataValuesQuery.ts | 6 ++--- src/queries/sql/events/getEventDataEvents.ts | 26 ++++++++++++++----- src/queries/sql/events/getEventDataFields.ts | 11 +++++--- src/queries/sql/events/getEventDataStats.ts | 11 +++++--- src/queries/sql/events/getEventDataValues.ts | 11 +++----- 10 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/app/api/websites/[websiteId]/event-data/events/route.ts b/src/app/api/websites/[websiteId]/event-data/events/route.ts index 4a490c03..2420f0fa 100644 --- a/src/app/api/websites/[websiteId]/event-data/events/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/events/route.ts @@ -27,12 +27,10 @@ export async function GET( return unauthorized(); } - const { event } = query; const filters = await getQueryFilters(query, websiteId); const data = await getEventDataEvents(websiteId, { ...filters, - event, }); return json(data); diff --git a/src/app/api/websites/[websiteId]/event-data/fields/route.ts b/src/app/api/websites/[websiteId]/event-data/fields/route.ts index 49ea9628..cfee4967 100644 --- a/src/app/api/websites/[websiteId]/event-data/fields/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/fields/route.ts @@ -3,6 +3,7 @@ import { getQueryFilters, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/permissions'; import { getEventDataFields } from '@/queries/sql'; +import { filterParams } from '@/lib/schema'; export async function GET( request: Request, @@ -11,6 +12,7 @@ export async function GET( const schema = z.object({ startAt: z.coerce.number().int(), endAt: z.coerce.number().int(), + ...filterParams, }); const { auth, query, error } = await parseRequest(request, schema); diff --git a/src/app/api/websites/[websiteId]/event-data/properties/route.ts b/src/app/api/websites/[websiteId]/event-data/properties/route.ts index c3b884ae..df513e5a 100644 --- a/src/app/api/websites/[websiteId]/event-data/properties/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/properties/route.ts @@ -1,17 +1,17 @@ -import { z } from 'zod'; import { getQueryFilters, parseRequest } from '@/lib/request'; -import { unauthorized, json } from '@/lib/response'; +import { json, unauthorized } from '@/lib/response'; +import { filterParams } from '@/lib/schema'; import { canViewWebsite } from '@/permissions'; import { getEventDataProperties } from '@/queries/sql'; -import { dateRangeParams, filterParams } from '@/lib/schema'; +import { z } from 'zod'; export async function GET( request: Request, { params }: { params: Promise<{ websiteId: string }> }, ) { const schema = z.object({ - propertyName: z.string().optional(), - ...dateRangeParams, + startAt: z.coerce.number().int(), + endAt: z.coerce.number().int(), ...filterParams, }); @@ -27,10 +27,9 @@ export async function GET( return unauthorized(); } - const { propertyName } = query; const filters = await getQueryFilters(query, websiteId); - const data = await getEventDataProperties(websiteId, { ...filters, propertyName }); + const data = await getEventDataProperties(websiteId, filters); return json(data); } diff --git a/src/app/api/websites/[websiteId]/event-data/stats/route.ts b/src/app/api/websites/[websiteId]/event-data/stats/route.ts index b10ffbbc..bb482e2c 100644 --- a/src/app/api/websites/[websiteId]/event-data/stats/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/stats/route.ts @@ -3,6 +3,7 @@ import { getQueryFilters, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/permissions'; import { getEventDataStats } from '@/queries/sql'; +import { filterParams } from '@/lib/schema'; export async function GET( request: Request, @@ -11,7 +12,7 @@ export async function GET( const schema = z.object({ startAt: z.coerce.number().int(), endAt: z.coerce.number().int(), - propertyName: z.string().optional(), + ...filterParams, }); const { auth, query, error } = await parseRequest(request, schema); diff --git a/src/app/api/websites/[websiteId]/event-data/values/route.ts b/src/app/api/websites/[websiteId]/event-data/values/route.ts index 5cf40665..2921af9f 100644 --- a/src/app/api/websites/[websiteId]/event-data/values/route.ts +++ b/src/app/api/websites/[websiteId]/event-data/values/route.ts @@ -3,16 +3,17 @@ import { getQueryFilters, parseRequest } from '@/lib/request'; import { unauthorized, json } from '@/lib/response'; import { canViewWebsite } from '@/permissions'; import { getEventDataValues } from '@/queries/sql'; -import { dateRangeParams, filterParams } from '@/lib/schema'; +import { filterParams } from '@/lib/schema'; export async function GET( request: Request, { params }: { params: Promise<{ websiteId: string }> }, ) { const schema = z.object({ - eventName: z.string().optional(), - propertyName: z.string().optional(), - ...dateRangeParams, + startAt: z.coerce.number().int(), + endAt: z.coerce.number().int(), + event: z.string(), + propertyName: z.string(), ...filterParams, }); @@ -28,12 +29,11 @@ export async function GET( return unauthorized(); } - const { eventName, propertyName } = query; + const { propertyName } = query; const filters = await getQueryFilters(query, websiteId); const data = await getEventDataValues(websiteId, { ...filters, - eventName, propertyName, }); diff --git a/src/components/hooks/queries/useEventDataValuesQuery.ts b/src/components/hooks/queries/useEventDataValuesQuery.ts index 6394a1bb..18bb6b2e 100644 --- a/src/components/hooks/queries/useEventDataValuesQuery.ts +++ b/src/components/hooks/queries/useEventDataValuesQuery.ts @@ -5,7 +5,7 @@ import { useFilterParameters } from '../useFilterParameters'; export function useEventDataValuesQuery( websiteId: string, - eventName: string, + event: string, propertyName: string, options?: ReactQueryOptions, ) { @@ -16,7 +16,7 @@ export function useEventDataValuesQuery( return useQuery({ queryKey: [ 'websites:event-data:values', - { websiteId, eventName, propertyName, startAt, endAt, unit, timezone, ...filters }, + { websiteId, event, propertyName, startAt, endAt, unit, timezone, ...filters }, ], queryFn: () => get(`/websites/${websiteId}/event-data/values`, { @@ -25,7 +25,7 @@ export function useEventDataValuesQuery( unit, timezone, ...filters, - eventName, + event, propertyName, }), enabled: !!(websiteId && propertyName), diff --git a/src/queries/sql/events/getEventDataEvents.ts b/src/queries/sql/events/getEventDataEvents.ts index c8c1e0a0..0d856371 100644 --- a/src/queries/sql/events/getEventDataEvents.ts +++ b/src/queries/sql/events/getEventDataEvents.ts @@ -78,7 +78,7 @@ async function clickhouseQuery( ): Promise<{ eventName: string; propertyName: string; dataType: number; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { event } = filters; - const { queryParams } = parseFilters({ + const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -93,9 +93,16 @@ async function clickhouseQuery( string_value as propertyValue, count(*) as total from event_data - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_name = {event:String} + join website_event + on website_event.event_id = event_data.event_id + and website_event.website_id = event_data.website_id + and website_event.website_id = {websiteId:UUID} + and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64} + ${cohortQuery} + where event_data.website_id = {websiteId:UUID} + and event_data.created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_data.event_name = {event:String} + ${filterQuery} group by data_key, data_type, string_value, event_name order by 1 asc, 2 asc, 3 asc, 5 desc limit 500 @@ -113,8 +120,15 @@ async function clickhouseQuery( data_type as dataType, count(*) as total from event_data - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} + join website_event + on website_event.event_id = event_data.event_id + and website_event.website_id = event_data.website_id + and website_event.website_id = {websiteId:UUID} + and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64} + ${cohortQuery} + where event_data.website_id = {websiteId:UUID} + and event_data.created_at between {startDate:DateTime64} and {endDate:DateTime64} + ${filterQuery} group by data_key, data_type, event_name order by 1 asc, 2 asc limit 500 diff --git a/src/queries/sql/events/getEventDataFields.ts b/src/queries/sql/events/getEventDataFields.ts index d62af2d4..42c46cd1 100644 --- a/src/queries/sql/events/getEventDataFields.ts +++ b/src/queries/sql/events/getEventDataFields.ts @@ -64,10 +64,15 @@ async function clickhouseQuery( data_type = 4, toString(date_trunc('hour', date_value)), string_value) as "value", count(*) as "total" - from event_data website_event + from event_data + join website_event + on website_event.event_id = event_data.event_id + and website_event.website_id = event_data.website_id + and website_event.website_id = {websiteId:UUID} + and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64} ${cohortQuery} - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} + where event_data.website_id = {websiteId:UUID} + and event_data.created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} group by data_key, data_type, value order by 2 desc diff --git a/src/queries/sql/events/getEventDataStats.ts b/src/queries/sql/events/getEventDataStats.ts index 606a49ea..28e2f4d0 100644 --- a/src/queries/sql/events/getEventDataStats.ts +++ b/src/queries/sql/events/getEventDataStats.ts @@ -71,10 +71,15 @@ async function clickhouseQuery( event_id, data_key, count(*) as "total" - from event_data website_event + from event_data + join website_event + on website_event.event_id = event_data.event_id + and website_event.website_id = event_data.website_id + and website_event.website_id = {websiteId:UUID} + and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64} ${cohortQuery} - where website_id = {websiteId:UUID} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} + where event_data.website_id = {websiteId:UUID} + and event_data.created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} group by event_id, data_key ) as t diff --git a/src/queries/sql/events/getEventDataValues.ts b/src/queries/sql/events/getEventDataValues.ts index 0af93830..ad7dd1a3 100644 --- a/src/queries/sql/events/getEventDataValues.ts +++ b/src/queries/sql/events/getEventDataValues.ts @@ -11,10 +11,7 @@ interface WebsiteEventData { } export async function getEventDataValues( - ...args: [ - websiteId: string, - filters: QueryFilters & { eventName?: string; propertyName?: string }, - ] + ...args: [websiteId: string, filters: QueryFilters & { propertyName?: string }] ): Promise { return runQuery({ [PRISMA]: () => relationalQuery(...args), @@ -24,7 +21,7 @@ export async function getEventDataValues( async function relationalQuery( websiteId: string, - filters: QueryFilters & { eventName?: string; propertyName?: string }, + filters: QueryFilters & { propertyName?: string }, ) { const { rawQuery, parseFilters, getDateSQL } = prisma; const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ @@ -63,7 +60,7 @@ async function relationalQuery( async function clickhouseQuery( websiteId: string, - filters: QueryFilters & { eventName?: string; propertyName?: string }, + filters: QueryFilters & { propertyName?: string }, ): Promise<{ value: string; total: number }[]> { const { rawQuery, parseFilters } = clickhouse; const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId }); @@ -85,7 +82,7 @@ async function clickhouseQuery( where event_data.website_id = {websiteId:UUID} and event_data.created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_data.data_key = {propertyName:String} - and event_data.event_name = {eventName:String} + and event_data.event_name = {event:String} ${filterQuery} group by value order by 2 desc