diff --git a/components/metrics/MetricCard.js b/components/metrics/MetricCard.js
index 6209509c..d0394b97 100644
--- a/components/metrics/MetricCard.js
+++ b/components/metrics/MetricCard.js
@@ -3,13 +3,38 @@ import { useSpring, animated } from 'react-spring';
import { formatNumber } from '../../lib/format';
import styles from './MetricCard.module.css';
-const MetricCard = ({ value = 0, label, format = formatNumber }) => {
+const MetricCard = ({
+ value = 0,
+ change = 0,
+ label,
+ reverseColors = false,
+ format = formatNumber,
+}) => {
const props = useSpring({ x: Number(value) || 0, from: { x: 0 } });
+ const changeProps = useSpring({ x: Number(change) || 0, from: { x: 0 } });
return (
{props.x.interpolate(x => format(x))}
-
{label}
+
+ {label}
+ {~~change === 0 &&
{format(0)}}
+ {~~change !== 0 && (
+
= 0
+ ? !reverseColors
+ ? styles.positive
+ : styles.negative
+ : !reverseColors
+ ? styles.negative
+ : styles.positive
+ }`}
+ >
+ {changeProps.x.interpolate(x => `${change >= 0 ? '+' : ''}${format(x)}`)}
+
+ )}
+
);
};
diff --git a/components/metrics/MetricCard.module.css b/components/metrics/MetricCard.module.css
index 50b92ac8..76a69609 100644
--- a/components/metrics/MetricCard.module.css
+++ b/components/metrics/MetricCard.module.css
@@ -16,4 +16,24 @@
.label {
font-size: var(--font-size-normal);
white-space: nowrap;
+ display: flex;
+ align-items: center;
+ gap: 5px;
+}
+
+.change {
+ font-size: 12px;
+ padding: 0 5px;
+ border-radius: 5px;
+ margin-left: 4px;
+ border: 1px solid var(--gray200);
+ color: var(--gray500);
+}
+
+.change.positive {
+ color: var(--green500);
+}
+
+.change.negative {
+ color: var(--red500);
}
diff --git a/components/metrics/MetricsBar.js b/components/metrics/MetricsBar.js
index 945cd5e2..435870a1 100644
--- a/components/metrics/MetricsBar.js
+++ b/components/metrics/MetricsBar.js
@@ -34,14 +34,22 @@ export default function MetricsBar({ websiteId, className }) {
[url, modified],
);
- const formatFunc = format ? formatLongNumber : formatNumber;
+ const formatFunc = format
+ ? n => (n >= 0 ? formatLongNumber(n) : `-${formatLongNumber(Math.abs(n))}`)
+ : formatNumber;
function handleSetFormat() {
setFormat(state => !state);
}
const { pageviews, uniques, bounces, totaltime } = data || {};
- const num = Math.min(uniques, bounces);
+ const num = Math.min(data && uniques.value, data && bounces.value);
+ const diffs = data && {
+ pageviews: pageviews.value - pageviews.change,
+ uniques: uniques.value - uniques.change,
+ bounces: bounces.value - bounces.change,
+ totaltime: totaltime.value - totaltime.change,
+ };
return (
@@ -51,18 +59,27 @@ export default function MetricsBar({ websiteId, className }) {
<>
}
- value={pageviews}
+ value={pageviews.value}
+ change={pageviews.change}
format={formatFunc}
/>
}
- value={uniques}
+ value={uniques.value}
+ change={uniques.change}
format={formatFunc}
/>
}
- value={uniques ? (num / uniques) * 100 : 0}
+ value={uniques.value ? (num / uniques.value) * 100 : 0}
+ change={
+ uniques.value && uniques.change
+ ? (num / uniques.value) * 100 -
+ (Math.min(diffs.uniques, diffs.bounces) / diffs.uniques) * 100 || 0
+ : 0
+ }
format={n => Number(n).toFixed(0) + '%'}
+ reverseColors
/>
}
- value={totaltime && pageviews ? totaltime / (pageviews - bounces) : 0}
- format={n => formatShortTime(n, ['m', 's'], ' ')}
+ value={
+ totaltime.value && pageviews.value
+ ? totaltime.value / (pageviews.value - bounces.value)
+ : 0
+ }
+ change={
+ totaltime.value && pageviews.value
+ ? (diffs.totaltime / (diffs.pageviews - diffs.bounces) -
+ totaltime.value / (pageviews.value - bounces.value)) *
+ -1 || 0
+ : 0
+ }
+ format={n => `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`}
/>
>
)}
diff --git a/pages/api/website/[id]/stats.js b/pages/api/website/[id]/stats.js
index 8b80a363..80dd22ec 100644
--- a/pages/api/website/[id]/stats.js
+++ b/pages/api/website/[id]/stats.js
@@ -14,10 +14,18 @@ export default async (req, res) => {
const startDate = new Date(+start_at);
const endDate = new Date(+end_at);
+ const distance = end_at - start_at;
+ const prevStartDate = new Date(+start_at - distance);
+ const prevEndDate = new Date(+end_at - distance);
+
const metrics = await getWebsiteStats(websiteId, startDate, endDate, { url });
+ const prevPeriod = await getWebsiteStats(websiteId, prevStartDate, prevEndDate, { url });
const stats = Object.keys(metrics[0]).reduce((obj, key) => {
- obj[key] = Number(metrics[0][key]) || 0;
+ obj[key] = {
+ value: Number(metrics[0][key]) || 0,
+ change: Number(metrics[0][key] - prevPeriod[0][key]) || 0,
+ };
return obj;
}, {});