mirror of
https://github.com/kremalicious/umami.git
synced 2024-06-30 13:41:50 +02:00
Updated cities table.
This commit is contained in:
parent
c22fefaa2d
commit
bb77c2ead6
|
@ -4,7 +4,13 @@ import { useRouter } from 'next/router';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import styles from './SideNav.module.css';
|
import styles from './SideNav.module.css';
|
||||||
|
|
||||||
export function SideNav({ selectedKey, items, shallow, onSelect = () => {} }) {
|
export function SideNav({
|
||||||
|
selectedKey,
|
||||||
|
items,
|
||||||
|
shallow = true,
|
||||||
|
scroll = false,
|
||||||
|
onSelect = () => {},
|
||||||
|
}) {
|
||||||
const { asPath } = useRouter();
|
const { asPath } = useRouter();
|
||||||
return (
|
return (
|
||||||
<Menu items={items} selectedKey={selectedKey} className={styles.menu} onSelect={onSelect}>
|
<Menu items={items} selectedKey={selectedKey} className={styles.menu} onSelect={onSelect}>
|
||||||
|
@ -13,7 +19,7 @@ export function SideNav({ selectedKey, items, shallow, onSelect = () => {} }) {
|
||||||
key={key}
|
key={key}
|
||||||
className={classNames(styles.item, { [styles.selected]: asPath.startsWith(url) })}
|
className={classNames(styles.item, { [styles.selected]: asPath.startsWith(url) })}
|
||||||
>
|
>
|
||||||
<Link href={url} shallow={shallow}>
|
<Link href={url} shallow={shallow} scroll={scroll}>
|
||||||
{label}
|
{label}
|
||||||
</Link>
|
</Link>
|
||||||
</Item>
|
</Item>
|
||||||
|
|
|
@ -1,20 +1,36 @@
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
import MetricsTable from './MetricsTable';
|
import MetricsTable from './MetricsTable';
|
||||||
import { emptyFilter } from 'lib/filters';
|
import { emptyFilter } from 'lib/filters';
|
||||||
import FilterLink from 'components/common/FilterLink';
|
import FilterLink from 'components/common/FilterLink';
|
||||||
import useLocale from 'components/hooks/useLocale';
|
import useLocale from 'components/hooks/useLocale';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
import useCountryNames from 'components/hooks/useCountryNames';
|
||||||
|
|
||||||
export function CitiesTable({ websiteId, ...props }) {
|
export function CitiesTable({ websiteId, ...props }) {
|
||||||
const { locale } = useLocale();
|
const { locale } = useLocale();
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { basePath } = useRouter();
|
||||||
|
const countryNames = useCountryNames(locale);
|
||||||
|
|
||||||
function renderLink({ x }) {
|
const renderLabel = (city, country) => {
|
||||||
|
const name = countryNames[country];
|
||||||
|
return name ? `${city}, ${name}` : city;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLink = ({ x: city, country }) => {
|
||||||
return (
|
return (
|
||||||
<div className={locale}>
|
<FilterLink id="city" value={city} label={renderLabel(city, country)}>
|
||||||
<FilterLink id="city" value={x} />
|
{country && (
|
||||||
</div>
|
<img
|
||||||
|
src={`${basePath}/images/flags/${
|
||||||
|
country?.split?.('-')?.[0]?.toLowerCase() || 'xx'
|
||||||
|
}.png`}
|
||||||
|
alt={country}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FilterLink>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MetricsTable
|
<MetricsTable
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { formatNumber, formatLongNumber } from 'lib/format';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
import styles from './DataTable.module.css';
|
import styles from './DataTable.module.css';
|
||||||
|
|
||||||
export function DataTable({
|
export function ListTable({
|
||||||
data = [],
|
data = [],
|
||||||
title,
|
title,
|
||||||
metric,
|
metric,
|
||||||
|
@ -94,7 +94,7 @@ const AnimatedRow = ({
|
||||||
<div className={styles.percent}>
|
<div className={styles.percent}>
|
||||||
<animated.div className={styles.bar} style={{ width: props.width.to(n => `${n}%`) }} />
|
<animated.div className={styles.bar} style={{ width: props.width.to(n => `${n}%`) }} />
|
||||||
<animated.span className={styles.percentValue}>
|
<animated.span className={styles.percentValue}>
|
||||||
{props.width.to(n => `${n.toFixed(0)}%`)}
|
{props.width.to(n => `${n?.toFixed?.(0)}%`)}
|
||||||
</animated.span>
|
</animated.span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -102,4 +102,4 @@ const AnimatedRow = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DataTable;
|
export default ListTable;
|
|
@ -8,7 +8,7 @@ import { percentFilter } from 'lib/filters';
|
||||||
import useDateRange from 'components/hooks/useDateRange';
|
import useDateRange from 'components/hooks/useDateRange';
|
||||||
import usePageQuery from 'components/hooks/usePageQuery';
|
import usePageQuery from 'components/hooks/usePageQuery';
|
||||||
import ErrorMessage from 'components/common/ErrorMessage';
|
import ErrorMessage from 'components/common/ErrorMessage';
|
||||||
import DataTable from './DataTable';
|
import ListTable from './ListTable';
|
||||||
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
||||||
import Icons from 'components/icons';
|
import Icons from 'components/icons';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
@ -104,7 +104,7 @@ export function MetricsTable({
|
||||||
<div className={classNames(styles.container, className)}>
|
<div className={classNames(styles.container, className)}>
|
||||||
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
||||||
{error && <ErrorMessage />}
|
{error && <ErrorMessage />}
|
||||||
{data && !error && <DataTable {...props} data={filteredData} className={className} />}
|
{data && !error && <ListTable {...props} data={filteredData} className={className} />}
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
{data && !error && limit && (
|
{data && !error && limit && (
|
||||||
<Link href={router.pathname} as={resolveUrl({ view: type })}>
|
<Link href={router.pathname} as={resolveUrl({ view: type })}>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import useLocale from 'components/hooks/useLocale';
|
import useLocale from 'components/hooks/useLocale';
|
||||||
import useCountryNames from 'components/hooks/useCountryNames';
|
import useCountryNames from 'components/hooks/useCountryNames';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
@ -24,7 +24,7 @@ export function RealtimeCountries({ data }) {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.countries)}
|
title={formatMessage(labels.countries)}
|
||||||
metric={formatMessage(labels.visitors)}
|
metric={formatMessage(labels.visitors)}
|
||||||
data={data}
|
data={data}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { useMemo, useState } from 'react';
|
||||||
import { ButtonGroup, Button, Flexbox } from 'react-basics';
|
import { ButtonGroup, Button, Flexbox } from 'react-basics';
|
||||||
import firstBy from 'thenby';
|
import firstBy from 'thenby';
|
||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
|
import { FILTER_PAGES, FILTER_REFERRERS } from 'lib/constants';
|
||||||
import useMessages from 'components/hooks/useMessages';
|
import useMessages from 'components/hooks/useMessages';
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
{filter === FILTER_REFERRERS && (
|
{filter === FILTER_REFERRERS && (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.referrers)}
|
title={formatMessage(labels.referrers)}
|
||||||
metric={formatMessage(labels.views)}
|
metric={formatMessage(labels.views)}
|
||||||
renderLabel={renderLink}
|
renderLabel={renderLink}
|
||||||
|
@ -90,7 +90,7 @@ export function RealtimeUrls({ websiteDomain, data = {} }) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{filter === FILTER_PAGES && (
|
{filter === FILTER_PAGES && (
|
||||||
<DataTable
|
<ListTable
|
||||||
title={formatMessage(labels.pages)}
|
title={formatMessage(labels.pages)}
|
||||||
metric={formatMessage(labels.views)}
|
metric={formatMessage(labels.views)}
|
||||||
renderLabel={renderLink}
|
renderLabel={renderLink}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import DataTable from 'components/metrics/DataTable';
|
import ListTable from 'components/metrics/ListTable';
|
||||||
import { useMessages } from 'components/hooks';
|
import { useMessages } from 'components/hooks';
|
||||||
import { ReportContext } from '../Report';
|
import { ReportContext } from '../Report';
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ export function FunnelTable() {
|
||||||
const { report } = useContext(ReportContext);
|
const { report } = useContext(ReportContext);
|
||||||
const { formatMessage, labels } = useMessages();
|
const { formatMessage, labels } = useMessages();
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<ListTable
|
||||||
data={report?.data}
|
data={report?.data}
|
||||||
title={formatMessage(labels.url)}
|
title={formatMessage(labels.url)}
|
||||||
metric={formatMessage(labels.visitors)}
|
metric={formatMessage(labels.visitors)}
|
||||||
|
|
|
@ -28,14 +28,18 @@ async function relationalQuery(websiteId: string, column: string, filters: Query
|
||||||
|
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select ${column} x, count(*) y
|
select
|
||||||
|
${column} x,
|
||||||
|
count(*) y
|
||||||
|
${column === 'city' ? ', country' : ''}
|
||||||
from website_event
|
from website_event
|
||||||
${joinSession}
|
${joinSession}
|
||||||
where website_event.website_id = {{websiteId::uuid}}
|
where website_event.website_id = {{websiteId::uuid}}
|
||||||
and website_event.created_at between {{startDate}} and {{endDate}}
|
and website_event.created_at between {{startDate}} and {{endDate}}
|
||||||
and website_event.event_type = {{eventType}}
|
and website_event.event_type = {{eventType}}
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by 1
|
group by 1
|
||||||
|
${column === 'city' ? ', 3' : ''}
|
||||||
order by 2 desc
|
order by 2 desc
|
||||||
limit 100`,
|
limit 100`,
|
||||||
params,
|
params,
|
||||||
|
@ -52,13 +56,16 @@ async function clickhouseQuery(websiteId: string, column: string, filters: Query
|
||||||
return rawQuery(
|
return rawQuery(
|
||||||
`
|
`
|
||||||
select
|
select
|
||||||
${column} x, count(distinct session_id) y
|
${column} x,
|
||||||
|
count(distinct session_id) y
|
||||||
|
${column === 'city' ? ', country' : ''}
|
||||||
from website_event
|
from website_event
|
||||||
where website_id = {websiteId:UUID}
|
where website_id = {websiteId:UUID}
|
||||||
and created_at between {startDate:DateTime} and {endDate:DateTime}
|
and created_at between {startDate:DateTime} and {endDate:DateTime}
|
||||||
and event_type = {eventType:UInt32}
|
and event_type = {eventType:UInt32}
|
||||||
${filterQuery}
|
${filterQuery}
|
||||||
group by x
|
group by x
|
||||||
|
${column === 'city' ? ', country' : ''}
|
||||||
order by y desc
|
order by y desc
|
||||||
limit 100
|
limit 100
|
||||||
`,
|
`,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user