Added filter buttons for realtime.
parent
5a73c224b7
commit
f1624780ee
|
@ -0,0 +1,11 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ButtonLayout from 'components/layout/ButtonLayout';
|
||||||
|
import ButtonGroup from './ButtonGroup';
|
||||||
|
|
||||||
|
export default function FilterButtons({ buttons, selected, onClick }) {
|
||||||
|
return (
|
||||||
|
<ButtonLayout>
|
||||||
|
<ButtonGroup size="xsmall" items={buttons} selectedItem={selected} onClick={onClick} />
|
||||||
|
</ButtonLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ export default function DataTable({
|
||||||
metric,
|
metric,
|
||||||
className,
|
className,
|
||||||
renderLabel,
|
renderLabel,
|
||||||
height = 400,
|
height,
|
||||||
animate = true,
|
animate = true,
|
||||||
virtualize = false,
|
virtualize = false,
|
||||||
}) {
|
}) {
|
||||||
|
@ -49,7 +49,7 @@ export default function DataTable({
|
||||||
{metric}
|
{metric}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.body}>
|
<div className={styles.body} style={{ height }}>
|
||||||
{data?.length === 0 && <NoData />}
|
{data?.length === 0 && <NoData />}
|
||||||
{virtualize && data.length > 0 ? (
|
{virtualize && data.length > 0 ? (
|
||||||
<FixedSizeList height={height} itemCount={data.length} itemSize={30}>
|
<FixedSizeList height={height} itemCount={data.length} itemSize={30}>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
.body {
|
.body {
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|
|
@ -17,16 +17,11 @@ import styles from './MetricsTable.module.css';
|
||||||
export default function MetricsTable({
|
export default function MetricsTable({
|
||||||
websiteId,
|
websiteId,
|
||||||
websiteDomain,
|
websiteDomain,
|
||||||
title,
|
|
||||||
metric,
|
|
||||||
type,
|
type,
|
||||||
className,
|
className,
|
||||||
dataFilter,
|
dataFilter,
|
||||||
filterOptions,
|
filterOptions,
|
||||||
limit,
|
limit,
|
||||||
virtualize,
|
|
||||||
renderLabel,
|
|
||||||
height,
|
|
||||||
onDataLoad,
|
onDataLoad,
|
||||||
...props
|
...props
|
||||||
}) {
|
}) {
|
||||||
|
@ -71,20 +66,9 @@ export default function MetricsTable({
|
||||||
<div className={classNames(styles.container, className)}>
|
<div className={classNames(styles.container, className)}>
|
||||||
{!data && loading && <Loading />}
|
{!data && loading && <Loading />}
|
||||||
{error && <ErrorMessage />}
|
{error && <ErrorMessage />}
|
||||||
{data && !error && (
|
{data && !error && <DataTable {...props} data={filteredData} className={className} />}
|
||||||
<DataTable
|
|
||||||
{...props}
|
|
||||||
title={title}
|
|
||||||
data={filteredData}
|
|
||||||
metric={metric}
|
|
||||||
className={className}
|
|
||||||
renderLabel={renderLabel}
|
|
||||||
height={height}
|
|
||||||
virtualize={virtualize}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
{limit && (
|
{data && !error && limit && (
|
||||||
<Link
|
<Link
|
||||||
icon={<Arrow />}
|
icon={<Arrow />}
|
||||||
href={router.pathname}
|
href={router.pathname}
|
||||||
|
|
|
@ -2,14 +2,15 @@ import React, { useState } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import ButtonGroup from 'components/common/ButtonGroup';
|
import FilterButtons from 'components/common/FilterButtons';
|
||||||
import ButtonLayout from 'components/layout/ButtonLayout';
|
|
||||||
import { urlFilter } from 'lib/filters';
|
import { urlFilter } from 'lib/filters';
|
||||||
import { FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
|
|
||||||
import usePageQuery from 'hooks/usePageQuery';
|
import usePageQuery from 'hooks/usePageQuery';
|
||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import styles from './PagesTable.module.css';
|
import styles from './PagesTable.module.css';
|
||||||
|
|
||||||
|
export const FILTER_COMBINED = 0;
|
||||||
|
export const FILTER_RAW = 1;
|
||||||
|
|
||||||
export default function PagesTable({ websiteId, websiteDomain, showFilters, ...props }) {
|
export default function PagesTable({ websiteId, websiteDomain, showFilters, ...props }) {
|
||||||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||||
const {
|
const {
|
||||||
|
@ -56,11 +57,3 @@ export default function PagesTable({ websiteId, websiteDomain, showFilters, ...p
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilterButtons = ({ buttons, selected, onClick }) => {
|
|
||||||
return (
|
|
||||||
<ButtonLayout>
|
|
||||||
<ButtonGroup size="xsmall" items={buttons} selectedItem={selected} onClick={onClick} />
|
|
||||||
</ButtonLayout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useMemo, useRef } from 'react';
|
||||||
import { format, parseISO, startOfMinute, subMinutes, isBefore } from 'date-fns';
|
import { format, parseISO, startOfMinute, subMinutes, isBefore } from 'date-fns';
|
||||||
import PageviewsChart from './PageviewsChart';
|
import PageviewsChart from './PageviewsChart';
|
||||||
import { getDateArray } from 'lib/date';
|
import { getDateArray } from 'lib/date';
|
||||||
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants';
|
||||||
|
|
||||||
function mapData(data) {
|
function mapData(data) {
|
||||||
let last = 0;
|
let last = 0;
|
||||||
|
@ -26,7 +26,7 @@ function mapData(data) {
|
||||||
|
|
||||||
export default function RealtimeChart({ data, unit, ...props }) {
|
export default function RealtimeChart({ data, unit, ...props }) {
|
||||||
const endDate = startOfMinute(new Date());
|
const endDate = startOfMinute(new Date());
|
||||||
const startDate = subMinutes(endDate, 30);
|
const startDate = subMinutes(endDate, REALTIME_RANGE);
|
||||||
const prevEndDate = useRef(endDate);
|
const prevEndDate = useRef(endDate);
|
||||||
|
|
||||||
const chartData = useMemo(() => {
|
const chartData = useMemo(() => {
|
||||||
|
@ -51,7 +51,7 @@ export default function RealtimeChart({ data, unit, ...props }) {
|
||||||
return (
|
return (
|
||||||
<PageviewsChart
|
<PageviewsChart
|
||||||
{...props}
|
{...props}
|
||||||
height={300}
|
height={200}
|
||||||
unit={unit}
|
unit={unit}
|
||||||
data={chartData}
|
data={chartData}
|
||||||
animationDuration={animationDuration}
|
animationDuration={animationDuration}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { FixedSizeList } from 'react-window';
|
import { FixedSizeList } from 'react-window';
|
||||||
import firstBy from 'thenby';
|
import firstBy from 'thenby';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import Icon from 'components/common/Icon';
|
import Icon from 'components/common/Icon';
|
||||||
import Tag from 'components/common/Tag';
|
import Tag from 'components/common/Tag';
|
||||||
|
import Dot from 'components/common/Dot';
|
||||||
|
import FilterButtons from 'components/common/FilterButtons';
|
||||||
import useLocale from 'hooks/useLocale';
|
import useLocale from 'hooks/useLocale';
|
||||||
import useCountryNames from 'hooks/useCountryNames';
|
import useCountryNames from 'hooks/useCountryNames';
|
||||||
import { BROWSERS } from 'lib/constants';
|
import { BROWSERS } from 'lib/constants';
|
||||||
|
@ -13,11 +15,11 @@ import Visitor from 'assets/visitor.svg';
|
||||||
import Eye from 'assets/eye.svg';
|
import Eye from 'assets/eye.svg';
|
||||||
import { stringToColor } from 'lib/format';
|
import { stringToColor } from 'lib/format';
|
||||||
import styles from './RealtimeLog.module.css';
|
import styles from './RealtimeLog.module.css';
|
||||||
import Dot from '../common/Dot';
|
|
||||||
|
|
||||||
const TYPE_PAGEVIEW = 0;
|
const TYPE_ALL = 0;
|
||||||
const TYPE_SESSION = 1;
|
const TYPE_PAGEVIEW = 1;
|
||||||
const TYPE_EVENT = 2;
|
const TYPE_SESSION = 2;
|
||||||
|
const TYPE_EVENT = 3;
|
||||||
|
|
||||||
const TYPE_ICONS = {
|
const TYPE_ICONS = {
|
||||||
[TYPE_PAGEVIEW]: <Eye />,
|
[TYPE_PAGEVIEW]: <Eye />,
|
||||||
|
@ -28,11 +30,16 @@ const TYPE_ICONS = {
|
||||||
export default function RealtimeLog({ data, websites }) {
|
export default function RealtimeLog({ data, websites }) {
|
||||||
const [locale] = useLocale();
|
const [locale] = useLocale();
|
||||||
const countryNames = useCountryNames(locale);
|
const countryNames = useCountryNames(locale);
|
||||||
|
const [filter, setFilter] = useState(TYPE_ALL);
|
||||||
|
|
||||||
const logs = useMemo(() => {
|
const logs = useMemo(() => {
|
||||||
const { pageviews, sessions, events } = data;
|
const { pageviews, sessions, events } = data;
|
||||||
return [...pageviews, ...sessions, ...events].sort(firstBy('created_at', -1));
|
const logs = [...pageviews, ...sessions, ...events].sort(firstBy('created_at', -1));
|
||||||
}, [data]);
|
if (filter) {
|
||||||
|
return logs.filter(row => getType(row) === filter);
|
||||||
|
}
|
||||||
|
return logs;
|
||||||
|
}, [data, filter]);
|
||||||
|
|
||||||
const uuids = useMemo(() => {
|
const uuids = useMemo(() => {
|
||||||
return data.sessions.reduce((obj, { session_id, session_uuid }) => {
|
return data.sessions.reduce((obj, { session_id, session_uuid }) => {
|
||||||
|
@ -41,6 +48,25 @@ export default function RealtimeLog({ data, websites }) {
|
||||||
}, {});
|
}, {});
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="label.all" defaultMessage="All" />,
|
||||||
|
value: TYPE_ALL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="metrics.pages" defaultMessage="Pages" />,
|
||||||
|
value: TYPE_PAGEVIEW,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="metrics.visitors" defaultMessage="Visitors" />,
|
||||||
|
value: TYPE_SESSION,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="metrics.events" defaultMessage="Events" />,
|
||||||
|
value: TYPE_EVENT,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
function getType({ view_id, session_id, event_id }) {
|
function getType({ view_id, session_id, event_id }) {
|
||||||
if (event_id) {
|
if (event_id) {
|
||||||
return TYPE_EVENT;
|
return TYPE_EVENT;
|
||||||
|
@ -88,7 +114,12 @@ export default function RealtimeLog({ data, websites }) {
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id="message.log.visitor"
|
id="message.log.visitor"
|
||||||
defaultMessage="Visitor from {country} using {browser} on {os} {device}"
|
defaultMessage="Visitor from {country} using {browser} on {os} {device}"
|
||||||
values={{ country: countryNames[country], browser: BROWSERS[browser], os, device }}
|
values={{
|
||||||
|
country: <b>{countryNames[country]}</b>,
|
||||||
|
browser: BROWSERS[browser],
|
||||||
|
os,
|
||||||
|
device,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -123,6 +154,7 @@ export default function RealtimeLog({ data, websites }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.table}>
|
<div className={styles.table}>
|
||||||
|
<FilterButtons buttons={buttons} selected={filter} onClick={setFilter} />
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<FormattedMessage id="label.realtime-logs" defaultMessage="Realtime logs" />
|
<FormattedMessage id="label.realtime-logs" defaultMessage="Realtime logs" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import React, { useMemo, useState, useCallback } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
import firstBy from 'thenby';
|
||||||
|
import { percentFilter } from 'lib/filters';
|
||||||
|
import DataTable from './DataTable';
|
||||||
|
import FilterButtons from 'components/common/FilterButtons';
|
||||||
|
|
||||||
|
const FILTER_REFERRERS = 0;
|
||||||
|
const FILTER_PAGES = 1;
|
||||||
|
|
||||||
|
export default function RealtimeViews({ websiteId, data, websites }) {
|
||||||
|
const { pageviews } = data;
|
||||||
|
const [filter, setFilter] = useState(FILTER_REFERRERS);
|
||||||
|
const domains = useMemo(() => websites.map(({ domain }) => domain), [websites]);
|
||||||
|
const getDomain = useCallback(
|
||||||
|
id => websites.find(({ website_id }) => website_id === id)?.domain,
|
||||||
|
[websites],
|
||||||
|
);
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="metrics.referrers" defaultMessage="Referrers" />,
|
||||||
|
value: FILTER_REFERRERS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <FormattedMessage id="metrics.pages" defaultMessage="Pages" />,
|
||||||
|
value: FILTER_PAGES,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const [referrers, pages] = useMemo(() => {
|
||||||
|
if (pageviews) {
|
||||||
|
const referrers = percentFilter(
|
||||||
|
pageviews
|
||||||
|
.reduce((arr, { referrer }) => {
|
||||||
|
if (referrer?.startsWith('http')) {
|
||||||
|
const hostname = new URL(referrer).hostname.replace(/^www\./, '');
|
||||||
|
|
||||||
|
if (hostname && !domains.includes(hostname)) {
|
||||||
|
const row = arr.find(({ x }) => x === hostname);
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
arr.push({ x: hostname, y: 1 });
|
||||||
|
} else {
|
||||||
|
row.y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}, [])
|
||||||
|
.sort(firstBy('y', -1)),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pages = percentFilter(
|
||||||
|
pageviews
|
||||||
|
.reduce((arr, { url, website_id }) => {
|
||||||
|
if (url?.startsWith('/')) {
|
||||||
|
if (!websiteId) {
|
||||||
|
url = `${getDomain(website_id)}${url}`;
|
||||||
|
}
|
||||||
|
const row = arr.find(({ x }) => x === url);
|
||||||
|
|
||||||
|
if (!row) {
|
||||||
|
arr.push({ x: url, y: 1 });
|
||||||
|
} else {
|
||||||
|
row.y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}, [])
|
||||||
|
.sort(firstBy('y', -1)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return [referrers, pages];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [pageviews]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FilterButtons buttons={buttons} selected={filter} onClick={setFilter} />
|
||||||
|
{filter === FILTER_REFERRERS && (
|
||||||
|
<DataTable
|
||||||
|
title={<FormattedMessage id="metrics.referrers" defaultMessage="Referrers" />}
|
||||||
|
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
||||||
|
data={referrers}
|
||||||
|
height={400}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{filter === FILTER_PAGES && (
|
||||||
|
<DataTable
|
||||||
|
title={<FormattedMessage id="metrics.pages" defaultMessage="Pages" />}
|
||||||
|
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
||||||
|
data={pages}
|
||||||
|
height={400}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import ButtonGroup from 'components/common/ButtonGroup';
|
import FilterButtons from 'components/common/FilterButtons';
|
||||||
import ButtonLayout from 'components/layout/ButtonLayout';
|
|
||||||
import { FILTER_DOMAIN_ONLY, FILTER_COMBINED, FILTER_RAW } from 'lib/constants';
|
|
||||||
import { refFilter } from 'lib/filters';
|
import { refFilter } from 'lib/filters';
|
||||||
|
|
||||||
|
export const FILTER_DOMAIN_ONLY = 0;
|
||||||
|
export const FILTER_COMBINED = 1;
|
||||||
|
export const FILTER_RAW = 2;
|
||||||
|
|
||||||
export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
|
export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
|
||||||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||||
|
|
||||||
|
@ -52,11 +54,3 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters,
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilterButtons = ({ buttons, selected, onClick }) => {
|
|
||||||
return (
|
|
||||||
<ButtonLayout>
|
|
||||||
<ButtonGroup size="xsmall" items={buttons} selectedItem={selected} onClick={onClick} />
|
|
||||||
</ButtonLayout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -9,16 +9,14 @@ import RealtimeLog from 'components/metrics/RealtimeLog';
|
||||||
import RealtimeHeader from 'components/metrics/RealtimeHeader';
|
import RealtimeHeader from 'components/metrics/RealtimeHeader';
|
||||||
import WorldMap from 'components/common/WorldMap';
|
import WorldMap from 'components/common/WorldMap';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import DataTable from 'components/metrics/DataTable';
|
||||||
|
import RealtimeViews from 'components/metrics/RealtimeViews';
|
||||||
import useFetch from 'hooks/useFetch';
|
import useFetch from 'hooks/useFetch';
|
||||||
import useLocale from 'hooks/useLocale';
|
import useLocale from 'hooks/useLocale';
|
||||||
import useCountryNames from 'hooks/useCountryNames';
|
import useCountryNames from 'hooks/useCountryNames';
|
||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
import { TOKEN_HEADER } from 'lib/constants';
|
import { TOKEN_HEADER, REALTIME_RANGE, REALTIME_INTERVAL } from 'lib/constants';
|
||||||
import styles from './RealtimeDashboard.module.css';
|
import styles from './RealtimeDashboard.module.css';
|
||||||
|
|
||||||
const REALTIME_RANGE = 30;
|
|
||||||
const REALTIME_INTERVAL = 3000;
|
|
||||||
|
|
||||||
function mergeData(state, data, time) {
|
function mergeData(state, data, time) {
|
||||||
const ids = state.map(({ __id }) => __id);
|
const ids = state.map(({ __id }) => __id);
|
||||||
return state
|
return state
|
||||||
|
@ -43,7 +41,10 @@ export default function RealtimeDashboard() {
|
||||||
headers: { [TOKEN_HEADER]: init?.token },
|
headers: { [TOKEN_HEADER]: init?.token },
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderCountryName = useCallback(({ x }) => countryNames[x], []);
|
const renderCountryName = useCallback(
|
||||||
|
({ x }) => <span className={locale}>{countryNames[x]}</span>,
|
||||||
|
[countryNames],
|
||||||
|
);
|
||||||
|
|
||||||
const realtimeData = useMemo(() => {
|
const realtimeData = useMemo(() => {
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -83,38 +84,11 @@ export default function RealtimeDashboard() {
|
||||||
return [];
|
return [];
|
||||||
}, [realtimeData?.sessions]);
|
}, [realtimeData?.sessions]);
|
||||||
|
|
||||||
const referrers = useMemo(() => {
|
|
||||||
if (realtimeData?.pageviews) {
|
|
||||||
return percentFilter(
|
|
||||||
realtimeData.pageviews
|
|
||||||
.reduce((arr, { referrer }) => {
|
|
||||||
if (referrer?.startsWith('http')) {
|
|
||||||
const { hostname } = new URL(referrer);
|
|
||||||
|
|
||||||
if (!data.domains.includes(hostname)) {
|
|
||||||
const row = arr.find(({ x }) => x === hostname);
|
|
||||||
|
|
||||||
if (!row) {
|
|
||||||
arr.push({ x: hostname, y: 1 });
|
|
||||||
} else {
|
|
||||||
row.y += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arr;
|
|
||||||
}, [])
|
|
||||||
.sort(firstBy('y', -1)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [realtimeData?.pageviews]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (init && !data) {
|
if (init && !data) {
|
||||||
const { websites, data } = init;
|
const { websites, data } = init;
|
||||||
const domains = init.websites.map(({ domain }) => domain);
|
|
||||||
|
|
||||||
setData({ websites, domains, ...data });
|
setData({ websites, ...data });
|
||||||
}
|
}
|
||||||
}, [init]);
|
}, [init]);
|
||||||
|
|
||||||
|
@ -158,12 +132,7 @@ export default function RealtimeDashboard() {
|
||||||
<GridLayout>
|
<GridLayout>
|
||||||
<GridRow>
|
<GridRow>
|
||||||
<GridColumn xs={12} lg={4}>
|
<GridColumn xs={12} lg={4}>
|
||||||
<DataTable
|
<RealtimeViews websiteId={websiteId} data={realtimeData} websites={websites} />
|
||||||
title={<FormattedMessage id="metrics.referrers" defaultMessage="Referrers" />}
|
|
||||||
metric={<FormattedMessage id="metrics.views" defaultMessage="Views" />}
|
|
||||||
data={referrers}
|
|
||||||
height={400}
|
|
||||||
/>
|
|
||||||
</GridColumn>
|
</GridColumn>
|
||||||
<GridColumn xs={12} lg={8}>
|
<GridColumn xs={12} lg={8}>
|
||||||
<RealtimeLog data={realtimeData} websites={websites} />
|
<RealtimeLog data={realtimeData} websites={websites} />
|
||||||
|
|
|
@ -173,7 +173,14 @@ export default function WebsiteDetails({ websiteId }) {
|
||||||
contentClassName={styles.content}
|
contentClassName={styles.content}
|
||||||
menu={menuOptions}
|
menu={menuOptions}
|
||||||
>
|
>
|
||||||
<DetailsComponent {...tableProps} height={500} limit={false} showFilters virtualize />
|
<DetailsComponent
|
||||||
|
{...tableProps}
|
||||||
|
height={500}
|
||||||
|
limit={false}
|
||||||
|
animte={false}
|
||||||
|
showFilters
|
||||||
|
virtualize
|
||||||
|
/>
|
||||||
</MenuLayout>
|
</MenuLayout>
|
||||||
)}
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
|
|
|
@ -33,7 +33,7 @@ export async function allowQuery(req, skipToken) {
|
||||||
const website = await getWebsiteById(websiteId);
|
const website = await getWebsiteById(websiteId);
|
||||||
|
|
||||||
if (website) {
|
if (website) {
|
||||||
if (token && !skipToken) {
|
if (token && token !== 'undefined' && !skipToken) {
|
||||||
return isValidToken(token, { website_id: websiteId });
|
return isValidToken(token, { website_id: websiteId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,15 @@ export const THEME_CONFIG = 'umami.theme';
|
||||||
export const VERSION_CHECK = 'umami.version-check';
|
export const VERSION_CHECK = 'umami.version-check';
|
||||||
export const TOKEN_HEADER = 'x-umami-token';
|
export const TOKEN_HEADER = 'x-umami-token';
|
||||||
|
|
||||||
|
export const DEFAULT_LOCALE = 'en-US';
|
||||||
|
export const DEFAULT_THEME = 'light';
|
||||||
|
export const DEFAUL_CHART_HEIGHT = 400;
|
||||||
|
export const DEFAULT_ANIMATION_DURATION = 300;
|
||||||
|
export const DEFAULT_DATE_RANGE = '24hour';
|
||||||
|
|
||||||
|
export const REALTIME_RANGE = 30;
|
||||||
|
export const REALTIME_INTERVAL = 3000;
|
||||||
|
|
||||||
export const THEME_COLORS = {
|
export const THEME_COLORS = {
|
||||||
light: {
|
light: {
|
||||||
primary: '#2680eb',
|
primary: '#2680eb',
|
||||||
|
@ -52,12 +61,6 @@ export const EVENT_COLORS = [
|
||||||
'#ffec16',
|
'#ffec16',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const DEFAULT_LOCALE = 'en-US';
|
|
||||||
export const DEFAULT_THEME = 'light';
|
|
||||||
export const DEFAUL_CHART_HEIGHT = 400;
|
|
||||||
export const DEFAULT_ANIMATION_DURATION = 300;
|
|
||||||
export const DEFAULT_DATE_RANGE = '24hour';
|
|
||||||
|
|
||||||
export const POSTGRESQL = 'postgresql';
|
export const POSTGRESQL = 'postgresql';
|
||||||
export const MYSQL = 'mysql';
|
export const MYSQL = 'mysql';
|
||||||
|
|
||||||
|
@ -77,10 +80,6 @@ export const POSTGRESQL_DATE_FORMATS = {
|
||||||
year: 'YYYY-01-01',
|
year: 'YYYY-01-01',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FILTER_DOMAIN_ONLY = 0;
|
|
||||||
export const FILTER_COMBINED = 1;
|
|
||||||
export const FILTER_RAW = 2;
|
|
||||||
|
|
||||||
export const DOMAIN_REGEX = /localhost(:\d{1,5})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}/;
|
export const DOMAIN_REGEX = /localhost(:\d{1,5})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}/;
|
||||||
|
|
||||||
export const DESKTOP_SCREEN_WIDTH = 1920;
|
export const DESKTOP_SCREEN_WIDTH = 1920;
|
||||||
|
|
Loading…
Reference in New Issue