umami/components/pages/realtime/RealtimeLog.js

158 lines
4.2 KiB
JavaScript
Raw Normal View History

import { useMemo, useState } from 'react';
2023-02-23 05:59:59 +01:00
import { StatusLight, Icon, Text } from 'react-basics';
import { useIntl, FormattedMessage } from 'react-intl';
2020-10-09 13:21:59 +02:00
import { FixedSizeList } from 'react-window';
2020-10-09 11:56:15 +02:00
import firstBy from 'thenby';
2020-10-13 01:31:51 +02:00
import FilterButtons from 'components/common/FilterButtons';
2021-02-27 07:41:05 +01:00
import NoData from 'components/common/NoData';
2023-03-16 23:56:05 +01:00
import { labels, messages } from 'components/messages';
2020-10-09 13:21:59 +02:00
import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames';
import { BROWSERS } from 'lib/constants';
import { stringToColor } from 'lib/format';
2021-02-27 07:41:05 +01:00
import { dateFormat } from 'lib/date';
2022-09-19 16:58:52 +02:00
import { safeDecodeURI } from 'next-basics';
import Icons from 'components/icons';
2020-10-09 11:56:15 +02:00
import styles from './RealtimeLog.module.css';
2023-02-23 05:59:59 +01:00
const TYPE_ALL = 'all';
const TYPE_PAGEVIEW = 'pageview';
const TYPE_SESSION = 'session';
const TYPE_EVENT = 'event';
2020-10-10 02:58:27 +02:00
2023-02-23 05:59:59 +01:00
const icons = {
[TYPE_PAGEVIEW]: <Icons.Eye />,
[TYPE_SESSION]: <Icons.Visitor />,
[TYPE_EVENT]: <Icons.Bolt />,
2020-10-10 02:58:27 +02:00
};
2023-02-15 11:27:18 +01:00
export default function RealtimeLog({ data, websiteDomain }) {
const { formatMessage } = useIntl();
const { locale } = useLocale();
2020-10-09 11:56:15 +02:00
const countryNames = useCountryNames(locale);
2020-10-13 01:31:51 +02:00
const [filter, setFilter] = useState(TYPE_ALL);
2020-10-13 01:31:51 +02:00
const buttons = [
{
label: formatMessage(labels.all),
key: TYPE_ALL,
2020-10-13 01:31:51 +02:00
},
{
label: formatMessage(labels.views),
key: TYPE_PAGEVIEW,
2020-10-13 01:31:51 +02:00
},
{
2023-02-23 05:59:59 +01:00
label: formatMessage(labels.visitors),
key: TYPE_SESSION,
2020-10-13 01:31:51 +02:00
},
{
label: formatMessage(labels.events),
key: TYPE_EVENT,
2020-10-13 01:31:51 +02:00
},
];
2023-02-23 05:59:59 +01:00
const getTime = ({ createdAt }) => dateFormat(new Date(createdAt), 'pp', locale);
const getColor = ({ sessionId }) => stringToColor(sessionId);
const getIcon = ({ __type }) => icons[__type];
const getDetail = log => {
const { __type, eventName, url, browser, os, country, device } = log;
if (__type === TYPE_EVENT) {
return (
<FormattedMessage
{...messages.eventLog}
values={{
event: <b>{eventName || formatMessage(labels.unknown)}</b>,
url: (
<a
href={`//${websiteDomain}${url}`}
className={styles.link}
target="_blank"
rel="noreferrer noopener"
>
{url}
</a>
),
}}
/>
);
2020-10-09 11:56:15 +02:00
}
2023-02-23 05:59:59 +01:00
if (__type === TYPE_PAGEVIEW) {
2020-11-03 04:37:13 +01:00
return (
<a
2023-02-15 11:27:18 +01:00
href={`//${websiteDomain}${url}`}
2023-02-23 05:59:59 +01:00
className={styles.link}
2020-11-03 04:37:13 +01:00
target="_blank"
rel="noreferrer noopener"
>
2022-09-19 16:58:52 +02:00
{safeDecodeURI(url)}
2020-11-03 04:37:13 +01:00
</a>
);
2020-10-09 11:56:15 +02:00
}
2023-02-23 05:59:59 +01:00
if (__type === TYPE_SESSION) {
2020-10-09 11:56:15 +02:00
return (
<FormattedMessage
2023-02-23 05:59:59 +01:00
{...messages.visitorLog}
2020-10-13 01:31:51 +02:00
values={{
country: <b>{countryNames[country] || formatMessage(labels.unknown)}</b>,
2020-10-14 23:16:00 +02:00
browser: <b>{BROWSERS[browser]}</b>,
os: <b>{os}</b>,
2023-03-16 23:56:05 +01:00
device: <b>{formatMessage(labels[device] || labels.unknown)}</b>,
2020-10-13 01:31:51 +02:00
}}
2020-10-09 11:56:15 +02:00
/>
);
}
2023-02-23 05:59:59 +01:00
};
2020-10-11 07:36:55 +02:00
2020-10-09 13:21:59 +02:00
const Row = ({ index, style }) => {
2020-10-10 10:16:28 +02:00
const row = logs[index];
2020-10-09 13:21:59 +02:00
return (
2020-10-10 10:16:28 +02:00
<div className={styles.row} style={style}>
<div>
<StatusLight color={getColor(row)} />
</div>
2020-10-11 07:36:55 +02:00
<div className={styles.time}>{getTime(row)}</div>
2020-10-10 10:16:28 +02:00
<div className={styles.detail}>
2023-02-23 05:59:59 +01:00
<Icon className={styles.icon}>{getIcon(row)}</Icon>
<Text>{getDetail(row)}</Text>
2020-10-10 10:16:28 +02:00
</div>
2020-10-09 13:21:59 +02:00
</div>
);
};
2023-02-23 05:59:59 +01:00
const logs = useMemo(() => {
if (!data) {
return [];
}
const { pageviews, visitors, events } = data;
const logs = [...pageviews, ...visitors, ...events].sort(firstBy('createdAt', -1));
if (filter !== TYPE_ALL) {
return logs.filter(({ __type }) => __type === filter);
}
return logs;
}, [data, filter]);
2020-10-09 11:56:15 +02:00
return (
2020-10-10 10:16:28 +02:00
<div className={styles.table}>
<FilterButtons items={buttons} selectedKey={filter} onSelect={setFilter} />
2023-02-23 05:59:59 +01:00
<div className={styles.header}>{formatMessage(labels.logs)}</div>
2020-10-10 10:16:28 +02:00
<div className={styles.body}>
2020-10-14 23:16:00 +02:00
{logs?.length === 0 && <NoData />}
{logs?.length > 0 && (
<FixedSizeList height={400} itemCount={logs.length} itemSize={40}>
{Row}
</FixedSizeList>
)}
2020-10-10 10:16:28 +02:00
</div>
2020-10-09 11:56:15 +02:00
</div>
);
}