diff --git a/components/metrics/BarChart.js b/components/metrics/BarChart.js
new file mode 100644
index 00000000..96063fd6
--- /dev/null
+++ b/components/metrics/BarChart.js
@@ -0,0 +1,150 @@
+import React, { useState, useRef, useEffect } from 'react';
+import ReactTooltip from 'react-tooltip';
+import classNames from 'classnames';
+import ChartJS from 'chart.js';
+import styles from './PageviewsChart.module.css';
+import { format } from 'date-fns';
+
+export default function BarChart({
+ chartId,
+ datasets,
+ unit,
+ records,
+ animationDuration = 300,
+ className,
+ onUpdate = () => {},
+}) {
+ const canvas = useRef();
+ const chart = useRef();
+ const [tooltip, setTooltip] = useState({});
+
+ const renderLabel = (label, index, values) => {
+ const d = new Date(values[index].value);
+ const n = records;
+
+ switch (unit) {
+ case 'hour':
+ return format(d, 'ha');
+ case 'day':
+ if (n >= 15) {
+ return index % ~~(n / 15) === 0 ? format(d, 'MMM d') : '';
+ }
+ return format(d, 'EEE M/d');
+ case 'month':
+ return format(d, 'MMMM');
+ default:
+ return label;
+ }
+ };
+
+ const renderTooltip = model => {
+ const { opacity, title, body, labelColors } = model;
+
+ if (!opacity) {
+ setTooltip(null);
+ } else {
+ const [label, value] = body[0].lines[0].split(':');
+
+ setTooltip({
+ title: title[0],
+ value,
+ label,
+ labelColor: labelColors[0].backgroundColor,
+ });
+ }
+ };
+
+ function draw() {
+ if (!chart.current) {
+ chart.current = new ChartJS(canvas.current, {
+ type: 'bar',
+ data: {
+ datasets,
+ },
+ options: {
+ animation: {
+ duration: animationDuration,
+ },
+ tooltips: {
+ enabled: false,
+ custom: renderTooltip,
+ },
+ hover: {
+ animationDuration: 0,
+ },
+ responsiveAnimationDuration: 0,
+ scales: {
+ xAxes: [
+ {
+ type: 'time',
+ distribution: 'series',
+ time: {
+ unit,
+ tooltipFormat: 'ddd MMMM DD YYYY',
+ },
+ ticks: {
+ callback: renderLabel,
+ minRotation: 0,
+ maxRotation: 0,
+ },
+ gridLines: {
+ display: false,
+ },
+ offset: true,
+ stacked: true,
+ },
+ ],
+ yAxes: [
+ {
+ ticks: {
+ beginAtZero: true,
+ },
+ },
+ ],
+ },
+ },
+ });
+ } else {
+ const { options } = chart.current;
+
+ options.scales.xAxes[0].time.unit = unit;
+ options.scales.xAxes[0].ticks.callback = renderLabel;
+
+ onUpdate(chart.current);
+ }
+ }
+
+ useEffect(() => {
+ if (datasets) {
+ draw();
+ setTooltip(null);
+ }
+ }, [datasets]);
+
+ return (
+
+
+
+ {tooltip ? : null}
+
+
+ );
+}
+
+const Tooltip = ({ title, value, label, labelColor }) => (
+
+);
diff --git a/components/metrics/EventsChart.js b/components/metrics/EventsChart.js
new file mode 100644
index 00000000..56a4a1a9
--- /dev/null
+++ b/components/metrics/EventsChart.js
@@ -0,0 +1,174 @@
+import React, { useState, useRef, useEffect, useCallback } from 'react';
+import ReactTooltip from 'react-tooltip';
+import classNames from 'classnames';
+import ChartJS from 'chart.js';
+import { format } from 'date-fns';
+import styles from './EventsChart.module.css';
+
+export default function EventsChart({
+ websiteId,
+ data,
+ unit,
+ animationDuration = 300,
+ className,
+ children,
+}) {
+ const canvas = useRef();
+ const chart = useRef();
+ const [tooltip, setTooltip] = useState({});
+
+ const renderLabel = useCallback(
+ (label, index, values) => {
+ const d = new Date(values[index].value);
+ const n = data.pageviews.length;
+
+ switch (unit) {
+ case 'day':
+ if (n >= 15) {
+ return index % ~~(n / 15) === 0 ? format(d, 'MMM d') : '';
+ }
+ return format(d, 'EEE M/d');
+ case 'month':
+ return format(d, 'MMMM');
+ default:
+ return label;
+ }
+ },
+ [unit, data],
+ );
+
+ const renderTooltip = model => {
+ const { opacity, title, body, labelColors } = model;
+
+ if (!opacity) {
+ setTooltip(null);
+ } else {
+ const [label, value] = body[0].lines[0].split(':');
+
+ setTooltip({
+ title: title[0],
+ value,
+ label,
+ labelColor: labelColors[0].backgroundColor,
+ });
+ }
+ };
+
+ function draw() {
+ if (!canvas.current) return;
+
+ if (!chart.current) {
+ chart.current = new ChartJS(canvas.current, {
+ type: 'bar',
+ data: {
+ datasets: [
+ {
+ label: 'unique visitors',
+ data: data.uniques,
+ lineTension: 0,
+ backgroundColor: 'rgb(38, 128, 235, 0.4)',
+ borderColor: 'rgb(13, 102, 208, 0.4)',
+ borderWidth: 1,
+ },
+ {
+ label: 'page views',
+ data: data.pageviews,
+ lineTension: 0,
+ backgroundColor: 'rgb(38, 128, 235, 0.2)',
+ borderColor: 'rgb(13, 102, 208, 0.2)',
+ borderWidth: 1,
+ },
+ ],
+ },
+ options: {
+ animation: {
+ duration: animationDuration,
+ },
+ tooltips: {
+ enabled: false,
+ custom: renderTooltip,
+ },
+ hover: {
+ animationDuration: 0,
+ },
+ scales: {
+ xAxes: [
+ {
+ type: 'time',
+ distribution: 'series',
+ time: {
+ unit,
+ tooltipFormat: 'ddd MMMM DD YYYY',
+ },
+ ticks: {
+ callback: renderLabel,
+ maxRotation: 0,
+ },
+ gridLines: {
+ display: false,
+ },
+ offset: true,
+ stacked: true,
+ },
+ ],
+ yAxes: [
+ {
+ ticks: {
+ beginAtZero: true,
+ },
+ },
+ ],
+ },
+ },
+ });
+ } else {
+ const {
+ data: { datasets },
+ options,
+ } = chart.current;
+
+ datasets[0].data = data.uniques;
+ datasets[1].data = data.pageviews;
+ options.scales.xAxes[0].time.unit = unit;
+ options.scales.xAxes[0].ticks.callback = renderLabel;
+ options.animation.duration = animationDuration;
+
+ chart.current.update();
+ }
+ }
+
+ useEffect(() => {
+ if (data) {
+ draw();
+ setTooltip(null);
+ }
+ }, [data]);
+
+ return (
+
+
+
+ {tooltip ? : null}
+
+ {children}
+
+ );
+}
+
+const Tooltip = ({ title, value, label, labelColor }) => (
+
+);
diff --git a/components/metrics/EventsChart.module.css b/components/metrics/EventsChart.module.css
new file mode 100644
index 00000000..d586bead
--- /dev/null
+++ b/components/metrics/EventsChart.module.css
@@ -0,0 +1,3 @@
+.chart {
+ display: flex;
+}
diff --git a/components/metrics/PageviewsChart.js b/components/metrics/PageviewsChart.js
index fe8c44e4..f174d604 100644
--- a/components/metrics/PageviewsChart.js
+++ b/components/metrics/PageviewsChart.js
@@ -1,174 +1,53 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
-import ReactTooltip from 'react-tooltip';
import classNames from 'classnames';
-import ChartJS from 'chart.js';
+import BarChart from './BarChart';
import { format } from 'date-fns';
import styles from './PageviewsChart.module.css';
-export default function PageviewsChart({
- websiteId,
- data,
- unit,
- animationDuration = 300,
- className,
- children,
-}) {
- const canvas = useRef();
- const chart = useRef();
- const [tooltip, setTooltip] = useState({});
+export default function PageviewsChart({ websiteId, data, unit, className, animationDuration }) {
+ const handleUpdate = chart => {
+ const {
+ data: { datasets },
+ options,
+ } = chart;
- const renderLabel = useCallback(
- (label, index, values) => {
- const d = new Date(values[index].value);
- const n = data.pageviews.length;
+ datasets[0].data = data.uniques;
+ datasets[1].data = data.pageviews;
+ options.animation.duration = animationDuration;
- switch (unit) {
- case 'day':
- if (n >= 15) {
- return index % ~~(n / 15) === 0 ? format(d, 'MMM d') : '';
- }
- return format(d, 'EEE M/d');
- case 'month':
- return format(d, 'MMMM');
- default:
- return label;
- }
- },
- [unit, data],
- );
-
- const renderTooltip = model => {
- const { opacity, title, body, labelColors } = model;
-
- if (!opacity) {
- setTooltip(null);
- } else {
- const [label, value] = body[0].lines[0].split(':');
-
- setTooltip({
- title: title[0],
- value,
- label,
- labelColor: labelColors[0].backgroundColor,
- });
- }
+ chart.update();
};
- function draw() {
- if (!canvas.current) return;
-
- if (!chart.current) {
- chart.current = new ChartJS(canvas.current, {
- type: 'bar',
- data: {
- datasets: [
- {
- label: 'unique visitors',
- data: data.uniques,
- lineTension: 0,
- backgroundColor: 'rgb(38, 128, 235, 0.4)',
- borderColor: 'rgb(13, 102, 208, 0.4)',
- borderWidth: 1,
- },
- {
- label: 'page views',
- data: data.pageviews,
- lineTension: 0,
- backgroundColor: 'rgb(38, 128, 235, 0.2)',
- borderColor: 'rgb(13, 102, 208, 0.2)',
- borderWidth: 1,
- },
- ],
- },
- options: {
- animation: {
- duration: animationDuration,
- },
- tooltips: {
- enabled: false,
- custom: renderTooltip,
- },
- hover: {
- animationDuration: 0,
- },
- scales: {
- xAxes: [
- {
- type: 'time',
- distribution: 'series',
- time: {
- unit,
- tooltipFormat: 'ddd MMMM DD YYYY',
- },
- ticks: {
- callback: renderLabel,
- maxRotation: 0,
- },
- gridLines: {
- display: false,
- },
- offset: true,
- stacked: true,
- },
- ],
- yAxes: [
- {
- ticks: {
- beginAtZero: true,
- },
- },
- ],
- },
- },
- });
- } else {
- const {
- data: { datasets },
- options,
- } = chart.current;
-
- datasets[0].data = data.uniques;
- datasets[1].data = data.pageviews;
- options.scales.xAxes[0].time.unit = unit;
- options.scales.xAxes[0].ticks.callback = renderLabel;
- options.animation.duration = animationDuration;
-
- chart.current.update();
- }
+ if (!data) {
+ return null;
}
- useEffect(() => {
- if (data) {
- draw();
- setTooltip(null);
- }
- }, [data]);
-
return (
-
-
-
- {tooltip ? : null}
-
- {children}
+
+
);
}
-
-const Tooltip = ({ title, value, label, labelColor }) => (
-
-);
diff --git a/components/metrics/WebsiteChart.js b/components/metrics/WebsiteChart.js
index 4f066808..f93a985a 100644
--- a/components/metrics/WebsiteChart.js
+++ b/components/metrics/WebsiteChart.js
@@ -76,14 +76,15 @@ export default function WebsiteChart({
{visible => (
-
+ <>
+
-
+ >
)}