diff --git a/README.md b/README.md index 3d08a1fd..18ec0a38 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,18 @@ # umami +Umami is a simple, fast, website analytics alternative to Google Analytics. + +## Getting started + +A detailed getting started guide can be found at [https://umami.is/docs/](https://umami.is/docs/) + ## Installation +### Requirements + +- A server with Node.js 10.13 or newer +- A database (MySQL or Postgresql) + ### Get the source code ``` diff --git a/components/charts/MetricCard.js b/components/charts/MetricCard.js index 4ebbe28a..ad6c62eb 100644 --- a/components/charts/MetricCard.js +++ b/components/charts/MetricCard.js @@ -1,12 +1,9 @@ import React from 'react'; import { useSpring, animated } from 'react-spring'; +import { formatNumber } from '../../lib/format'; import styles from './MetricCard.module.css'; -function defaultFormat(n) { - return Number(n).toFixed(0); -} - -const MetricCard = ({ value = 0, label, format = defaultFormat }) => { +const MetricCard = ({ value = 0, label, format = formatNumber }) => { const props = useSpring({ x: value, from: { x: 0 } }); return ( diff --git a/components/charts/MetricCard.module.css b/components/charts/MetricCard.module.css index 96adad3c..7f03049a 100644 --- a/components/charts/MetricCard.module.css +++ b/components/charts/MetricCard.module.css @@ -2,7 +2,7 @@ display: flex; flex-direction: column; justify-content: center; - width: 140px; + min-width: 140px; } .value { diff --git a/components/charts/MetricsBar.js b/components/charts/MetricsBar.js index 6a6c20a1..18a1a43b 100644 --- a/components/charts/MetricsBar.js +++ b/components/charts/MetricsBar.js @@ -2,13 +2,16 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import MetricCard from './MetricCard'; import { get } from 'lib/web'; -import { formatShortTime } from 'lib/format'; +import { formatShortTime, formatNumber, formatLongNumber } from 'lib/format'; import styles from './MetricsBar.module.css'; export default function MetricsBar({ websiteId, startDate, endDate, className }) { const [data, setData] = useState({}); + const [format, setFormat] = useState(true); const { pageviews, uniques, bounces, totaltime } = data; + const formatFunc = format ? formatLongNumber : formatNumber; + async function loadData() { setData( await get(`/api/website/${websiteId}/metrics`, { @@ -18,14 +21,18 @@ export default function MetricsBar({ websiteId, startDate, endDate, className }) ); } + function handleSetFormat() { + setFormat(state => !state); + } + useEffect(() => { loadData(); }, [websiteId, startDate, endDate]); return ( -
- - +
+ + div:last-child { display: none; } diff --git a/components/charts/RankingsChart.js b/components/charts/RankingsChart.js index 54a7b4bb..89de63a3 100644 --- a/components/charts/RankingsChart.js +++ b/components/charts/RankingsChart.js @@ -2,11 +2,11 @@ import React, { useState, useEffect, useMemo } from 'react'; import { FixedSizeList } from 'react-window'; import { useSpring, animated, config } from 'react-spring'; import classNames from 'classnames'; -import CheckVisible from 'components/helpers/CheckVisible'; import Button from 'components/common/Button'; import Arrow from 'assets/arrow-right.svg'; import { get } from 'lib/web'; import { percentFilter } from 'lib/filters'; +import { formatNumber, formatLongNumber } from 'lib/format'; import styles from './RankingsChart.module.css'; export default function RankingsChart({ @@ -23,6 +23,8 @@ export default function RankingsChart({ onExpand = () => {}, }) { const [data, setData] = useState(); + const [format, setFormat] = useState(true); + const formatFunc = format ? formatLongNumber : formatNumber; const rankings = useMemo(() => { if (data) { @@ -48,14 +50,8 @@ export default function RankingsChart({ onDataLoad(updated); } - useEffect(() => { - if (websiteId) { - loadData(); - } - }, [websiteId, startDate, endDate, type]); - - if (!data) { - return null; + function handleSetFormat() { + setFormat(state => !state); } const Row = ({ index, style }) => { @@ -67,16 +63,33 @@ export default function RankingsChart({ ); }; + useEffect(() => { + if (websiteId) { + loadData(); + } + }, [websiteId, startDate, endDate, type]); + + if (!data) { + return null; + } + return (
-
+
{title}
{heading}
{limit ? ( rankings.map(({ x, y, z }) => ( - + )) ) : ( @@ -95,7 +108,7 @@ export default function RankingsChart({ ); } -const AnimatedRow = ({ label, value, percent, animate }) => { +const AnimatedRow = ({ label, value, percent, animate, format }) => { const props = useSpring({ width: percent, y: value, @@ -106,7 +119,7 @@ const AnimatedRow = ({ label, value, percent, animate }) => { return (
{label}
- {props.y.interpolate(n => n.toFixed(0))} + {props.y.interpolate(format)}
= 1000000) { + return `${(n / 1000000).toFixed(1)}m`; + } + if (n >= 100000) { + return `${(n / 1000).toFixed(0)}k`; + } + if (n >= 10000) { + return `${(n / 1000).toFixed(1)}k`; + } + if (n >= 1000) { + return `${(n / 1000).toFixed(2)}k`; + } + return formatNumber(n); +} diff --git a/package.json b/package.json index 66a9a59d..d661b553 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "umami", - "version": "0.9.0", + "version": "0.10.0", "description": "A simple, fast, website analytics alternative to Google Analytics. ", "author": "Mike Cao ", "license": "MIT",