fix visitDuration calculation and conditionally show bounce/visitDuration on session columns
Some checks failed
Node.js CI / build (postgresql, 18.18) (push) Has been cancelled

This commit is contained in:
Francis Cao 2025-08-25 09:26:49 -07:00
parent 6c832bd0db
commit f06ef6fbc9
4 changed files with 28 additions and 106 deletions

View file

@ -59,7 +59,7 @@ export function Breakdown({ websiteId, selectedFields = [], startDate, endDate }
</DataColumn>
<DataColumn id="visitDuration" label={formatMessage(labels.visitDuration)} align="end">
{row => {
const n = (row?.['totaltime'] / row?.['visits']) * 100;
const n = row?.['totaltime'] / row?.['visits'];
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>

View file

@ -1,42 +0,0 @@
import { FilterLink } from '@/components/common/FilterLink';
import { useCountryNames } from '@/components/hooks';
import { useLocale, useMessages, useFormat } from '@/components/hooks';
import { MetricsTable, MetricsTableProps } from './MetricsTable';
import { TypeIcon } from '@/components/common/TypeIcon';
import { MetricsExpandedTable } from '@/components/metrics/MetricsExpandedTable';
export interface CountriesTableProps extends MetricsTableProps {
isExpanded?: boolean;
}
export function CountriesTable({ isExpanded, ...props }: CountriesTableProps) {
const { locale } = useLocale();
const { countryNames } = useCountryNames(locale);
const { formatMessage, labels } = useMessages();
const { formatCountry } = useFormat();
const renderLabel = ({ label: code }) => {
return (
<FilterLink
type="country"
value={(countryNames[code] && code) || code}
label={formatCountry(code)}
>
<TypeIcon type="country" value={code} />
</FilterLink>
);
};
const Component = isExpanded ? MetricsExpandedTable : MetricsTable;
return (
<Component
{...props}
title={formatMessage(labels.countries)}
type="country"
metric={formatMessage(labels.visitors)}
renderLabel={renderLabel}
searchFormattedValues={true}
/>
);
}

View file

@ -1,47 +0,0 @@
import { useMessages } from '@/components/hooks';
import { formatShortTime } from '@/lib/format';
import { DataColumn, DataTable } from '@umami/react-zen';
import { ReactNode } from 'react';
export interface ListExpandedTableProps {
data?: any[];
title?: string;
renderLabel?: (row: any, index: number) => ReactNode;
}
export function ListExpandedTable({ data = [], title, renderLabel }: ListExpandedTableProps) {
const { formatMessage, labels } = useMessages();
return (
<DataTable data={data}>
<DataColumn id="label" label={title} align="start">
{row =>
renderLabel
? renderLabel({ x: row?.['name'], country: row?.['country'] }, Number(row.id))
: (row?.['name'] ?? formatMessage(labels.unknown))
}
</DataColumn>
<DataColumn id="visitors" label={formatMessage(labels.visitors)} align="end">
{row => row?.['visitors']?.toLocaleString()}
</DataColumn>
<DataColumn id="visits" label={formatMessage(labels.visits)} align="end">
{row => row?.['visits']?.toLocaleString()}
</DataColumn>
<DataColumn id="pageviews" label={formatMessage(labels.views)} align="end">
{row => row?.['pageviews']?.toLocaleString()}
</DataColumn>
<DataColumn id="bounceRate" label={formatMessage(labels.bounceRate)} align="end">
{row => {
const n = (Math.min(row?.['visits'], row?.['bounces']) / row?.['visits']) * 100;
return Math.round(+n) + '%';
}}
</DataColumn>
<DataColumn id="visitDuration" label={formatMessage(labels.visitDuration)} align="end">
{row => {
const n = (row?.['totaltime'] / row?.['visits']) * 100;
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>
</DataTable>
);
}

View file

@ -6,6 +6,7 @@ import { Close } from '@/components/icons';
import { DownloadButton } from '@/components/input/DownloadButton';
import { formatShortTime } from '@/lib/format';
import { MetricLabel } from '@/components/metrics/MetricLabel';
import { SESSION_COLUMNS } from '@/lib/constants';
export interface MetricsExpandedTableProps {
websiteId: string;
@ -34,6 +35,7 @@ export function MetricsExpandedTable({
const [search, setSearch] = useState('');
const { formatMessage, labels } = useMessages();
const isType = ['browser', 'country', 'device', 'os'].includes(type);
const showBounceDuration = SESSION_COLUMNS.includes(type);
const { data, isLoading, isFetching, error } = useWebsiteExpandedMetricsQuery(websiteId, {
type,
@ -85,22 +87,31 @@ export function MetricsExpandedTable({
<DataColumn id="pageviews" label={formatMessage(labels.views)} align="end">
{row => row?.['pageviews']?.toLocaleString()}
</DataColumn>
<DataColumn id="bounceRate" label={formatMessage(labels.bounceRate)} align="end">
{row => {
const n = (Math.min(row?.['visits'], row?.['bounces']) / row?.['visits']) * 100;
return Math.round(+n) + '%';
}}
</DataColumn>
<DataColumn
id="visitDuration"
label={formatMessage(labels.visitDuration)}
align="end"
>
{row => {
const n = (row?.['totaltime'] / row?.['visits']) * 100;
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>
{showBounceDuration && [
<DataColumn
key="bounceRate"
id="bounceRate"
label={formatMessage(labels.bounceRate)}
align="end"
>
{row => {
const n = (Math.min(row?.['visits'], row?.['bounces']) / row?.['visits']) * 100;
return Math.round(+n) + '%';
}}
</DataColumn>,
<DataColumn
key="visitDuration"
id="visitDuration"
label={formatMessage(labels.visitDuration)}
align="end"
>
{row => {
const n = row?.['totaltime'] / row?.['visits'];
return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
}}
</DataColumn>,
]}
</DataTable>
)}
</Column>