Event data UI/UX polish
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user