add better metrics to revenue page AOV/ARPU

This commit is contained in:
Francis Cao
2026-03-03 16:00:39 -08:00
parent 5d87a9e030
commit 4ecef2e290
5 changed files with 31 additions and 26 deletions
+2
View File
@@ -332,6 +332,8 @@
"unique": "Unique", "unique": "Unique",
"unique-events": "Unique events", "unique-events": "Unique events",
"unique-visitors": "Unique visitors", "unique-visitors": "Unique visitors",
"aov": "AOV",
"arpu": "ARPU",
"uniqueCustomers": "Unique Customers", "uniqueCustomers": "Unique Customers",
"unknown": "Unknown", "unknown": "Unknown",
"untitled": "Untitled", "untitled": "Untitled",
@@ -44,7 +44,7 @@ export function Revenue({ websiteId, startDate, endDate, unit }: RevenueProps) {
const metrics = useMemo(() => { const metrics = useMemo(() => {
if (!data) return []; if (!data) return [];
const { sum, count, average, unique_count, comparison } = data.total; const { sum, count, average, unique_count, arpu, comparison } = data.total;
return [ return [
{ {
@@ -55,10 +55,16 @@ export function Revenue({ websiteId, startDate, endDate, unit }: RevenueProps) {
}, },
{ {
value: average, value: average,
label: t(labels.average), label: t(labels.aov),
change: comparison ? average - comparison.average : 0, change: comparison ? average - comparison.average : 0,
formatValue: (n: number) => formatLongCurrency(n, currency), formatValue: (n: number) => formatLongCurrency(n, currency),
}, },
{
value: arpu,
label: t(labels.arpu),
change: comparison ? arpu - (comparison.arpu ?? 0) : 0,
formatValue: (n: number) => formatLongCurrency(n, currency),
},
{ {
value: count, value: count,
label: t(labels.transactions), label: t(labels.transactions),
@@ -1,21 +0,0 @@
import { DataColumn, DataTable } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
import { formatLongCurrency } from '@/lib/format';
export function RevenueTable({ data = [] }) {
const { t, labels } = useMessages();
return (
<DataTable data={data}>
<DataColumn id="currency" label={t(labels.currency)} align="end" />
<DataColumn id="total" label={t(labels.total)} align="end">
{(row: any) => formatLongCurrency(row.sum, row.currency)}
</DataColumn>
<DataColumn id="average" label={t(labels.average)} align="end">
{(row: any) => formatLongCurrency(row.count ? row.sum / row.count : 0, row.currency)}
</DataColumn>
<DataColumn id="count" label={t(labels.transactions)} align="end" />
<DataColumn id="unique_count" label={t(labels.uniqueCustomers)} align="end" />
</DataTable>
);
}
+2
View File
@@ -255,6 +255,8 @@ export const labels: Record<string, string> = {
myAccount: 'label.my-account', myAccount: 'label.my-account',
transfer: 'label.transfer', transfer: 'label.transfer',
transactions: 'label.transactions', transactions: 'label.transactions',
aov: 'label.aov',
arpu: 'label.arpu',
uniqueCustomers: 'label.uniqueCustomers', uniqueCustomers: 'label.uniqueCustomers',
viewedPage: 'message.viewed-page', viewedPage: 'message.viewed-page',
collectedData: 'message.collected-data', collectedData: 'message.collected-data',
+19 -3
View File
@@ -9,6 +9,7 @@ export interface RevenueStatsResult {
count: number; count: number;
average: number; average: number;
unique_count: number; unique_count: number;
arpu: number;
} }
export async function getRevenueStats( export async function getRevenueStats(
@@ -52,7 +53,11 @@ async function relationalQuery(
select select
sum(revenue.revenue) as sum, sum(revenue.revenue) as sum,
count(distinct revenue.event_id) as count, count(distinct revenue.event_id) as count,
count(distinct revenue.session_id) as unique_count count(distinct revenue.session_id) as unique_count,
(select count(distinct session_id)
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}) as total_sessions
from revenue from revenue
${joinQuery} ${joinQuery}
${cohortQuery} ${cohortQuery}
@@ -66,6 +71,7 @@ async function relationalQuery(
).then(result => result?.[0]); ).then(result => result?.[0]);
total.average = total.count > 0 ? Number(total.sum) / Number(total.count) : 0; total.average = total.count > 0 ? Number(total.sum) / Number(total.count) : 0;
total.arpu = total.total_sessions > 0 ? Number(total.sum) / Number(total.total_sessions) : 0;
return total; return total;
} }
@@ -97,12 +103,21 @@ async function clickhouseQuery(
and website_event.event_id = website_revenue.event_id` and website_event.event_id = website_revenue.event_id`
: ''; : '';
const total = await rawQuery<{ sum: number; count: number; unique_count: number }>( const total = await rawQuery<{
sum: number;
count: number;
unique_count: number;
total_sessions: number;
}>(
` `
select select
sum(website_revenue.revenue) as sum, sum(website_revenue.revenue) as sum,
uniqExact(website_revenue.event_id) as count, uniqExact(website_revenue.event_id) as count,
uniqExact(website_revenue.session_id) as unique_count uniqExact(website_revenue.session_id) as unique_count,
(select uniqExact(session_id)
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}) as total_sessions
from website_revenue from website_revenue
${joinQuery} ${joinQuery}
${cohortQuery} ${cohortQuery}
@@ -115,6 +130,7 @@ async function clickhouseQuery(
).then(result => result?.[0]); ).then(result => result?.[0]);
total.average = total.count > 0 ? total.sum / total.count : 0; total.average = total.count > 0 ? total.sum / total.count : 0;
total.arpu = total.total_sessions > 0 ? total.sum / total.total_sessions : 0;
return total; return total;
} }