mirror of
https://github.com/umami-software/umami.git
synced 2026-02-08 06:37:18 +01:00
Typescript refactor.
This commit is contained in:
parent
b578162cb6
commit
7c42f0da82
173 changed files with 968 additions and 549 deletions
|
|
@ -4,14 +4,22 @@ import useApi from 'components/hooks/useApi';
|
|||
import useMessages from 'components/hooks/useMessages';
|
||||
import styles from './ActiveUsers.module.css';
|
||||
|
||||
export function ActiveUsers({ websiteId, value, refetchInterval = 60000 }) {
|
||||
export function ActiveUsers({
|
||||
websiteId,
|
||||
value,
|
||||
refetchInterval = 60000,
|
||||
}: {
|
||||
websiteId: string;
|
||||
value?: number;
|
||||
refetchInterval?: number;
|
||||
}) {
|
||||
const { formatMessage, messages } = useMessages();
|
||||
const { get, useQuery } = useApi();
|
||||
const { data } = useQuery({
|
||||
queryKey: ['websites:active', websiteId],
|
||||
queryFn: () => get(`/websites/${websiteId}/active`),
|
||||
refetchInterval,
|
||||
enabled: !!websiteId,
|
||||
refetchInterval,
|
||||
});
|
||||
|
||||
const count = useMemo(() => {
|
||||
|
|
@ -10,12 +10,28 @@ import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
|||
import { renderNumberLabels } from 'lib/charts';
|
||||
import styles from './BarChart.module.css';
|
||||
|
||||
export interface BarChartProps {
|
||||
datasets?: any[];
|
||||
unit?: string;
|
||||
animationDuration?: number;
|
||||
stacked?: boolean;
|
||||
isLoading?: boolean;
|
||||
renderXLabel?: (label: string, index: number, values: any[]) => string;
|
||||
renderYLabel?: (label: string, index: number, values: any[]) => string;
|
||||
XAxisType?: string;
|
||||
YAxisType?: string;
|
||||
renderTooltipPopup?: (setTooltipPopup: (data: any) => void, model: any) => void;
|
||||
onCreate?: (chart: any) => void;
|
||||
onUpdate?: (chart: any) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function BarChart({
|
||||
datasets,
|
||||
datasets = [],
|
||||
unit,
|
||||
animationDuration = DEFAULT_ANIMATION_DURATION,
|
||||
stacked = false,
|
||||
loading = false,
|
||||
isLoading = false,
|
||||
renderXLabel,
|
||||
renderYLabel,
|
||||
XAxisType = 'time',
|
||||
|
|
@ -24,7 +40,7 @@ export function BarChart({
|
|||
onCreate,
|
||||
onUpdate,
|
||||
className,
|
||||
}) {
|
||||
}: BarChartProps) {
|
||||
const canvas = useRef();
|
||||
const chart = useRef(null);
|
||||
const [tooltip, setTooltipPopup] = useState(null);
|
||||
|
|
@ -85,7 +101,7 @@ export function BarChart({
|
|||
color: colors.chart.line,
|
||||
},
|
||||
ticks: {
|
||||
color: colors.text,
|
||||
color: colors.chart.text,
|
||||
callback: renderYLabel || renderNumberLabels,
|
||||
},
|
||||
},
|
||||
|
|
@ -106,16 +122,15 @@ export function BarChart({
|
|||
const createChart = () => {
|
||||
Chart.defaults.font.family = 'Inter';
|
||||
|
||||
const options = getOptions();
|
||||
|
||||
chart.current = new Chart(canvas.current, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
datasets,
|
||||
},
|
||||
options,
|
||||
});
|
||||
|
||||
chart.current.options = getOptions();
|
||||
|
||||
onCreate?.(chart.current);
|
||||
};
|
||||
|
||||
|
|
@ -147,7 +162,7 @@ export function BarChart({
|
|||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={classNames(styles.chart, className)}>
|
||||
{loading && <Loading position="page" icon="dots" />}
|
||||
{isLoading && <Loading position="page" icon="dots" />}
|
||||
<canvas ref={canvas} />
|
||||
</div>
|
||||
<Legend chart={chart.current} />
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import FilterLink from 'components/common/FilterLink';
|
||||
import MetricsTable from 'components/metrics/MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from 'components/metrics/MetricsTable';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import useFormat from 'components/hooks/useFormat';
|
||||
|
||||
export function BrowsersTable({ websiteId, ...props }) {
|
||||
export function BrowsersTable(props: MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatBrowser } = useFormat();
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ export function BrowsersTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.browsers)}
|
||||
type="browser"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
);
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import { emptyFilter } from 'lib/filters';
|
||||
import FilterLink from 'components/common/FilterLink';
|
||||
import useLocale from 'components/hooks/useLocale';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import useCountryNames from 'components/hooks/useCountryNames';
|
||||
|
||||
export function CitiesTable({ websiteId, ...props }) {
|
||||
export function CitiesTable(props: MetricsTableProps) {
|
||||
const { locale } = useLocale();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const countryNames = useCountryNames(locale);
|
||||
|
||||
const renderLabel = (city, country) => {
|
||||
const renderLabel = (city: string, country: string) => {
|
||||
const name = countryNames[country];
|
||||
return name ? `${city}, ${name}` : city;
|
||||
};
|
||||
|
|
@ -34,7 +34,6 @@ export function CitiesTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.cities)}
|
||||
type="city"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
dataFilter={emptyFilter}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
|
|
@ -1,15 +1,24 @@
|
|||
import FilterLink from 'components/common/FilterLink';
|
||||
import useCountryNames from 'components/hooks/useCountryNames';
|
||||
import { useLocale, useMessages, useFormat } from 'components/hooks';
|
||||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
|
||||
export function CountriesTable({ websiteId, ...props }) {
|
||||
export function CountriesTable({
|
||||
onDataLoad,
|
||||
...props
|
||||
}: {
|
||||
onDataLoad: (data: any) => void;
|
||||
} & MetricsTableProps) {
|
||||
const { locale } = useLocale();
|
||||
const countryNames = useCountryNames(locale);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatCountry } = useFormat();
|
||||
|
||||
function renderLink({ x: code }) {
|
||||
const handleDataLoad = (data: any) => {
|
||||
onDataLoad?.(data);
|
||||
};
|
||||
|
||||
const renderLink = ({ x: code }) => {
|
||||
return (
|
||||
<FilterLink
|
||||
id="country"
|
||||
|
|
@ -23,7 +32,7 @@ export function CountriesTable({ websiteId, ...props }) {
|
|||
/>
|
||||
</FilterLink>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MetricsTable
|
||||
|
|
@ -31,8 +40,8 @@ export function CountriesTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.countries)}
|
||||
type="country"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
renderLabel={renderLink}
|
||||
onDataLoad={handleDataLoad}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ export function DatePickerForm({
|
|||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.filter}>
|
||||
<ButtonGroup selectedKey={selected} onSelect={setSelected}>
|
||||
<ButtonGroup selectedKey={selected} onSelect={key => setSelected(key as any)}>
|
||||
<Button key={FILTER_DAY}>{formatMessage(labels.singleDay)}</Button>
|
||||
<Button key={FILTER_RANGE}>{formatMessage(labels.dateRange)}</Button>
|
||||
</ButtonGroup>
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import FilterLink from 'components/common/FilterLink';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import { useFormat } from 'components/hooks';
|
||||
|
||||
export function DevicesTable({ websiteId, ...props }) {
|
||||
export function DevicesTable(props: MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { formatDevice } = useFormat();
|
||||
|
||||
|
|
@ -26,7 +26,6 @@ export function DevicesTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.devices)}
|
||||
type="device"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
);
|
||||
|
|
@ -7,7 +7,13 @@ import { useApi, useLocale, useDateRange, useTimezone, useNavigation } from 'com
|
|||
import { EVENT_COLORS } from 'lib/constants';
|
||||
import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts';
|
||||
|
||||
export function EventsChart({ websiteId, className, token }) {
|
||||
export interface EventsChartProps {
|
||||
websiteId: string;
|
||||
className?: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
export function EventsChart({ websiteId, className, token }: EventsChartProps) {
|
||||
const { get, useQuery } = useApi();
|
||||
const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId);
|
||||
const { locale } = useLocale();
|
||||
|
|
@ -71,7 +77,6 @@ export function EventsChart({ websiteId, className, token }) {
|
|||
className={className}
|
||||
datasets={datasets}
|
||||
unit={unit}
|
||||
height={300}
|
||||
loading={isLoading}
|
||||
stacked
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
|
||||
export function EventsTable({ websiteId, ...props }) {
|
||||
export function EventsTable(props: MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
function handleDataLoad(data) {
|
||||
function handleDataLoad(data: any) {
|
||||
props.onDataLoad?.(data);
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,6 @@ export function EventsTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.events)}
|
||||
type="event"
|
||||
metric={formatMessage(labels.actions)}
|
||||
websiteId={websiteId}
|
||||
onDataLoad={handleDataLoad}
|
||||
/>
|
||||
);
|
||||
|
|
@ -18,7 +18,7 @@ export function FilterTags({ params }) {
|
|||
return null;
|
||||
}
|
||||
|
||||
function handleCloseFilter(param) {
|
||||
function handleCloseFilter(param?: string) {
|
||||
if (!param) {
|
||||
router.push(makeUrl({ view }, true));
|
||||
} else {
|
||||
|
|
@ -44,7 +44,7 @@ export function FilterTags({ params }) {
|
|||
</div>
|
||||
);
|
||||
})}
|
||||
<Button size="sm" variant="quiet" onClick={() => handleCloseFilter()}>
|
||||
<Button size="sm" variant="quiet" onClick={handleCloseFilter}>
|
||||
<Icon>
|
||||
<Icons.Close />
|
||||
</Icon>
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import { percentFilter } from 'lib/filters';
|
||||
import useLanguageNames from 'components/hooks/useLanguageNames';
|
||||
import useLocale from 'components/hooks/useLocale';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
|
||||
export function LanguagesTable({ websiteId, onDataLoad, ...props }) {
|
||||
export function LanguagesTable({
|
||||
onDataLoad,
|
||||
...props
|
||||
}: { onDataLoad: (data: any) => void } & MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { locale } = useLocale();
|
||||
const languageNames = useLanguageNames(locale);
|
||||
|
|
@ -19,7 +22,6 @@ export function LanguagesTable({ websiteId, onDataLoad, ...props }) {
|
|||
title={formatMessage(labels.languages)}
|
||||
type="language"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
onDataLoad={data => onDataLoad?.(percentFilter(data))}
|
||||
renderLabel={renderLabel}
|
||||
/>
|
||||
|
|
@ -5,9 +5,22 @@ import Empty from 'components/common/Empty';
|
|||
import { formatLongNumber } from 'lib/format';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import styles from './ListTable.module.css';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
const ITEM_SIZE = 30;
|
||||
|
||||
export interface ListTableProps {
|
||||
data?: any[];
|
||||
title?: string;
|
||||
metric?: string;
|
||||
className?: string;
|
||||
renderLabel?: (row: any) => ReactNode;
|
||||
animate?: boolean;
|
||||
virtualize?: boolean;
|
||||
showPercentage?: boolean;
|
||||
itemCount?: number;
|
||||
}
|
||||
|
||||
export function ListTable({
|
||||
data = [],
|
||||
title,
|
||||
|
|
@ -18,7 +31,7 @@ export function ListTable({
|
|||
virtualize = false,
|
||||
showPercentage = true,
|
||||
itemCount = 10,
|
||||
}) {
|
||||
}: ListTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const getRow = row => {
|
||||
|
|
@ -76,7 +89,7 @@ const AnimatedRow = ({ label, value = 0, percent, animate, showPercentage = true
|
|||
<div className={styles.row}>
|
||||
<div className={styles.label}>{label}</div>
|
||||
<div className={styles.value}>
|
||||
<animated.div className={styles.value} title={props?.y}>
|
||||
<animated.div className={styles.value} title={props?.y as any}>
|
||||
{props.y?.to(formatLongNumber)}
|
||||
</animated.div>
|
||||
</div>
|
||||
|
|
@ -3,6 +3,16 @@ import { useSpring, animated } from '@react-spring/web';
|
|||
import { formatNumber } from 'lib/format';
|
||||
import styles from './MetricCard.module.css';
|
||||
|
||||
export interface MetricCardProps {
|
||||
value: number;
|
||||
change?: number;
|
||||
label: string;
|
||||
reverseColors?: boolean;
|
||||
format?: typeof formatNumber;
|
||||
hideComparison?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const MetricCard = ({
|
||||
value = 0,
|
||||
change = 0,
|
||||
|
|
@ -11,13 +21,13 @@ export const MetricCard = ({
|
|||
format = formatNumber,
|
||||
hideComparison = false,
|
||||
className,
|
||||
}) => {
|
||||
}: MetricCardProps) => {
|
||||
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
|
||||
const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } });
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.card, className)}>
|
||||
<animated.div className={styles.value} title={props?.x}>
|
||||
<animated.div className={styles.value} title={props?.x as any}>
|
||||
{props?.x?.to(x => format(x))}
|
||||
</animated.div>
|
||||
<div className={styles.label}>
|
||||
|
|
@ -29,7 +39,7 @@ export const MetricCard = ({
|
|||
[styles.negative]: change * (reverseColors ? -1 : 1) < 0,
|
||||
[styles.plusSign]: change > 0,
|
||||
})}
|
||||
title={changeProps?.x}
|
||||
title={changeProps?.x as any}
|
||||
>
|
||||
{changeProps?.x?.to(x => format(x))}
|
||||
</animated.span>
|
||||
|
|
@ -1,9 +1,17 @@
|
|||
import { ReactNode } from 'react';
|
||||
import { Loading, cloneChildren } from 'react-basics';
|
||||
import ErrorMessage from 'components/common/ErrorMessage';
|
||||
import { formatLongNumber } from 'lib/format';
|
||||
import styles from './MetricsBar.module.css';
|
||||
|
||||
export function MetricsBar({ children, isLoading, isFetched, error }) {
|
||||
export interface MetricsBarProps {
|
||||
isLoading?: boolean;
|
||||
isFetched?: boolean;
|
||||
error?: unknown;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function MetricsBar({ children, isLoading, isFetched, error }: MetricsBarProps) {
|
||||
const formatFunc = n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`);
|
||||
|
||||
return (
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Loading, Icon, Text } from 'react-basics';
|
||||
import firstBy from 'thenby';
|
||||
import classNames from 'classnames';
|
||||
import useApi from 'components/hooks/useApi';
|
||||
import { percentFilter } from 'lib/filters';
|
||||
|
|
@ -8,24 +7,33 @@ import useDateRange from 'components/hooks/useDateRange';
|
|||
import useNavigation from 'components/hooks/useNavigation';
|
||||
import ErrorMessage from 'components/common/ErrorMessage';
|
||||
import LinkButton from 'components/common/LinkButton';
|
||||
import ListTable from './ListTable';
|
||||
import ListTable, { ListTableProps } from './ListTable';
|
||||
import { DEFAULT_ANIMATION_DURATION } from 'lib/constants';
|
||||
import Icons from 'components/icons';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import useLocale from 'components/hooks/useLocale';
|
||||
import styles from './MetricsTable.module.css';
|
||||
|
||||
export interface MetricsTableProps extends ListTableProps {
|
||||
websiteId: string;
|
||||
type?: string;
|
||||
className?: string;
|
||||
dataFilter?: (data: any) => any;
|
||||
limit?: number;
|
||||
delay?: number;
|
||||
onDataLoad?: (data: any) => void;
|
||||
}
|
||||
|
||||
export function MetricsTable({
|
||||
websiteId,
|
||||
type,
|
||||
className,
|
||||
dataFilter,
|
||||
filterOptions,
|
||||
limit,
|
||||
onDataLoad,
|
||||
delay = null,
|
||||
...props
|
||||
}) {
|
||||
}: MetricsTableProps) {
|
||||
const [{ startDate, endDate, modified }] = useDateRange(websiteId);
|
||||
const {
|
||||
makeUrl,
|
||||
|
|
@ -53,26 +61,28 @@ export function MetricsTable({
|
|||
city,
|
||||
},
|
||||
],
|
||||
queryFn: () => {
|
||||
queryFn: async () => {
|
||||
const filters = { url, title, referrer, os, browser, device, country, region, city };
|
||||
|
||||
filters[type] = undefined;
|
||||
|
||||
onDataLoad?.();
|
||||
|
||||
return get(`/websites/${websiteId}/metrics`, {
|
||||
const data = await get(`/websites/${websiteId}/metrics`, {
|
||||
type,
|
||||
startAt: +startDate,
|
||||
endAt: +endDate,
|
||||
...filters,
|
||||
});
|
||||
|
||||
onDataLoad?.(data);
|
||||
|
||||
return data;
|
||||
},
|
||||
retryDelay: delay || DEFAULT_ANIMATION_DURATION,
|
||||
});
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
if (data) {
|
||||
let items = data;
|
||||
let items: any[] = data;
|
||||
|
||||
if (dataFilter) {
|
||||
if (Array.isArray(dataFilter)) {
|
||||
|
|
@ -89,20 +99,19 @@ export function MetricsTable({
|
|||
if (limit) {
|
||||
items = items.filter((e, i) => i < limit);
|
||||
}
|
||||
if (filterOptions?.sort === false) {
|
||||
return items;
|
||||
}
|
||||
|
||||
return items.sort(firstBy('y', -1).thenBy('x'));
|
||||
return items;
|
||||
}
|
||||
return [];
|
||||
}, [data, error, dataFilter, filterOptions, limit]);
|
||||
}, [data, error, dataFilter, limit]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.container, className)}>
|
||||
{!data && isLoading && !isFetched && <Loading icon="dots" />}
|
||||
{error && <ErrorMessage />}
|
||||
{data && !error && <ListTable {...props} data={filteredData} className={className} />}
|
||||
{data && !error && (
|
||||
<ListTable {...(props as ListTableProps)} data={filteredData} className={className} />
|
||||
)}
|
||||
<div className={styles.footer}>
|
||||
{data && !error && limit && (
|
||||
<LinkButton href={makeUrl({ view: type })} variant="quiet">
|
||||
|
|
@ -2,7 +2,7 @@ import MetricsTable from './MetricsTable';
|
|||
import FilterLink from 'components/common/FilterLink';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
|
||||
export function OSTable({ websiteId, ...props }) {
|
||||
export function OSTable({ websiteId, limit }: { websiteId: string; limit?: number }) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
function renderLink({ x: os }) {
|
||||
|
|
@ -10,7 +10,7 @@ export function OSTable({ websiteId, ...props }) {
|
|||
<FilterLink id="os" value={os}>
|
||||
<img
|
||||
src={`${process.env.basePath}/images/os/${
|
||||
os?.toLowerCase().replaceAll(/[^\w]+/g, '-') || 'unknown'
|
||||
os?.toLowerCase().replaceAll(/\W/g, '-') || 'unknown'
|
||||
}.png`}
|
||||
alt={os}
|
||||
width={16}
|
||||
|
|
@ -22,8 +22,8 @@ export function OSTable({ websiteId, ...props }) {
|
|||
|
||||
return (
|
||||
<MetricsTable
|
||||
{...props}
|
||||
websiteId={websiteId}
|
||||
limit={limit}
|
||||
title={formatMessage(labels.os)}
|
||||
metric={formatMessage(labels.visitors)}
|
||||
renderLabel={renderLink}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
import FilterLink from 'components/common/FilterLink';
|
||||
import FilterButtons from 'components/common/FilterButtons';
|
||||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import useNavigation from 'components/hooks/useNavigation';
|
||||
import { emptyFilter } from 'lib/filters';
|
||||
|
||||
export function PagesTable({ websiteId, showFilters, ...props }) {
|
||||
export interface PagesTableProps extends MetricsTableProps {
|
||||
showFilters?: boolean;
|
||||
}
|
||||
|
||||
export function PagesTable({ showFilters, ...props }: PagesTableProps) {
|
||||
const {
|
||||
router,
|
||||
makeUrl,
|
||||
|
|
@ -40,7 +44,6 @@ export function PagesTable({ websiteId, showFilters, ...props }) {
|
|||
title={formatMessage(labels.pages)}
|
||||
type={view}
|
||||
metric={formatMessage(labels.views)}
|
||||
websiteId={websiteId}
|
||||
dataFilter={emptyFilter}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
|
|
@ -1,9 +1,18 @@
|
|||
import { useMemo } from 'react';
|
||||
import BarChart from './BarChart';
|
||||
import BarChart, { BarChartProps } from './BarChart';
|
||||
import { useLocale, useTheme, useMessages } from 'components/hooks';
|
||||
import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts';
|
||||
|
||||
export function PageviewsChart({ websiteId, data, unit, loading, ...props }) {
|
||||
export interface PageviewsChartProps extends BarChartProps {
|
||||
data: {
|
||||
sessions: any[];
|
||||
pageviews: any[];
|
||||
};
|
||||
unit: string;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
export function PageviewsChart({ data, unit, isLoading, ...props }: PageviewsChartProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { colors } = useTheme();
|
||||
const { locale } = useLocale();
|
||||
|
|
@ -30,10 +39,9 @@ export function PageviewsChart({ websiteId, data, unit, loading, ...props }) {
|
|||
return (
|
||||
<BarChart
|
||||
{...props}
|
||||
key={websiteId}
|
||||
datasets={datasets}
|
||||
unit={unit}
|
||||
loading={loading}
|
||||
isLoading={isLoading}
|
||||
renderXLabel={renderDateLabels(unit, locale)}
|
||||
renderTooltipPopup={renderStatusTooltipPopup(unit, locale)}
|
||||
/>
|
||||
|
|
@ -3,7 +3,7 @@ import { safeDecodeURI } from 'next-basics';
|
|||
import FilterButtons from 'components/common/FilterButtons';
|
||||
import { emptyFilter, paramFilter } from 'lib/filters';
|
||||
import { FILTER_RAW, FILTER_COMBINED } from 'lib/constants';
|
||||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import styles from './QueryParametersTable.module.css';
|
||||
|
||||
|
|
@ -12,7 +12,10 @@ const filters = {
|
|||
[FILTER_COMBINED]: [emptyFilter, paramFilter],
|
||||
};
|
||||
|
||||
export function QueryParametersTable({ websiteId, showFilters, ...props }) {
|
||||
export function QueryParametersTable({
|
||||
showFilters,
|
||||
...props
|
||||
}: { showFilters: boolean } & MetricsTableProps) {
|
||||
const [filter, setFilter] = useState(FILTER_COMBINED);
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
|
|
@ -32,7 +35,6 @@ export function QueryParametersTable({ websiteId, showFilters, ...props }) {
|
|||
title={formatMessage(labels.query)}
|
||||
type="query"
|
||||
metric={formatMessage(labels.views)}
|
||||
websiteId={websiteId}
|
||||
dataFilter={filters[filter]}
|
||||
renderLabel={({ x, p, v }) =>
|
||||
filter === FILTER_RAW ? (
|
||||
|
|
@ -4,7 +4,7 @@ import PageviewsChart from './PageviewsChart';
|
|||
import { getDateArray } from 'lib/date';
|
||||
import { DEFAULT_ANIMATION_DURATION, REALTIME_RANGE } from 'lib/constants';
|
||||
|
||||
function mapData(data) {
|
||||
function mapData(data: any[]) {
|
||||
let last = 0;
|
||||
const arr = [];
|
||||
|
||||
|
|
@ -23,7 +23,15 @@ function mapData(data) {
|
|||
return arr;
|
||||
}
|
||||
|
||||
export function RealtimeChart({ data, unit, ...props }) {
|
||||
export interface RealtimeChartProps {
|
||||
data: {
|
||||
pageviews: any[];
|
||||
visitors: any[];
|
||||
};
|
||||
unit: string;
|
||||
}
|
||||
|
||||
export function RealtimeChart({ data, unit, ...props }: RealtimeChartProps) {
|
||||
const endDate = startOfMinute(new Date());
|
||||
const startDate = subMinutes(endDate, REALTIME_RANGE);
|
||||
const prevEndDate = useRef(endDate);
|
||||
|
|
@ -49,13 +57,7 @@ export function RealtimeChart({ data, unit, ...props }) {
|
|||
}, [endDate]);
|
||||
|
||||
return (
|
||||
<PageviewsChart
|
||||
{...props}
|
||||
height={200}
|
||||
unit={unit}
|
||||
data={chartData}
|
||||
animationDuration={animationDuration}
|
||||
/>
|
||||
<PageviewsChart {...props} unit={unit} data={chartData} animationDuration={animationDuration} />
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import FilterLink from 'components/common/FilterLink';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
|
||||
export function ReferrersTable({ websiteId, ...props }) {
|
||||
export function ReferrersTable(props: MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
const renderLink = ({ x: referrer }) => {
|
||||
|
|
@ -23,7 +23,6 @@ export function ReferrersTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.referrers)}
|
||||
type="referrer"
|
||||
metric={formatMessage(labels.views)}
|
||||
websiteId={websiteId}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
</>
|
||||
|
|
@ -3,15 +3,15 @@ import { emptyFilter } from 'lib/filters';
|
|||
import useLocale from 'components/hooks/useLocale';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
import useCountryNames from 'components/hooks/useCountryNames';
|
||||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import regions from 'public/iso-3166-2.json';
|
||||
|
||||
export function RegionsTable({ websiteId, ...props }) {
|
||||
export function RegionsTable(props: MetricsTableProps) {
|
||||
const { locale } = useLocale();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const countryNames = useCountryNames(locale);
|
||||
|
||||
const renderLabel = (code, country) => {
|
||||
const renderLabel = (code: string, country: string) => {
|
||||
const region = code.includes('-') ? code : `${country}-${code}`;
|
||||
return regions[region] ? `${regions[region]}, ${countryNames[country]}` : region;
|
||||
};
|
||||
|
|
@ -33,7 +33,6 @@ export function RegionsTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.regions)}
|
||||
type="region"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
dataFilter={emptyFilter}
|
||||
renderLabel={renderLink}
|
||||
/>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import MetricsTable from './MetricsTable';
|
||||
import MetricsTable, { MetricsTableProps } from './MetricsTable';
|
||||
import useMessages from 'components/hooks/useMessages';
|
||||
|
||||
export function ScreenTable({ websiteId, ...props }) {
|
||||
export function ScreenTable(props: MetricsTableProps) {
|
||||
const { formatMessage, labels } = useMessages();
|
||||
|
||||
return (
|
||||
|
|
@ -10,7 +10,6 @@ export function ScreenTable({ websiteId, ...props }) {
|
|||
title={formatMessage(labels.screens)}
|
||||
type="screen"
|
||||
metric={formatMessage(labels.visitors)}
|
||||
websiteId={websiteId}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import { formatLongNumber } from 'lib/format';
|
|||
import { percentFilter } from 'lib/filters';
|
||||
import styles from './WorldMap.module.css';
|
||||
|
||||
export function WorldMap({ data, className }) {
|
||||
export function WorldMap({ data = [], className }: { data?: any[]; className?: string }) {
|
||||
const [tooltip, setTooltipPopup] = useState();
|
||||
const { theme, colors } = useTheme();
|
||||
const { locale } = useLocale();
|
||||
|
|
@ -21,7 +21,7 @@ export function WorldMap({ data, className }) {
|
|||
const visitorsLabel = formatMessage(labels.visitors).toLocaleLowerCase(locale);
|
||||
const metrics = useMemo(() => (data ? percentFilter(data) : []), [data]);
|
||||
|
||||
function getFillColor(code) {
|
||||
function getFillColor(code: string) {
|
||||
if (code === 'AQ') return;
|
||||
const country = metrics?.find(({ x }) => x === code);
|
||||
|
||||
|
|
@ -41,7 +41,9 @@ export function WorldMap({ data, className }) {
|
|||
function handleHover(code) {
|
||||
if (code === 'AQ') return;
|
||||
const country = metrics?.find(({ x }) => x === code);
|
||||
setTooltipPopup(`${countryNames[code]}: ${formatLongNumber(country?.y || 0)} ${visitorsLabel}`);
|
||||
setTooltipPopup(
|
||||
`${countryNames[code]}: ${formatLongNumber(country?.y || 0)} ${visitorsLabel}` as any,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
Loading…
Add table
Add a link
Reference in a new issue