Event data UI/UX polish

This commit is contained in:
Francis Cao
2026-04-27 12:09:13 -07:00
parent afa878cd3a
commit 877945e721
4 changed files with 124 additions and 47 deletions
@@ -169,7 +169,20 @@ export function EventDataDateChart({
}, [exactDateRows, chartUnit, dateLocale]);
const chartData: any = useMemo(() => {
if (!rows.length || !minPropertyDate || !maxPropertyDate) return null;
if (!minPropertyDate || !maxPropertyDate) {
return {
datasets: [
{
label: t(labels.count),
data: [],
barPercentage: 0.9,
categoryPercentage: 0.9,
...colors.chart.visitors,
borderWidth: 1,
},
],
};
}
return {
datasets: [
@@ -231,6 +244,28 @@ export function EventDataDateChart({
};
});
}, [exactDateRows, total, dateLocale]);
const topDateByDay = useMemo(() => {
if (!exactDateRows.length || total === 0) return null;
const dayTotals = exactDateRows.reduce(
(obj, { date, y }) => {
const dayDate = startOfDay(date);
const key = format(dayDate, 'yyyy-MM-dd');
const existing = obj[key];
obj[key] = {
date: dayDate,
count: (existing?.count ?? 0) + y,
};
return obj;
},
{} as Record<string, { date: Date; count: number }>,
);
return Object.values(dayTotals)
.sort((a, b) => b.count - a.count || b.date.getTime() - a.date.getTime())[0];
}, [exactDateRows, total]);
const topWeekday = useMemo(
() => [...weekdayTableData].sort((a, b) => b.count - a.count)[0],
@@ -264,18 +299,9 @@ export function EventDataDateChart({
],
};
}, [weekdayTableData, weekdayLabels, weekdayColorMap]);
const spanDays = useMemo(
() =>
minPropertyDate && maxPropertyDate
? differenceInCalendarDays(maxPropertyDate, minPropertyDate) + 1
: 0,
[minPropertyDate, maxPropertyDate],
);
return (
<Column gap="6">
<LoadingPanel
data={rows}
isLoading={query.isLoading}
isFetching={query.isFetching}
error={query.error}
@@ -295,16 +321,18 @@ export function EventDataDateChart({
}
/>
<InsightCard
label="Date span"
value={spanDays ? `${formatLongNumber(spanDays)} days` : t(labels.none)}
label="Top date"
value={topDateByDay ? formatDate(topDateByDay.date, 'PP', locale) : t(labels.none)}
hint={
minPropertyDate && maxPropertyDate
? `${formatDate(minPropertyDate, 'PP', locale)} - ${formatDate(maxPropertyDate, 'PP', locale)}`
topDateByDay
? `${formatLongNumber(topDateByDay.count)} ${t(labels.count).toLowerCase()} - ${Math.round(
100 * (topDateByDay.count / total),
)}%`
: undefined
}
/>
<InsightCard
label="Earliest value"
label="Earliest date"
value={minPropertyDate ? formatDate(minPropertyDate, 'PPpp', locale) : t(labels.none)}
hint={
minPropertyDate
@@ -316,7 +344,7 @@ export function EventDataDateChart({
}
/>
<InsightCard
label="Latest value"
label="Latest date"
value={maxPropertyDate ? formatDate(maxPropertyDate, 'PPpp', locale) : t(labels.none)}
hint={
maxPropertyDate
@@ -331,27 +359,28 @@ export function EventDataDateChart({
</Column>
</LoadingPanel>
<LoadingPanel
data={rows}
isLoading={query.isLoading}
isFetching={query.isFetching}
error={query.error}
minHeight="400px"
>
{chartData && minPropertyDate && maxPropertyDate && (
<BarChart
chartData={chartData}
minDate={minPropertyDate}
maxDate={maxPropertyDate}
unit={chartUnit}
stacked={false}
renderXLabel={renderXLabel}
renderYLabel={(value: string | number) => formatLongNumber(Number(value))}
height="400px"
/>
)}
<Column minHeight="400px">
{chartData && (
<BarChart
chartData={chartData}
minDate={minPropertyDate ?? zonedNow}
maxDate={maxPropertyDate ?? zonedNow}
unit={chartUnit}
stacked={false}
renderXLabel={renderXLabel}
renderYLabel={(value: string | number) => formatLongNumber(Number(value))}
height="400px"
/>
)}
</Column>
</LoadingPanel>
<LoadingPanel
data={rows}
data={weekdayTableData}
isLoading={query.isLoading}
isFetching={query.isFetching}
error={query.error}
@@ -34,6 +34,10 @@ export function EventDataPivotTable({
return [...new Set<string>(keys)];
}, [propertiesQuery.data, eventName]);
const tableMinWidth = useMemo(() => {
return `${72 + 220 + 180 + propertyKeys.length * 160}px`;
}, [propertyKeys.length]);
const tableData = useMemo(() => {
if (!pivotQuery.data?.data) return [];
return pivotQuery.data.data.map(
@@ -76,16 +80,16 @@ export function EventDataPivotTable({
renderEmpty={() => <Empty />}
>
<Column gap="4" minWidth="0" width="100%" maxWidth="100%">
<Column minWidth="0" width="100%" maxWidth="100%" overflow={isMobile ? undefined : 'hidden'}>
<DataTable data={tableQuery?.data} style={{ width: '100%' }} displayMode={isMobile ? 'cards' : 'table'}>
<DataColumn id="session" label={t(labels.session)} width="0.75fr">
{isMobile ? (
<DataTable data={tableQuery?.data} style={{ width: '100%' }} displayMode="cards">
<DataColumn id="session" label={t(labels.session)} width="72px">
{(row: any) => (
<Link href={updateParams({ session: row.sessionId })}>
<Avatar seed={row.sessionId} size={32} />
</Link>
)}
</DataColumn>
<DataColumn id="urlPath" label={t(labels.path)} width="1.4fr">
<DataColumn id="urlPath" label={t(labels.path)} width="220px">
{(row: any) => (
<Text truncate title={row.urlPath}>
{row.urlPath}
@@ -93,7 +97,7 @@ export function EventDataPivotTable({
)}
</DataColumn>
{propertyKeys.map(key => (
<DataColumn key={key} id={key} label={key} width="1fr">
<DataColumn key={key} id={key} label={key} width="160px">
{(row: any) => (
<Text truncate title={row[key]}>
{row[key] ?? ''}
@@ -101,11 +105,48 @@ export function EventDataPivotTable({
)}
</DataColumn>
))}
<DataColumn id="createdAt" label={t(labels.created)} width="1.1fr">
<DataColumn id="createdAt" label={t(labels.created)} width="180px">
{(row: any) => <DateDistance date={new Date(row.createdAt)} />}
</DataColumn>
</DataTable>
</Column>
) : (
<div style={{ width: '100%', maxWidth: '100%', overflowX: 'auto', overflowY: 'hidden' }}>
<div style={{ width: tableMinWidth, minWidth: tableMinWidth }}>
<DataTable
data={tableQuery?.data}
style={{ width: '100%' }}
displayMode="table"
>
<DataColumn id="session" label={t(labels.session)} width="72px">
{(row: any) => (
<Link href={updateParams({ session: row.sessionId })}>
<Avatar seed={row.sessionId} size={32} />
</Link>
)}
</DataColumn>
<DataColumn id="urlPath" label={t(labels.path)} width="220px">
{(row: any) => (
<Text truncate title={row.urlPath}>
{row.urlPath}
</Text>
)}
</DataColumn>
{propertyKeys.map(key => (
<DataColumn key={key} id={key} label={key} width="160px">
{(row: any) => (
<Text truncate title={row[key]}>
{row[key] ?? ''}
</Text>
)}
</DataColumn>
))}
<DataColumn id="createdAt" label={t(labels.created)} width="180px">
{(row: any) => <DateDistance date={new Date(row.createdAt)} />}
</DataColumn>
</DataTable>
</div>
</div>
)}
{showPager && tableQuery && (
<Row marginTop="6">
<Pager
@@ -2,6 +2,7 @@
import { Column, ComboBox, Grid, Label, ListItem, Row, Select } from '@umami/react-zen';
import { useMemo, useState } from 'react';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { Panel } from '@/components/common/Panel';
import { useEventDataPropertiesQuery, useMessages } from '@/components/hooks';
import { DATA_TYPE } from '@/lib/constants';
import type { EventPropertyFilter } from '@/lib/types';
@@ -168,11 +169,13 @@ export function EventProperties({ websiteId }: { websiteId: string }) {
</Column>
)}
{eventName && (
<EventDataPivotTable
websiteId={websiteId}
eventName={eventName}
eventFilters={eventFilters}
/>
<Panel minWidth="0" width="100%">
<EventDataPivotTable
websiteId={websiteId}
eventName={eventName}
eventFilters={eventFilters}
/>
</Panel>
)}
</Column>
</LoadingPanel>
@@ -87,17 +87,21 @@ export function EventsPage({ websiteId }) {
})}
</MetricsBar>
</LoadingPanel>
<Panel>
<Tabs selectedKey={tab} onSelectionChange={key => handleSelect(key)}>
<Panel minWidth="0" width="100%" style={{ overflow: 'hidden' }}>
<Tabs
selectedKey={tab}
onSelectionChange={key => handleSelect(key)}
style={{ minWidth: 0, width: '100%' }}
>
<TabList>
<Tab id="chart">{t(labels.chart)}</Tab>
<Tab id="activity">{t(labels.activity)}</Tab>
<Tab id="properties">{t(labels.properties)}</Tab>
</TabList>
<TabPanel id="activity">
<TabPanel id="activity" style={{ minWidth: 0, width: '100%' }}>
<EventsDataTable websiteId={websiteId} />
</TabPanel>
<TabPanel id="chart">
<TabPanel id="chart" style={{ minWidth: 0, width: '100%' }}>
<Column gap="6">
<Column border="bottom" paddingBottom="6">
<EventsChart websiteId={websiteId} limit={50} />
@@ -111,7 +115,7 @@ export function EventsPage({ websiteId }) {
/>
</Column>
</TabPanel>
<TabPanel id="properties">
<TabPanel id="properties" style={{ minWidth: 0, width: '100%', overflow: 'hidden' }}>
<EventProperties websiteId={websiteId} />
</TabPanel>
</Tabs>