From bf548c5acae58cc1420d3ae7e79127f3db654e81 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Sun, 9 Nov 2025 21:19:38 -0800 Subject: [PATCH 1/5] Fix revenue bigInt but and case insensitive currency --- src/queries/sql/reports/getRevenue.ts | 38 +++++++++++---------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/queries/sql/reports/getRevenue.ts b/src/queries/sql/reports/getRevenue.ts index e13106ce..5771bdef 100644 --- a/src/queries/sql/reports/getRevenue.ts +++ b/src/queries/sql/reports/getRevenue.ts @@ -41,6 +41,15 @@ async function relationalQuery( currency, }); + const joinQuery = filterQuery + ? `join website_event + on website_event.website_id = revenue.website_id + and website_event.session_id = revenue.session_id + and website_event.event_id = revenue.event_id + and website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}}` + : ''; + const chart = await rawQuery( ` select @@ -48,17 +57,12 @@ async function relationalQuery( ${getDateSQL('revenue.created_at', unit, timezone)} t, sum(revenue.revenue) y from revenue - join website_event - on website_event.website_id = revenue.website_id - and website_event.session_id = revenue.session_id - and website_event.event_id = revenue.event_id - and website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} + ${joinQuery} ${cohortQuery} ${joinSessionQuery} where revenue.website_id = {{websiteId::uuid}} and revenue.created_at between {{startDate}} and {{endDate}} - and revenue.currency like {{currency}} + and revenue.currency ilike {{currency}} ${filterQuery} group by x, t order by t @@ -72,19 +76,14 @@ async function relationalQuery( session.country as name, sum(revenue) value from revenue - join website_event - on website_event.website_id = revenue.website_id - and website_event.session_id = revenue.session_id - and website_event.event_id = revenue.event_id - and website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} + ${joinQuery} join session on session.website_id = revenue.website_id and session.session_id = revenue.session_id ${cohortQuery} where revenue.website_id = {{websiteId::uuid}} and revenue.created_at between {{startDate}} and {{endDate}} - and revenue.currency = {{currency}} + and revenue.currency ilike {{currency}} ${filterQuery} group by session.country `, @@ -98,23 +97,18 @@ async function relationalQuery( count(distinct revenue.event_id) as count, count(distinct revenue.session_id) as unique_count from revenue - join website_event - on website_event.website_id = revenue.website_id - and website_event.session_id = revenue.session_id - and website_event.event_id = revenue.event_id - and website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} + ${joinQuery} ${cohortQuery} ${joinSessionQuery} where revenue.website_id = {{websiteId::uuid}} and revenue.created_at between {{startDate}} and {{endDate}} - and revenue.currency = {{currency}} + and revenue.currency ilike {{currency}} ${filterQuery} `, queryParams, ).then(result => result?.[0]); - total.average = total.count > 0 ? total.sum / total.count : 0; + total.average = total.count > 0 ? Number(total.sum) / Number(total.count) : 0; return { chart, country, total }; } From f30724629cb4a6b4c401c78e83085b7671e1fbe4 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Sun, 9 Nov 2025 21:37:35 -0800 Subject: [PATCH 2/5] Fix null and string return types from getWebsiteStats --- src/queries/sql/getWebsiteStats.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/queries/sql/getWebsiteStats.ts b/src/queries/sql/getWebsiteStats.ts index 391d22be..4a4bef78 100644 --- a/src/queries/sql/getWebsiteStats.ts +++ b/src/queries/sql/getWebsiteStats.ts @@ -36,11 +36,11 @@ async function relationalQuery( return rawQuery( ` select - sum(t.c) as "pageviews", + cast(coalesce(sum(t.c), 0) as bigint) as "pageviews", count(distinct t.session_id) as "visitors", count(distinct t.visit_id) as "visits", - sum(case when t.c = 1 then 1 else 0 end) as "bounces", - sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" + coalesce(sum(case when t.c = 1 then 1 else 0 end), 0) as "bounces", + cast(coalesce(sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}), 0) as bigint) as "totaltime" from ( select website_event.session_id, From 9230f3cb7b18203614fe2856d216540916be785c Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Sun, 9 Nov 2025 22:03:06 -0800 Subject: [PATCH 3/5] manually include basePath --- src/app/(main)/App.tsx | 2 +- src/app/logout/LogoutPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/(main)/App.tsx b/src/app/(main)/App.tsx index 7700639d..ec08838d 100644 --- a/src/app/(main)/App.tsx +++ b/src/app/(main)/App.tsx @@ -16,7 +16,7 @@ export function App({ children }) { } if (error) { - window.location.href = '/login'; + window.location.href = `${process.env.basePath || ''}/login`; return null; } diff --git a/src/app/logout/LogoutPage.tsx b/src/app/logout/LogoutPage.tsx index bd471796..d66d62a9 100644 --- a/src/app/logout/LogoutPage.tsx +++ b/src/app/logout/LogoutPage.tsx @@ -13,7 +13,7 @@ export function LogoutPage() { async function logout() { await post('/auth/logout'); - window.location.href = '/login'; + window.location.href = `${process.env.basePath || ''}/login`; } removeClientAuthToken(); From f3e246c64bf75093664472d0cae32a6067089327 Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Sun, 9 Nov 2025 23:58:20 -0800 Subject: [PATCH 4/5] fix hasdata queries, add hasData to website events, fix sessionactivity truncation, --- .../[websiteId]/events/EventsTable.tsx | 37 ++++++++++++++++++- .../[websiteId]/sessions/SessionActivity.tsx | 9 +++-- src/queries/sql/events/getEventData.ts | 28 +++++++------- src/queries/sql/events/getWebsiteEvents.ts | 6 ++- .../sql/sessions/getSessionActivity.ts | 4 +- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index e9e3e6a0..ea0edde1 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,11 +1,24 @@ -import { DataTable, DataColumn, Row, Text, DataTableProps, IconLabel } from '@umami/react-zen'; +import { + DataTable, + DataColumn, + Row, + Text, + DataTableProps, + IconLabel, + Button, + Dialog, + DialogTrigger, + Icon, + Popover, +} from '@umami/react-zen'; import { useFormat, useMessages, useNavigation } from '@/components/hooks'; import { Avatar } from '@/components/common/Avatar'; import Link from 'next/link'; -import { Eye } from '@/components/icons'; +import { Eye, FileText } from '@/components/icons'; import { Lightning } from '@/components/svg'; import { DateDistance } from '@/components/common/DateDistance'; import { TypeIcon } from '@/components/common/TypeIcon'; +import { EventData } from '@/components/metrics/EventData'; export function EventsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); @@ -32,6 +45,7 @@ export function EventsTable(props: DataTableProps) { > {row.eventName || row.urlPath} + {row.hasData > 0 && } ); }} @@ -72,3 +86,22 @@ export function EventsTable(props: DataTableProps) { ); } + +const PropertiesButton = props => { + return ( + + + + + + + + + ); +}; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx index b9f34e48..7bcf1b76 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionActivity.tsx @@ -14,7 +14,7 @@ import { import { LoadingPanel } from '@/components/common/LoadingPanel'; import { Eye, FileText } from '@/components/icons'; import { Lightning } from '@/components/svg'; -import { useMessages, useSessionActivityQuery, useTimezone } from '@/components/hooks'; +import { useMessages, useMobile, useSessionActivityQuery, useTimezone } from '@/components/hooks'; import { EventData } from '@/components/metrics/EventData'; export function SessionActivity({ @@ -36,6 +36,7 @@ export function SessionActivity({ startDate, endDate, ); + const { isMobile } = useMobile(); let lastDay = null; return ( @@ -50,16 +51,16 @@ export function SessionActivity({ {showHeader && {formatTimezoneDate(createdAt, 'PPPP')}} - {formatTimezoneDate(createdAt, 'pp')} + {formatTimezoneDate(createdAt, 'pp')} {eventName ? : } - + {eventName ? formatMessage(labels.triggeredEvent) : formatMessage(labels.viewedPage)} - + {eventName || urlPath} {hasData > 0 && } diff --git a/src/queries/sql/events/getEventData.ts b/src/queries/sql/events/getEventData.ts index 42dc2040..269258a8 100644 --- a/src/queries/sql/events/getEventData.ts +++ b/src/queries/sql/events/getEventData.ts @@ -19,20 +19,20 @@ async function relationalQuery(websiteId: string, eventId: string) { return rawQuery( ` - select website_id as "websiteId", - session_id as "sessionId", - event_id as "eventId", - url_path as "urlPath", - event_name as "eventName", - data_key as "dataKey", - string_value as "stringValue", - number_value as "numberValue", - date_value as "dateValue", - data_type as "dataType", - created_at as "createdAt" + select event_data.website_id as "websiteId", + event_data.website_event_id as "eventId", + website_event.event_name as "eventName", + event_data.data_key as "dataKey", + event_data.string_value as "stringValue", + event_data.number_value as "numberValue", + event_data.date_value as "dateValue", + event_data.data_type as "dataType", + event_data.created_at as "createdAt" from event_data - website_id = {{websiteId::uuid}} - event_id = {{eventId::uuid}} + join website_event on website_event.event_id = event_data.website_event_id + and website_event.website_id = {{websiteId::uuid}} + where event_data.website_id = {{websiteId::uuid}} + and event_data.website_event_id = {{eventId::uuid}} `, { websiteId, eventId }, FUNCTION_NAME, @@ -45,9 +45,7 @@ async function clickhouseQuery(websiteId: string, eventId: string): Promise Date: Mon, 10 Nov 2025 01:07:11 -0800 Subject: [PATCH 5/5] fix realtime logs for mobile --- .../[websiteId]/realtime/RealtimeLog.tsx | 38 ++++++++++++++----- .../[websiteId]/realtime/RealtimePage.tsx | 5 ++- src/components/metrics/ListTable.tsx | 6 +-- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx index 9ae19bf8..3dec340f 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx @@ -9,6 +9,7 @@ import { useCountryNames, useLocale, useMessages, + useMobile, useNavigation, useTimezone, useWebsite, @@ -40,6 +41,7 @@ export function RealtimeLog({ data }: { data: any }) { const { countryNames } = useCountryNames(locale); const [filter, setFilter] = useState(TYPE_ALL); const { updateParams } = useNavigation(); + const { isPhone } = useMobile(); const buttons = [ { @@ -123,12 +125,18 @@ export function RealtimeLog({ data }: { data: any }) { const row = logs[index]; return ( - - - - {getTime(row)} + + + + + + + {getTime(row)} + - {getDetail(row)} + + {getDetail(row)} + ); @@ -168,10 +176,22 @@ export function RealtimeLog({ data }: { data: any }) { return ( {formatMessage(labels.activity)} - - - - + {isPhone ? ( + <> + + + + + + + + ) : ( + + + + + )} + {logs?.length === 0 && } {logs?.length > 0 && ( diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimePage.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimePage.tsx index 7f9ab608..0f9fa358 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/RealtimePage.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimePage.tsx @@ -6,7 +6,7 @@ import { PageBody } from '@/components/common/PageBody'; import { Panel } from '@/components/common/Panel'; import { RealtimeChart } from '@/components/metrics/RealtimeChart'; import { WorldMap } from '@/components/metrics/WorldMap'; -import { useRealtimeQuery } from '@/components/hooks'; +import { useMobile, useRealtimeQuery } from '@/components/hooks'; import { RealtimeLog } from './RealtimeLog'; import { RealtimeHeader } from './RealtimeHeader'; import { RealtimePaths } from './RealtimePaths'; @@ -16,6 +16,7 @@ import { percentFilter } from '@/lib/filters'; export function RealtimePage({ websiteId }: { websiteId: string }) { const { data, isLoading, error } = useRealtimeQuery(websiteId); + const { isMobile } = useMobile(); if (isLoading || error) { return ; @@ -48,7 +49,7 @@ export function RealtimePage({ websiteId }: { websiteId: string }) { - + diff --git a/src/components/metrics/ListTable.tsx b/src/components/metrics/ListTable.tsx index 303556b0..e76e0174 100644 --- a/src/components/metrics/ListTable.tsx +++ b/src/components/metrics/ListTable.tsx @@ -57,7 +57,7 @@ export function ListTable({ showPercentage={showPercentage} change={renderChange ? renderChange(row, index) : null} currency={currency} - isMobile={isPhone} + isPhone={isPhone} /> ); }; @@ -101,7 +101,7 @@ const AnimatedRow = ({ animate, showPercentage = true, currency, - isMobile, + isPhone, }) => { const props = useSpring({ width: percent, @@ -120,7 +120,7 @@ const AnimatedRow = ({ gap > - + {label}