diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx index f2bf7cb9..49b31656 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -5,7 +5,6 @@ import { ReactNode } from 'react'; export default function EventsDataTable({ websiteId, - children, }: { websiteId?: string; teamId?: string; @@ -13,12 +12,8 @@ export default function EventsDataTable({ }) { const queryResult = useWebsiteEvents(websiteId); - if (queryResult?.result?.data?.length === 0) { - return children; - } - return ( - + {({ data }) => } ); diff --git a/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx new file mode 100644 index 00000000..f77c6c04 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/events/EventsMetricsBar.tsx @@ -0,0 +1,42 @@ +import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; +import { Flexbox } from 'react-basics'; +import MetricsBar from 'components/metrics/MetricsBar'; +import MetricCard from 'components/metrics/MetricCard'; +import { useMessages } from 'components/hooks'; +import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; +import { formatLongNumber } from 'lib/format'; + +export function EventsMetricsBar({ websiteId }: { websiteId: string }) { + const { formatMessage, labels } = useMessages(); + const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); + + return ( + + + + + + + + + + ); +} + +export default EventsMetricsBar; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx index 8f793d81..e35442d0 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx @@ -1,12 +1,31 @@ 'use client'; import WebsiteHeader from '../WebsiteHeader'; import EventsDataTable from './EventsDataTable'; +import EventsMetricsBar from './EventsMetricsBar'; +import EventsChart from 'components/metrics/EventsChart'; +import { GridRow } from 'components/layout/Grid'; +import MetricsTable from 'components/metrics/MetricsTable'; +import { useMessages } from 'components/hooks'; export default function EventsPage({ websiteId }) { + const { formatMessage, labels } = useMessages(); + return ( <> - + + + + + + + + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx index 6d3c3579..9133ca71 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsMetricsBar.tsx @@ -1,5 +1,6 @@ import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; -import { Flexbox, Loading } from 'react-basics'; +import { Flexbox } from 'react-basics'; +import MetricsBar from 'components/metrics/MetricsBar'; import MetricCard from 'components/metrics/MetricCard'; import { useMessages } from 'components/hooks'; import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; @@ -7,33 +8,32 @@ import { formatLongNumber } from 'lib/format'; export function SessionsMetricsBar({ websiteId }: { websiteId: string }) { const { formatMessage, labels } = useMessages(); - const { data, isLoading } = useWebsiteStats(websiteId); + const { data, isLoading, isFetched, error } = useWebsiteStats(websiteId); return ( - - - {isLoading && } - {!isLoading && data && ( - <> - - - - - - )} - + + + + + + + ); diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index 0f011c33..775e123c 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -34,7 +34,7 @@ export function DataTable({ const { page, pageSize, count, data } = result || {}; const { query } = params || {}; const hasData = Boolean(!isLoading && data?.length); - const noResults = Boolean(!isLoading && query && !hasData); + const noResults = Boolean(query && !hasData); const { router, renderUrl } = useNavigation(); const handleSearch = (query: string) => { diff --git a/src/components/hooks/queries/useWebsiteSessions.ts b/src/components/hooks/queries/useWebsiteSessions.ts index e8a1ce2f..ce65512c 100644 --- a/src/components/hooks/queries/useWebsiteSessions.ts +++ b/src/components/hooks/queries/useWebsiteSessions.ts @@ -15,6 +15,7 @@ export function useWebsiteSessions(websiteId: string, params?: { [key: string]: ...data, ...params, ...filters, + pageSize: 20, }); }, }); diff --git a/src/components/input/DateFilter.module.css b/src/components/input/DateFilter.module.css new file mode 100644 index 00000000..bd9ec1a8 --- /dev/null +++ b/src/components/input/DateFilter.module.css @@ -0,0 +1,3 @@ +.dropdown span { + white-space: nowrap; +} diff --git a/src/components/input/DateFilter.tsx b/src/components/input/DateFilter.tsx index 6e7c099a..e486551d 100644 --- a/src/components/input/DateFilter.tsx +++ b/src/components/input/DateFilter.tsx @@ -5,6 +5,8 @@ import DatePickerForm from 'components/metrics/DatePickerForm'; import { useLocale, useMessages } from 'components/hooks'; import Icons from 'components/icons'; import { formatDate, parseDateValue } from 'lib/date'; +import styles from './DateFilter.module.css'; +import classNames from 'classnames'; export interface DateFilterProps { value: string; @@ -123,7 +125,7 @@ export function DateFilter({ return ( <> 0)` : ''} order by created_at desc `, - params, + { ...params, query }, pageParams, ); } diff --git a/src/queries/analytics/getWebsiteStats.ts b/src/queries/analytics/getWebsiteStats.ts index 7f7f4f19..a33011ff 100644 --- a/src/queries/analytics/getWebsiteStats.ts +++ b/src/queries/analytics/getWebsiteStats.ts @@ -1,5 +1,4 @@ import clickhouse from 'lib/clickhouse'; -import { EVENT_TYPE } from 'lib/constants'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import prisma from 'lib/prisma'; import { QueryFilters } from 'lib/types'; @@ -25,7 +24,6 @@ async function relationalQuery( const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { filterQuery, joinSession, params } = await parseFilters(websiteId, { ...filters, - eventType: EVENT_TYPE.pageView, }); return rawQuery( @@ -35,12 +33,15 @@ async function relationalQuery( count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", count(distinct t.country) as "countries", + count(t.event_name) as "countries", + sum(case when t.event_type = 2 then 1 else 0 end) as "events", sum(case when t.c = 1 then 1 else 0 end) as "bounces", sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime", from ( select website_event.session_id, website_event.visit_id, + website_event.event_type, session.country, count(*) as "c", min(website_event.created_at) as "min_time", @@ -49,9 +50,8 @@ async function relationalQuery( ${joinSession} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} - and event_type = {{eventType}} ${filterQuery} - group by 1, 2, 3 + group by 1, 2, 3, 4 ) as t `, params, @@ -67,7 +67,6 @@ async function clickhouseQuery( const { rawQuery, parseFilters } = clickhouse; const { filterQuery, params } = await parseFilters(websiteId, { ...filters, - eventType: EVENT_TYPE.pageView, }); let sql = ''; @@ -79,12 +78,14 @@ async function clickhouseQuery( uniq(t.session_id) as "visitors", uniq(t.visit_id) as "visits", uniq(t.country) as "countries", - sum(if(t.c = 1, 1, 0)) as "bounces", + sumIf(1, t.event_type = 2) as "events", + sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, + event_type, country, count(*) c, min(created_at) min_time, @@ -92,9 +93,8 @@ async function clickhouseQuery( from website_event where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id, country + group by session_id, visit_id, event_type, country ) as t; `; } else { @@ -104,12 +104,14 @@ async function clickhouseQuery( uniq(session_id) as "visitors", uniq(visit_id) as "visits", uniq(country) as "countries", + sumIf(1, t.event_type = 2) as "events", sumIf(1, t.c = 1) as "bounces", sum(max_time-min_time) as "totaltime" from ( select session_id, visit_id, + event_type, country, sum(views) c, min(min_time) min_time, @@ -117,9 +119,8 @@ async function clickhouseQuery( from umami.website_event_stats_hourly "website_event" where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - and event_type = {eventType:UInt32} ${filterQuery} - group by session_id, visit_id, country + group by session_id, visit_id, event_type, country ) as t; `; } @@ -133,6 +134,7 @@ async function clickhouseQuery( bounces: Number(a.bounces), totaltime: Number(a.totaltime), countries: Number(a.countries), + events: Number(a.events), }; }); }); diff --git a/src/queries/analytics/sessions/getWebsiteSessions.ts b/src/queries/analytics/sessions/getWebsiteSessions.ts index 94d2827e..26852616 100644 --- a/src/queries/analytics/sessions/getWebsiteSessions.ts +++ b/src/queries/analytics/sessions/getWebsiteSessions.ts @@ -14,10 +14,12 @@ export async function getWebsiteSessions( async function relationalQuery(websiteId: string, filters: QueryFilters, pageParams: PageParams) { const { pagedQuery } = prisma; + const { query } = pageParams; const where = { ...filters, id: websiteId, + ...prisma.getSearchParameters(query, [{ eventName: 'contains' }, { urlPath: 'contains' }]), }; return pagedQuery('session', { where }, pageParams); @@ -26,6 +28,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar async function clickhouseQuery(websiteId: string, filters: QueryFilters, pageParams?: PageParams) { const { pagedQuery, parseFilters } = clickhouse; const { params, dateQuery, filterQuery } = await parseFilters(websiteId, filters); + const { query } = pageParams; return pagedQuery( ` @@ -49,6 +52,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, pagePar where website_id = {websiteId:UUID} ${dateQuery} ${filterQuery} + ${query ? `and (positionCaseInsensitive(event_name, {query:String}) > 0)` : ''} group by session_id, website_id, hostname, browser, os, device, screen, language, country, subdivision1, city order by lastAt desc `,