From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001
From: Fuwn <50817549+Fuwn@users.noreply.github.com>
Date: Sat, 24 Jan 2026 13:09:50 +0000
Subject: Initial commit
Created from https://vercel.com/new
---
src/components/metrics/MetricsExpandedTable.tsx | 139 ++++++++++++++++++++++++
1 file changed, 139 insertions(+)
create mode 100644 src/components/metrics/MetricsExpandedTable.tsx
(limited to 'src/components/metrics/MetricsExpandedTable.tsx')
diff --git a/src/components/metrics/MetricsExpandedTable.tsx b/src/components/metrics/MetricsExpandedTable.tsx
new file mode 100644
index 0000000..f24c952
--- /dev/null
+++ b/src/components/metrics/MetricsExpandedTable.tsx
@@ -0,0 +1,139 @@
+import { Button, Column, DataColumn, DataTable, Icon, Row, SearchField } from '@umami/react-zen';
+import { type ReactNode, useState } from 'react';
+import { LoadingPanel } from '@/components/common/LoadingPanel';
+import { useMessages, useWebsiteExpandedMetricsQuery } from '@/components/hooks';
+import { X } from '@/components/icons';
+import { DownloadButton } from '@/components/input/DownloadButton';
+import { MetricLabel } from '@/components/metrics/MetricLabel';
+import { SESSION_COLUMNS } from '@/lib/constants';
+import { formatShortTime } from '@/lib/format';
+
+export interface MetricsExpandedTableProps {
+ websiteId: string;
+ type?: string;
+ title?: string;
+ dataFilter?: (data: any) => any;
+ onSearch?: (search: string) => void;
+ params?: { [key: string]: any };
+ allowSearch?: boolean;
+ allowDownload?: boolean;
+ renderLabel?: (row: any, index: number) => ReactNode;
+ onClose?: () => void;
+ children?: ReactNode;
+}
+
+export function MetricsExpandedTable({
+ websiteId,
+ type,
+ title,
+ params,
+ allowSearch = true,
+ allowDownload = true,
+ onClose,
+ children,
+}: MetricsExpandedTableProps) {
+ 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,
+ search: isType ? undefined : search,
+ ...params,
+ });
+
+ const items = data?.map(({ name, ...props }) => ({ label: name, ...props }));
+
+ return (
+ <>
+
+ {allowSearch && }
+
+ {children}
+ {allowDownload && }
+ {onClose && (
+
+ )}
+
+
+
+
+ {items && (
+
+
+ {row => (
+
+
+
+ )}
+
+
+ {row => row?.visitors?.toLocaleString()}
+
+
+ {row => row?.visits?.toLocaleString()}
+
+
+ {row => row?.pageviews?.toLocaleString()}
+
+ {showBounceDuration && [
+
+ {row => {
+ const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
+ return `${Math.round(+n)}%`;
+ }}
+ ,
+
+
+ {row => {
+ const n = row?.totaltime / row?.visits;
+ return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
+ }}
+ ,
+ ]}
+
+ )}
+
+
+ >
+ );
+}
--
cgit v1.2.3