Show percentages in metrics bar.

This commit is contained in:
Mike Cao 2024-05-26 21:30:03 -07:00
parent b7a7d4de4d
commit 8b304b7ca2
3 changed files with 24 additions and 17 deletions

View File

@ -3,7 +3,7 @@ import { useMessages, useSticky } from 'components/hooks';
import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; import WebsiteDateFilter from 'components/input/WebsiteDateFilter';
import MetricCard from 'components/metrics/MetricCard'; import MetricCard from 'components/metrics/MetricCard';
import MetricsBar from 'components/metrics/MetricsBar'; import MetricsBar from 'components/metrics/MetricsBar';
import { formatShortTime } from 'lib/format'; import { formatShortTime, formatLongNumber } from 'lib/format';
import WebsiteFilterButton from './WebsiteFilterButton'; import WebsiteFilterButton from './WebsiteFilterButton';
import useWebsiteStats from 'components/hooks/queries/useWebsiteStats'; import useWebsiteStats from 'components/hooks/queries/useWebsiteStats';
import styles from './WebsiteMetricsBar.module.css'; import styles from './WebsiteMetricsBar.module.css';
@ -37,16 +37,19 @@ export function WebsiteMetricsBar({
...pageviews, ...pageviews,
label: formatMessage(labels.views), label: formatMessage(labels.views),
change: pageviews.value - pageviews.prev, change: pageviews.value - pageviews.prev,
formatValue: formatLongNumber,
}, },
{ {
...visits, ...visits,
label: formatMessage(labels.visits), label: formatMessage(labels.visits),
change: visits.value - visits.prev, change: visits.value - visits.prev,
formatValue: formatLongNumber,
}, },
{ {
...visitors, ...visitors,
label: formatMessage(labels.visitors), label: formatMessage(labels.visitors),
change: visitors.value - visitors.prev, change: visitors.value - visitors.prev,
formatValue: formatLongNumber,
}, },
{ {
label: formatMessage(labels.bounceRate), label: formatMessage(labels.bounceRate),
@ -55,7 +58,7 @@ export function WebsiteMetricsBar({
change: change:
(Math.min(visitors.value, bounces.value) / visitors.value) * 100 - (Math.min(visitors.value, bounces.value) / visitors.value) * 100 -
(Math.min(visitors.prev, bounces.prev) / visitors.prev) * 100, (Math.min(visitors.prev, bounces.prev) / visitors.prev) * 100,
format: n => Number(n).toFixed(0) + '%', formatValue: n => Number(n).toFixed(0) + '%',
reverseColors: true, reverseColors: true,
}, },
{ {
@ -63,7 +66,8 @@ export function WebsiteMetricsBar({
value: totaltime.value / visits.value, value: totaltime.value / visits.value,
prev: totaltime.prev / visits.prev, prev: totaltime.prev / visits.prev,
change: totaltime.value / visits.value - totaltime.prev / visits.prev, change: totaltime.value / visits.value - totaltime.prev / visits.prev,
format: n => `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`, formatValue: n =>
`${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`,
}, },
] ]
: []; : [];
@ -78,12 +82,12 @@ export function WebsiteMetricsBar({
ref={ref} ref={ref}
className={classNames(styles.container, { className={classNames(styles.container, {
[styles.sticky]: sticky, [styles.sticky]: sticky,
[styles.isSticky]: isSticky, [styles.isSticky]: sticky && isSticky,
})} })}
> >
<div> <div>
<MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}> <MetricsBar isLoading={isLoading} isFetched={isFetched} error={error}>
{metrics.map(({ label, value, prev, change, format, reverseColors }) => { {metrics.map(({ label, value, prev, change, formatValue, reverseColors }) => {
return ( return (
<MetricCard <MetricCard
key={label} key={label}
@ -91,7 +95,7 @@ export function WebsiteMetricsBar({
previousValue={prev} previousValue={prev}
label={label} label={label}
change={change} change={change}
format={format} formatValue={formatValue}
reverseColors={reverseColors} reverseColors={reverseColors}
showChange={compareMode || showChange} showChange={compareMode || showChange}
showPrevious={compareMode} showPrevious={compareMode}

View File

@ -10,7 +10,7 @@ export interface MetricCardProps {
change?: number; change?: number;
label?: string; label?: string;
reverseColors?: boolean; reverseColors?: boolean;
format?: typeof formatNumber; formatValue?: typeof formatNumber;
showLabel?: boolean; showLabel?: boolean;
showChange?: boolean; showChange?: boolean;
showPrevious?: boolean; showPrevious?: boolean;
@ -22,32 +22,35 @@ export const MetricCard = ({
change = 0, change = 0,
label, label,
reverseColors = false, reverseColors = false,
format = formatNumber, formatValue = formatNumber,
showLabel = true, showLabel = true,
showChange = false, showChange = false,
showPrevious = false, showPrevious = false,
className, className,
}: MetricCardProps) => { }: MetricCardProps) => {
const diff = value - change;
const pct = ((value - diff) / diff) * 100;
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } }); const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } }); const changeProps = useSpring({ x: Number(pct) || 0, from: { x: 0 } });
const prevProps = useSpring({ x: Number(value - change) || 0, from: { x: 0 } }); const prevProps = useSpring({ x: Number(diff) || 0, from: { x: 0 } });
return ( return (
<div className={classNames(styles.card, className, showPrevious && styles.compare)}> <div className={classNames(styles.card, className, showPrevious && styles.compare)}>
{showLabel && <div className={styles.label}>{label}</div>} {showLabel && <div className={styles.label}>{label}</div>}
<animated.div className={styles.value} title={value.toString()}> <animated.div className={styles.value} title={formatValue(value)}>
{props?.x?.to(x => format(x))} {props?.x?.to(x => formatValue(x))}
</animated.div> </animated.div>
{showChange && ( {showChange && (
<ChangeLabel className={styles.change} value={change} reverseColors={reverseColors}> <ChangeLabel className={styles.change} value={change} reverseColors={reverseColors}>
<animated.span title={change.toString()}> <animated.span title={formatValue(change)}>
{changeProps?.x?.to(x => format(Math.abs(x)))} {changeProps?.x?.to(x => Math.abs(~~x))}
</animated.span> </animated.span>
%
</ChangeLabel> </ChangeLabel>
)} )}
{showPrevious && ( {showPrevious && (
<animated.div className={classNames(styles.value, styles.prev)} title={prevProps?.x as any}> <animated.div className={classNames(styles.value, styles.prev)} title={formatValue(diff)}>
{prevProps?.x?.to(x => format(x))} {prevProps?.x?.to(x => formatValue(x))}
</animated.div> </animated.div>
)} )}
</div> </div>

View File

@ -46,7 +46,7 @@ export function PageviewsChart({ data, unit, isLoading, ...props }: PageviewsCha
? [ ? [
{ {
type: 'line', type: 'line',
label: `${formatMessage(labels.visits)} (${formatMessage(labels.previous)})`, label: `${formatMessage(labels.views)} (${formatMessage(labels.previous)})`,
data: data.compare.pageviews, data: data.compare.pageviews,
borderWidth: 2, borderWidth: 2,
backgroundColor: '#8601B0', backgroundColor: '#8601B0',