mirror of
https://github.com/umami-software/umami.git
synced 2025-12-08 05:12:36 +01:00
Display page views and unique visitors.
This commit is contained in:
parent
bdcdcd9d13
commit
ce92c7897d
16 changed files with 162 additions and 44 deletions
|
|
@ -15,7 +15,9 @@ export default function DateFilter({ onChange }) {
|
|||
return (
|
||||
<select value={selected} onChange={handleChange}>
|
||||
{filterOptions.map(option => (
|
||||
<option name={option}>{option}</option>
|
||||
<option key={option} name={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
);
|
||||
|
|
|
|||
11
components/MetricCard.js
Normal file
11
components/MetricCard.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import styles from './MetricCard.module.css';
|
||||
|
||||
const MetricCard = ({ value, label }) => (
|
||||
<div className={styles.card}>
|
||||
<div className={styles.value}>{value}</div>
|
||||
<div className={styles.label}>{label}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default MetricCard;
|
||||
16
components/MetricCard.module.css
Normal file
16
components/MetricCard.module.css
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-right: 50px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 36px;
|
||||
line-height: 40px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
|
@ -1,16 +1,9 @@
|
|||
import React, { useRef, useEffect, useMemo } from 'react';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import ChartJS from 'chart.js';
|
||||
import { getLocalTime } from 'lib/date';
|
||||
|
||||
export default function PageviewsChart({ data }) {
|
||||
const canvas = useRef();
|
||||
const chart = useRef();
|
||||
const pageviews = useMemo(() => {
|
||||
if (data) {
|
||||
return data.pageviews.map(({ t, y }) => ({ t: getLocalTime(t), y }));
|
||||
}
|
||||
return [];
|
||||
}, [data]);
|
||||
|
||||
function draw() {
|
||||
if (!canvas.current) return;
|
||||
|
|
@ -21,11 +14,19 @@ export default function PageviewsChart({ data }) {
|
|||
data: {
|
||||
datasets: [
|
||||
{
|
||||
label: 'page views',
|
||||
data: pageviews,
|
||||
label: 'unique visitors',
|
||||
data: data.uniques,
|
||||
lineTension: 0,
|
||||
backgroundColor: 'rgb(38, 128, 235, 0.1)',
|
||||
borderColor: 'rgb(13, 102, 208, 0.2)',
|
||||
backgroundColor: 'rgb(146, 86, 217, 0.2)',
|
||||
borderColor: 'rgb(122, 66, 191, 0.3)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
{
|
||||
label: 'page views',
|
||||
data: data.pageviews,
|
||||
lineTension: 0,
|
||||
backgroundColor: 'rgb(38, 128, 235, 0.2)',
|
||||
borderColor: 'rgb(13, 102, 208, 0.3)',
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
|
|
@ -52,6 +53,10 @@ export default function PageviewsChart({ data }) {
|
|||
},
|
||||
tooltipFormat: 'ddd M/DD hA',
|
||||
},
|
||||
gridLines: {
|
||||
display: false,
|
||||
},
|
||||
stacked: true,
|
||||
},
|
||||
],
|
||||
yAxes: [
|
||||
|
|
@ -59,13 +64,15 @@ export default function PageviewsChart({ data }) {
|
|||
ticks: {
|
||||
beginAtZero: true,
|
||||
},
|
||||
stacked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
chart.current.data.datasets[0].data = pageviews;
|
||||
chart.current.data.datasets[0].data = data.uniques;
|
||||
chart.current.data.datasets[1].data = data.pageviews;
|
||||
chart.current.update();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export default function WebsiteList() {
|
|||
<DateFilter onChange={handleDateChange} />
|
||||
{data &&
|
||||
data.websites.map(({ website_id, label }) => (
|
||||
<div>
|
||||
<div key={website_id}>
|
||||
<h2>{label}</h2>
|
||||
<WebsiteStats
|
||||
websiteId={website_id}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,20 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import PageviewsChart from './PageviewsChart';
|
||||
import { get } from 'lib/web';
|
||||
import { getTimezone } from 'lib/date';
|
||||
import { getDateArray, getTimezone } from 'lib/date';
|
||||
import WebsiteSummary from './WebsiteSummary';
|
||||
|
||||
export default function WebsiteStats({ websiteId, startDate, endDate, unit }) {
|
||||
const [data, setData] = useState();
|
||||
const [pageviews, uniques] = useMemo(() => {
|
||||
if (data) {
|
||||
return [
|
||||
getDateArray(data.pageviews, startDate, endDate, unit),
|
||||
getDateArray(data.uniques, startDate, endDate, unit),
|
||||
];
|
||||
}
|
||||
return [[], []];
|
||||
}, [data]);
|
||||
|
||||
async function loadData() {
|
||||
setData(
|
||||
|
|
@ -19,7 +29,12 @@ export default function WebsiteStats({ websiteId, startDate, endDate, unit }) {
|
|||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
}, [websiteId, startDate, endDate]);
|
||||
}, [websiteId, startDate, endDate, unit]);
|
||||
|
||||
return <PageviewsChart data={data} />;
|
||||
return (
|
||||
<div>
|
||||
<WebsiteSummary data={{ pageviews, uniques }} />
|
||||
<PageviewsChart data={{ pageviews, uniques }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
16
components/WebsiteSummary.js
Normal file
16
components/WebsiteSummary.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import MetricCard from './MetricCard';
|
||||
import styles from './WebsiteSummary.module.css';
|
||||
|
||||
function getTotal(data) {
|
||||
return data.reduce((n, v) => n + v.y, 0);
|
||||
}
|
||||
|
||||
export default function WebsiteSummary({ data }) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<MetricCard label="Views" value={getTotal(data.pageviews)} />
|
||||
<MetricCard label="Visitors" value={getTotal(data.uniques)} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
components/WebsiteSummary.module.css
Normal file
3
components/WebsiteSummary.module.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.container {
|
||||
display: flex;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue