mirror of
https://github.com/umami-software/umami.git
synced 2026-02-06 21:57:16 +01:00
Updated metrics components for compare mode.
This commit is contained in:
parent
6b03935fca
commit
df66acaacf
117 changed files with 602 additions and 513 deletions
|
|
@ -1,13 +1,17 @@
|
|||
import { useApi } from './useApi';
|
||||
import { useFilterParams } from '../useFilterParams';
|
||||
|
||||
export function useWebsiteStats(websiteId: string, options?: { [key: string]: string }) {
|
||||
export function useWebsiteStats(
|
||||
websiteId: string,
|
||||
compare?: string,
|
||||
options?: { [key: string]: string },
|
||||
) {
|
||||
const { get, useQuery } = useApi();
|
||||
const params = useFilterParams(websiteId);
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['websites:stats', { websiteId, ...params }],
|
||||
queryFn: () => get(`/websites/${websiteId}/stats`, params),
|
||||
queryKey: ['websites:stats', { websiteId, ...params, compare }],
|
||||
queryFn: () => get(`/websites/${websiteId}/stats`, { ...params, compare }),
|
||||
enabled: !!websiteId,
|
||||
...options,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ export const labels = defineMessages({
|
|||
uniqueVisitors: { id: 'label.unique-visitors', defaultMessage: 'Unique visitors' },
|
||||
bounceRate: { id: 'label.bounce-rate', defaultMessage: 'Bounce rate' },
|
||||
viewsPerVisit: { id: 'label.views-per-visit', defaultMessage: 'Views per visit' },
|
||||
averageVisitTime: { id: 'label.average-visit-time', defaultMessage: 'Average visit time' },
|
||||
visitDuration: { id: 'label.visit-duration', defaultMessage: 'Visit duration' },
|
||||
desktop: { id: 'label.desktop', defaultMessage: 'Desktop' },
|
||||
laptop: { id: 'label.laptop', defaultMessage: 'Laptop' },
|
||||
tablet: { id: 'label.tablet', defaultMessage: 'Tablet' },
|
||||
|
|
@ -253,6 +253,8 @@ export const labels = defineMessages({
|
|||
defaultMessage: 'Understand how users nagivate through your website.',
|
||||
},
|
||||
compare: { id: 'label.compare', defaultMessage: 'Compare' },
|
||||
previousPeriod: { id: 'label.previous-period', defaultMessage: 'Previous period' },
|
||||
yearOverYear: { id: 'label.year-over-year', defaultMessage: 'Year over year' },
|
||||
});
|
||||
|
||||
export const messages = defineMessages({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,16 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-width: 140px;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.card.compare {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.card.compare .change {
|
||||
font-size: 16px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.card:first-child {
|
||||
|
|
@ -14,30 +23,33 @@
|
|||
}
|
||||
|
||||
.value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 36px;
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
min-height: 60px;
|
||||
color: var(--base900);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.value.prev {
|
||||
color: var(--base800);
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 700;
|
||||
gap: 10px;
|
||||
white-space: nowrap;
|
||||
min-height: 30px;
|
||||
color: var(--base800);
|
||||
}
|
||||
|
||||
.change {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
color: var(--base500);
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.change.positive {
|
||||
|
|
@ -49,7 +61,3 @@
|
|||
color: var(--red700);
|
||||
background: var(--red100);
|
||||
}
|
||||
|
||||
.change.plusSign::before {
|
||||
content: '+';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
import classNames from 'classnames';
|
||||
import { Icon, Icons } from 'react-basics';
|
||||
import { useSpring, animated } from '@react-spring/web';
|
||||
import { formatNumber } from 'lib/format';
|
||||
import styles from './MetricCard.module.css';
|
||||
|
||||
export interface MetricCardProps {
|
||||
value: number;
|
||||
previousValue?: number;
|
||||
change?: number;
|
||||
label: string;
|
||||
label?: string;
|
||||
reverseColors?: boolean;
|
||||
format?: typeof formatNumber;
|
||||
hideComparison?: boolean;
|
||||
showLabel?: boolean;
|
||||
showChange?: boolean;
|
||||
showPrevious?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
|
@ -19,32 +23,43 @@ export const MetricCard = ({
|
|||
label,
|
||||
reverseColors = false,
|
||||
format = formatNumber,
|
||||
hideComparison = false,
|
||||
showLabel = true,
|
||||
showChange = true,
|
||||
showPrevious = false,
|
||||
className,
|
||||
}: MetricCardProps) => {
|
||||
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
|
||||
const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } });
|
||||
const prevProps = useSpring({ x: Number(value - change) || 0, from: { x: 0 } });
|
||||
const positive = change * (reverseColors ? -1 : 1) >= 0;
|
||||
const negative = change * (reverseColors ? -1 : 1) < 0;
|
||||
|
||||
return (
|
||||
<div className={classNames(styles.card, className)}>
|
||||
<div className={styles.label}>
|
||||
{label}
|
||||
{~~change !== 0 && !hideComparison && (
|
||||
<animated.span
|
||||
className={classNames(styles.change, {
|
||||
[styles.positive]: change * (reverseColors ? -1 : 1) >= 0,
|
||||
[styles.negative]: change * (reverseColors ? -1 : 1) < 0,
|
||||
[styles.plusSign]: change > 0,
|
||||
})}
|
||||
title={changeProps?.x as any}
|
||||
>
|
||||
{changeProps?.x?.to(x => format(x))}
|
||||
</animated.span>
|
||||
)}
|
||||
</div>
|
||||
<div className={classNames(styles.card, className, showPrevious && styles.compare)}>
|
||||
{showLabel && <div className={styles.label}>{label}</div>}
|
||||
<animated.div className={styles.value} title={props?.x as any}>
|
||||
{props?.x?.to(x => format(x))}
|
||||
</animated.div>
|
||||
{showChange && (
|
||||
<div
|
||||
className={classNames(styles.change, {
|
||||
[styles.positive]: positive,
|
||||
[styles.negative]: negative,
|
||||
})}
|
||||
>
|
||||
<Icon rotate={positive ? -45 : 45} size={showPrevious ? 'sm' : 'xs'}>
|
||||
<Icons.ArrowRight />
|
||||
</Icon>
|
||||
<animated.span title={changeProps?.x as any}>
|
||||
{changeProps?.x?.to(x => format(Math.abs(x)))}
|
||||
</animated.span>
|
||||
</div>
|
||||
)}
|
||||
{showPrevious && (
|
||||
<animated.div className={classNames(styles.value, styles.prev)} title={prevProps?.x as any}>
|
||||
{prevProps?.x?.to(x => format(x))}
|
||||
</animated.div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue