mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 05:37:20 +01:00
Referrer filtering.
This commit is contained in:
parent
65d4094095
commit
ebd52335bb
15 changed files with 158 additions and 54 deletions
|
|
@ -6,6 +6,8 @@
|
|||
margin: auto;
|
||||
display: flex;
|
||||
z-index: 1;
|
||||
background-color: var(--gray50);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
|
|
|
|||
27
components/metrics/FilterTags.js
Normal file
27
components/metrics/FilterTags.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Button from 'components/common/Button';
|
||||
import Times from 'assets/times.svg';
|
||||
import styles from './FilterTags.module.css';
|
||||
|
||||
export default function FilterTags({ params, onClick }) {
|
||||
if (Object.keys(params).filter(key => params[key]).length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={classNames(styles.filters, 'col-12')}>
|
||||
{Object.keys(params).map(key => {
|
||||
if (!params[key]) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={styles.tag}>
|
||||
<Button icon={<Times />} onClick={() => onClick(key)} variant="action" iconRight>
|
||||
{`${key}: ${params[key]}`}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
14
components/metrics/FilterTags.module.css
Normal file
14
components/metrics/FilterTags.module.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.filters {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.tag {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tag + .tag {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ export default function MetricsBar({ websiteId, className }) {
|
|||
const { startDate, endDate, modified } = dateRange;
|
||||
const [format, setFormat] = useState(true);
|
||||
const {
|
||||
query: { url },
|
||||
query: { url, ref },
|
||||
} = usePageQuery();
|
||||
|
||||
const { data, error, loading } = useFetch(
|
||||
|
|
@ -28,10 +28,11 @@ export default function MetricsBar({ websiteId, className }) {
|
|||
start_at: +startDate,
|
||||
end_at: +endDate,
|
||||
url,
|
||||
ref,
|
||||
},
|
||||
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||
},
|
||||
[url, modified],
|
||||
[modified, url, ref],
|
||||
);
|
||||
|
||||
const formatFunc = format
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default function PagesTable({ websiteId, websiteDomain, showFilters, ...p
|
|||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||
const {
|
||||
resolve,
|
||||
query: { url },
|
||||
query: { url: currentUrl },
|
||||
} = usePageQuery();
|
||||
|
||||
const buttons = [
|
||||
|
|
@ -27,16 +27,16 @@ export default function PagesTable({ websiteId, websiteDomain, showFilters, ...p
|
|||
{ label: <FormattedMessage id="metrics.filter.raw" defaultMessage="Raw" />, value: FILTER_RAW },
|
||||
];
|
||||
|
||||
const renderLink = ({ x }) => {
|
||||
const renderLink = ({ x: url }) => {
|
||||
return (
|
||||
<Link href={resolve({ url: x })} replace={true}>
|
||||
<Link href={resolve({ url })} replace={true}>
|
||||
<a
|
||||
className={classNames({
|
||||
[styles.inactive]: url && x !== url,
|
||||
[styles.active]: x === url,
|
||||
[styles.inactive]: currentUrl && url !== currentUrl,
|
||||
[styles.active]: url === currentUrl,
|
||||
})}
|
||||
>
|
||||
{safeDecodeURI(x)}
|
||||
{safeDecodeURI(url)}
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ import MetricsTable from './MetricsTable';
|
|||
import FilterButtons from 'components/common/FilterButtons';
|
||||
import { refFilter } from 'lib/filters';
|
||||
import { safeDecodeURI } from 'lib/url';
|
||||
import Link from 'next/link';
|
||||
import classNames from 'classnames';
|
||||
import usePageQuery from 'hooks/usePageQuery';
|
||||
import External from 'assets/arrow-up-right-from-square.svg';
|
||||
import Icon from '../common/Icon';
|
||||
import styles from './ReferrersTable.module.css';
|
||||
|
||||
export const FILTER_DOMAIN_ONLY = 0;
|
||||
export const FILTER_COMBINED = 1;
|
||||
|
|
@ -11,6 +17,10 @@ export const FILTER_RAW = 2;
|
|||
|
||||
export default function ReferrersTable({ websiteId, websiteDomain, showFilters, ...props }) {
|
||||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||
const {
|
||||
resolve,
|
||||
query: { ref: currentRef },
|
||||
} = usePageQuery();
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
|
|
@ -24,13 +34,24 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters,
|
|||
{ label: <FormattedMessage id="metrics.filter.raw" defaultMessage="Raw" />, value: FILTER_RAW },
|
||||
];
|
||||
|
||||
const renderLink = ({ w: href, x: url }) => {
|
||||
return (href || url).startsWith('http') ? (
|
||||
<a href={href || url} target="_blank" rel="noreferrer">
|
||||
{safeDecodeURI(url)}
|
||||
</a>
|
||||
) : (
|
||||
safeDecodeURI(url)
|
||||
const renderLink = ({ w: link, x: label }) => {
|
||||
console.log({ link, label });
|
||||
return (
|
||||
<div className={styles.row}>
|
||||
<Link href={resolve({ ref: label })} replace={true}>
|
||||
<a
|
||||
className={classNames(styles.label, {
|
||||
[styles.inactive]: currentRef && label !== currentRef,
|
||||
[styles.active]: label === currentRef,
|
||||
})}
|
||||
>
|
||||
{safeDecodeURI(label)}
|
||||
</a>
|
||||
</Link>
|
||||
<a href={link || label} target="_blank" rel="noreferrer noopener" className={styles.link}>
|
||||
<Icon icon={<External />} className={styles.icon} />
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
31
components/metrics/ReferrersTable.module.css
Normal file
31
components/metrics/ReferrersTable.module.css
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
body .inactive {
|
||||
color: var(--gray500);
|
||||
}
|
||||
|
||||
body .active {
|
||||
color: var(--gray900);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.row .link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.row .label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.row:hover .link {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -5,17 +5,16 @@ import MetricsBar from './MetricsBar';
|
|||
import WebsiteHeader from './WebsiteHeader';
|
||||
import DateFilter from 'components/common/DateFilter';
|
||||
import StickyHeader from 'components/helpers/StickyHeader';
|
||||
import Button from 'components/common/Button';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import useDateRange from 'hooks/useDateRange';
|
||||
import useTimezone from 'hooks/useTimezone';
|
||||
import usePageQuery from 'hooks/usePageQuery';
|
||||
import { getDateArray, getDateLength } from 'lib/date';
|
||||
import Times from 'assets/times.svg';
|
||||
import ErrorMessage from 'components/common/ErrorMessage';
|
||||
import FilterTags from 'components/metrics/FilterTags';
|
||||
import useShareToken from 'hooks/useShareToken';
|
||||
import { TOKEN_HEADER } from 'lib/constants';
|
||||
import styles from './WebsiteChart.module.css';
|
||||
import ErrorMessage from '../common/ErrorMessage';
|
||||
import useShareToken from '../../hooks/useShareToken';
|
||||
import { TOKEN_HEADER } from '../../lib/constants';
|
||||
|
||||
export default function WebsiteChart({
|
||||
websiteId,
|
||||
|
|
@ -33,7 +32,7 @@ export default function WebsiteChart({
|
|||
const {
|
||||
router,
|
||||
resolve,
|
||||
query: { url },
|
||||
query: { url, ref },
|
||||
} = usePageQuery();
|
||||
|
||||
const { data, loading, error } = useFetch(
|
||||
|
|
@ -45,11 +44,12 @@ export default function WebsiteChart({
|
|||
unit,
|
||||
tz: timezone,
|
||||
url,
|
||||
ref,
|
||||
},
|
||||
onDataLoad,
|
||||
headers: { [TOKEN_HEADER]: shareToken?.token },
|
||||
},
|
||||
[url, modified],
|
||||
[modified, url, ref],
|
||||
);
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
|
|
@ -62,8 +62,8 @@ export default function WebsiteChart({
|
|||
return { pageviews: [], sessions: [] };
|
||||
}, [data]);
|
||||
|
||||
function handleCloseFilter() {
|
||||
router.push(resolve({ url: undefined }));
|
||||
function handleCloseFilter(param) {
|
||||
router.push(resolve({ [param]: undefined }));
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -75,7 +75,7 @@ export default function WebsiteChart({
|
|||
stickyClassName={styles.sticky}
|
||||
enabled={stickyHeader}
|
||||
>
|
||||
{url && <PageFilter url={url} onClick={handleCloseFilter} />}
|
||||
<FilterTags params={{ url, ref }} onClick={handleCloseFilter} />
|
||||
<div className="col-12 col-lg-9">
|
||||
<MetricsBar websiteId={websiteId} />
|
||||
</div>
|
||||
|
|
@ -90,7 +90,7 @@ export default function WebsiteChart({
|
|||
</StickyHeader>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col">
|
||||
<div className={classNames(styles.chart, 'col')}>
|
||||
{error && <ErrorMessage />}
|
||||
{!hideChart && (
|
||||
<PageviewsChart
|
||||
|
|
@ -106,13 +106,3 @@ export default function WebsiteChart({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const PageFilter = ({ url, onClick }) => {
|
||||
return (
|
||||
<div className={classNames(styles.url, 'col-12')}>
|
||||
<Button icon={<Times />} onClick={onClick} variant="action" iconRight>
|
||||
{url}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
.container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.chart {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--font-size-large);
|
||||
line-height: 60px;
|
||||
|
|
@ -37,11 +42,6 @@
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.url {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
.filter {
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -118,9 +118,9 @@ export default function WebsiteDetails({ websiteId }) {
|
|||
showLink={false}
|
||||
stickyHeader
|
||||
/>
|
||||
{!chartLoaded && <Loading />}
|
||||
</div>
|
||||
</div>
|
||||
{!chartLoaded && <Loading />}
|
||||
{chartLoaded && !view && (
|
||||
<GridLayout>
|
||||
<GridRow>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue