From 785338154a4f8906b2c4623d0a38880e348d58af Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Sat, 20 Feb 2021 13:21:12 +0800 Subject: [PATCH 01/15] fix: the page content jitters when the page jumps. --- styles/index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/styles/index.css b/styles/index.css index e65608b6..de6f9ce8 100644 --- a/styles/index.css +++ b/styles/index.css @@ -14,6 +14,7 @@ body { font-size: var(--font-size-normal); color: var(--gray900); background: var(--gray75); + overflow-y: overlay; } .zh-CN { From f3579160420ce7c6ab28c21cb63564074a08cd23 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 10:31:23 +0100 Subject: [PATCH 02/15] fix: event table fails to render in detail view The `EventsTable` component failed to render in the detail view, because the `onDataLoad` property is called but none was provided. See the issue for details. Add a nullcheck to `onDataLoad` before calling it. Refs #495 --- components/metrics/EventsTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/metrics/EventsTable.js b/components/metrics/EventsTable.js index 5599bc2e..e497a25e 100644 --- a/components/metrics/EventsTable.js +++ b/components/metrics/EventsTable.js @@ -19,7 +19,7 @@ export default function EventsTable({ websiteId, ...props }) { function handleDataLoad(data) { setEventTypes([...new Set(data.map(({ x }) => x.split('\t')[0]))]); - props.onDataLoad(data); + props.onDataLoad?.(data); } return ( From a65f9b9d730187821bb2c30b55773d8022b5b07d Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 10:54:30 +0100 Subject: [PATCH 03/15] fix(deps): add prop-types dependency Forgot to do this in the referenced PR. Refs #481 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index a512a5ad..5234bf2f 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "moment-timezone": "^0.5.32", "next": "^10.0.7", "prompts": "2.4.0", + "prop-types": "^15.7.2", "react": "^17.0.1", "react-dom": "^17.0.1", "react-intl": "^5.12.3", From 20932a4a113eb3bb7710c0976911cabad4a653e5 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 13:20:19 +0100 Subject: [PATCH 04/15] style(grid): grid row x-padding for smaller screens --- components/layout/GridLayout.module.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/layout/GridLayout.module.css b/components/layout/GridLayout.module.css index f17c195e..675fce16 100644 --- a/components/layout/GridLayout.module.css +++ b/components/layout/GridLayout.module.css @@ -35,6 +35,6 @@ .row > .col { border-top: 1px solid var(--gray300); border-left: 0; - padding: 0; + padding: 20px 0; } } From a981fa30c75ccd8fa85c29145c3aeab0a2ec596d Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 13:25:27 +0100 Subject: [PATCH 05/15] style(MetricTable): fill container height --- components/metrics/MetricsTable.module.css | 1 + 1 file changed, 1 insertion(+) diff --git a/components/metrics/MetricsTable.module.css b/components/metrics/MetricsTable.module.css index e93f536e..d3a70866 100644 --- a/components/metrics/MetricsTable.module.css +++ b/components/metrics/MetricsTable.module.css @@ -1,6 +1,7 @@ .container { position: relative; min-height: 430px; + height: 100%; font-size: var(--font-size-small); display: flex; flex-direction: column; From 98a60d0d3ecc08224d8e9284ee5f5defc54cf29a Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 13:51:33 +0100 Subject: [PATCH 06/15] fix(Dashboard): "no data" caption not rendered Change styling of `DataTable` to make `NoData` visible again. Refs #498 --- components/metrics/DataTable.module.css | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/metrics/DataTable.module.css b/components/metrics/DataTable.module.css index b8b203c4..b21b92b9 100644 --- a/components/metrics/DataTable.module.css +++ b/components/metrics/DataTable.module.css @@ -1,14 +1,15 @@ .table { position: relative; + height: 100%; font-size: var(--font-size-small); - display: flex; - flex-direction: column; - flex: 1; + display: grid; + grid-template-rows: fit-content(100%) auto; overflow: hidden; } .body { position: relative; + height: 100%; overflow: auto; } From c5e03cd5d4e68ae867cb4d40c2cd3414424ce7f9 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 14:39:30 +0100 Subject: [PATCH 07/15] fix(styling): "no data" caption not considered in height calculations Change `NoData` container position to `relative`. Otherwise it won't be considered in `auto` height calculations by browsers. Also change the way `NoData` content is centered. --- components/common/NoData.module.css | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/common/NoData.module.css b/components/common/NoData.module.css index 82f9c3ee..518fa488 100644 --- a/components/common/NoData.module.css +++ b/components/common/NoData.module.css @@ -1,8 +1,11 @@ .container { color: var(--gray500); font-size: var(--font-size-normal); - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); + position: relative; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + width: 100%; + height: 100%; } From f93ecdfee8dfda78ecb0861a6fb3aefcccad31bf Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Sun, 21 Feb 2021 15:12:49 +0100 Subject: [PATCH 08/15] style(realtime): fix layout for realtime protocol --- components/metrics/RealtimeLog.js | 8 +++++--- components/metrics/RealtimeLog.module.css | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/metrics/RealtimeLog.js b/components/metrics/RealtimeLog.js index 63add268..b5e7a65b 100644 --- a/components/metrics/RealtimeLog.js +++ b/components/metrics/RealtimeLog.js @@ -176,9 +176,11 @@ export default function RealtimeLog({ data, websites, websiteId }) {
{logs?.length === 0 && } - - {Row} - + {logs?.length > 0 && ( + + {Row} + + )}
); diff --git a/components/metrics/RealtimeLog.module.css b/components/metrics/RealtimeLog.module.css index 6fb09a64..7c07d017 100644 --- a/components/metrics/RealtimeLog.module.css +++ b/components/metrics/RealtimeLog.module.css @@ -1,6 +1,9 @@ .table { font-size: var(--font-size-xsmall); overflow: hidden; + height: 100%; + display: grid; + grid-template-rows: fit-content(100%) fit-content(100%) auto; } .header { @@ -21,6 +24,7 @@ .body { overflow: auto; + height: 100%; } .icon { From 0c2b68d56eb7b0185dc1a863f5e77a4ceca762b8 Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Tue, 23 Feb 2021 15:58:31 +0100 Subject: [PATCH 09/15] fix(regex): protocol accepted in domain name Add missing brackets to the regex. Before, the caret and dollar sign symbols were not applied to the whole string but to either side of the `|` symbol. --- lib/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/constants.js b/lib/constants.js index 0d14448b..8f052fc3 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -80,7 +80,7 @@ export const POSTGRESQL_DATE_FORMATS = { year: 'YYYY-01-01', }; -export const DOMAIN_REGEX = /^localhost(:\d{1,5})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$/; +export const DOMAIN_REGEX = /^(localhost(:\d{1,5})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/; export const DESKTOP_SCREEN_WIDTH = 1920; export const LAPTOP_SCREEN_WIDTH = 1024; From 22ca617165cf2147f30338e0c8106d6d9fa8438d Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Tue, 23 Feb 2021 16:11:42 +0100 Subject: [PATCH 10/15] fix(regex): ports in domain names Accept port numbers on all domain names, not just `localhost`. Deny leading zeros in port numbers. --- lib/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/constants.js b/lib/constants.js index 8f052fc3..701a97d4 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -80,7 +80,7 @@ export const POSTGRESQL_DATE_FORMATS = { year: 'YYYY-01-01', }; -export const DOMAIN_REGEX = /^(localhost(:\d{1,5})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/; +export const DOMAIN_REGEX = /^(localhost|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})(:[1-9]\d{0,4})?$/; export const DESKTOP_SCREEN_WIDTH = 1920; export const LAPTOP_SCREEN_WIDTH = 1024; From 9115ce42007302dbe09b8d7c7afd36687e7f22af Mon Sep 17 00:00:00 2001 From: Alexander Klein Date: Wed, 24 Feb 2021 11:00:08 +0100 Subject: [PATCH 11/15] revert: allow ports in non-localhost domain names Refs: 22ca617 --- lib/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/constants.js b/lib/constants.js index 701a97d4..95421b22 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -80,7 +80,7 @@ export const POSTGRESQL_DATE_FORMATS = { year: 'YYYY-01-01', }; -export const DOMAIN_REGEX = /^(localhost|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})(:[1-9]\d{0,4})?$/; +export const DOMAIN_REGEX = /^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/; export const DESKTOP_SCREEN_WIDTH = 1920; export const LAPTOP_SCREEN_WIDTH = 1024; From f7201c9cfc5ac250992282266a0b60b6606cde8c Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 26 Feb 2021 19:50:44 -0800 Subject: [PATCH 12/15] Remove domain parameter from queries. --- components/metrics/MetricsTable.js | 2 -- components/metrics/ReferrersTable.js | 1 - lib/queries.js | 2 +- pages/api/website/[id]/metrics.js | 26 ++++++++++++++++---------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js index 25bb4a08..95eb00c3 100644 --- a/components/metrics/MetricsTable.js +++ b/components/metrics/MetricsTable.js @@ -17,7 +17,6 @@ import styles from './MetricsTable.module.css'; export default function MetricsTable({ websiteId, - websiteDomain, type, className, dataFilter, @@ -42,7 +41,6 @@ export default function MetricsTable({ type, start_at: +startDate, end_at: +endDate, - domain: websiteDomain, url, }, onDataLoad, diff --git a/components/metrics/ReferrersTable.js b/components/metrics/ReferrersTable.js index 2d51ab74..cbd4c9ba 100644 --- a/components/metrics/ReferrersTable.js +++ b/components/metrics/ReferrersTable.js @@ -42,7 +42,6 @@ export default function ReferrersTable({ websiteId, websiteDomain, showFilters, type="referrer" metric={} websiteId={websiteId} - websiteDomain={websiteDomain} dataFilter={refFilter} filterOptions={{ domain: websiteDomain, diff --git a/lib/queries.js b/lib/queries.js index df23bde8..48f9f265 100644 --- a/lib/queries.js +++ b/lib/queries.js @@ -428,7 +428,7 @@ export function getPageviewMetrics(website_id, start_at, end_at, field, table, f if (domain) { domainFilter = `and referrer not like $${params.length + 1} and referrer not like '/%'`; - params.push(`%${domain}%`); + params.push(`%://${domain}/%`); } if (url) { diff --git a/pages/api/website/[id]/metrics.js b/pages/api/website/[id]/metrics.js index ef736ee0..3e9b9925 100644 --- a/pages/api/website/[id]/metrics.js +++ b/pages/api/website/[id]/metrics.js @@ -1,6 +1,5 @@ -import { getPageviewMetrics, getSessionMetrics } from 'lib/queries'; -import { ok, badRequest, methodNotAllowed, unauthorized } from 'lib/response'; -import { DOMAIN_REGEX } from 'lib/constants'; +import { getPageviewMetrics, getSessionMetrics, getWebsiteById } from 'lib/queries'; +import { ok, methodNotAllowed, unauthorized, badRequest } from 'lib/response'; import { allowQuery } from 'lib/auth'; const sessionColumns = ['browser', 'os', 'device', 'country']; @@ -31,11 +30,7 @@ export default async (req, res) => { return unauthorized(res); } - const { id, type, start_at, end_at, domain, url } = req.query; - - if (domain && !DOMAIN_REGEX.test(domain)) { - return badRequest(res); - } + const { id, type, start_at, end_at, url } = req.query; const websiteId = +id; const startDate = new Date(+start_at); @@ -47,7 +42,18 @@ export default async (req, res) => { return ok(res, data); } - if (type === 'event' || pageviewColumns.includes(type)) { + if (pageviewColumns.includes(type) || type === 'event') { + let domain; + if (type === 'referrer') { + const website = getWebsiteById(websiteId); + + if (!website) { + return badRequest(res); + } + + domain = website.domain; + } + const data = await getPageviewMetrics( websiteId, startDate, @@ -55,7 +61,7 @@ export default async (req, res) => { getColumn(type), getTable(type), { - domain: type !== 'event' && domain, + domain, url: type !== 'url' && url, }, ); From 50bc2371b84dbb34182723809b5f51e17308392d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 26 Feb 2021 21:20:47 -0800 Subject: [PATCH 13/15] Fix unknown country display on realtime page. --- components/metrics/RealtimeLog.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/metrics/RealtimeLog.js b/components/metrics/RealtimeLog.js index b5e7a65b..6f23e4c8 100644 --- a/components/metrics/RealtimeLog.js +++ b/components/metrics/RealtimeLog.js @@ -129,7 +129,12 @@ export default function RealtimeLog({ data, websites, websiteId }) { id="message.log.visitor" defaultMessage="Visitor from {country} using {browser} on {os} {device}" values={{ - country: {countryNames[country]}, + country: ( + + {countryNames[country] || + intl.formatMessage({ id: 'label.unknown', defaultMessage: 'Unknown' })} + + ), browser: {BROWSERS[browser]}, os: {os}, device: {intl.formatMessage(devices[device])?.toLowerCase()}, From d6d0f99daa2ed9ba2a2fbf3cbeb59d61823b2d70 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 26 Feb 2021 22:41:05 -0800 Subject: [PATCH 14/15] Updated date formatting for locales. --- components/common/Calendar.js | 2 +- components/common/DateFilter.js | 3 +-- components/metrics/BarChart.js | 8 ++++---- components/metrics/RealtimeLog.js | 6 +++--- lib/date.js | 16 ++++++++++++++++ lib/lang.js | 14 -------------- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/components/common/Calendar.js b/components/common/Calendar.js index 0414ff7f..237f065e 100644 --- a/components/common/Calendar.js +++ b/components/common/Calendar.js @@ -18,7 +18,7 @@ import { } from 'date-fns'; import Button from './Button'; import useLocale from 'hooks/useLocale'; -import { dateFormat } from 'lib/lang'; +import { dateFormat } from 'lib/date'; import { chunk } from 'lib/array'; import Chevron from 'assets/chevron-down.svg'; import Cross from 'assets/times.svg'; diff --git a/components/common/DateFilter.js b/components/common/DateFilter.js index 6279d338..45950086 100644 --- a/components/common/DateFilter.js +++ b/components/common/DateFilter.js @@ -6,8 +6,7 @@ import Modal from './Modal'; import DropDown from './DropDown'; import DatePickerForm from 'components/forms/DatePickerForm'; import useLocale from 'hooks/useLocale'; -import { getDateRange } from 'lib/date'; -import { dateFormat } from 'lib/lang'; +import { getDateRange, dateFormat } from 'lib/date'; import Calendar from 'assets/calendar-alt.svg'; import Icon from './Icon'; diff --git a/components/metrics/BarChart.js b/components/metrics/BarChart.js index c3903829..856e81e8 100644 --- a/components/metrics/BarChart.js +++ b/components/metrics/BarChart.js @@ -3,7 +3,7 @@ import classNames from 'classnames'; import ChartJS from 'chart.js'; import Legend from 'components/metrics/Legend'; import { formatLongNumber } from 'lib/format'; -import { dateFormat, timeFormat } from 'lib/lang'; +import { dateFormat } from 'lib/date'; import useLocale from 'hooks/useLocale'; import useTheme from 'hooks/useTheme'; import { DEFAUL_CHART_HEIGHT, DEFAULT_ANIMATION_DURATION, THEME_COLORS } from 'lib/constants'; @@ -46,7 +46,7 @@ export default function BarChart({ case 'minute': return index % 2 === 0 ? dateFormat(d, 'H:mm', locale) : ''; case 'hour': - return timeFormat(d, locale); + return dateFormat(d, 'p', locale); case 'day': if (records > 31) { if (w <= 500) { @@ -93,9 +93,9 @@ export default function BarChart({ function getTooltipFormat(unit) { switch (unit) { case 'hour': - return 'EEE ha — MMM d yyyy'; + return 'EEE p — PPP'; default: - return 'EEE MMMM d yyyy'; + return 'PPPP'; } } diff --git a/components/metrics/RealtimeLog.js b/components/metrics/RealtimeLog.js index 6f23e4c8..8324f686 100644 --- a/components/metrics/RealtimeLog.js +++ b/components/metrics/RealtimeLog.js @@ -2,11 +2,11 @@ import React, { useMemo, useState } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import { FixedSizeList } from 'react-window'; import firstBy from 'thenby'; -import { format } from 'date-fns'; import Icon from 'components/common/Icon'; import Tag from 'components/common/Tag'; import Dot from 'components/common/Dot'; import FilterButtons from 'components/common/FilterButtons'; +import NoData from 'components/common/NoData'; import { devices } from 'components/messages'; import useLocale from 'hooks/useLocale'; import useCountryNames from 'hooks/useCountryNames'; @@ -15,8 +15,8 @@ import Bolt from 'assets/bolt.svg'; import Visitor from 'assets/visitor.svg'; import Eye from 'assets/eye.svg'; import { stringToColor } from 'lib/format'; +import { dateFormat } from 'lib/date'; import styles from './RealtimeLog.module.css'; -import NoData from '../common/NoData'; const TYPE_ALL = 0; const TYPE_PAGEVIEW = 1; @@ -145,7 +145,7 @@ export default function RealtimeLog({ data, websites, websiteId }) { } function getTime({ created_at }) { - return format(new Date(created_at), 'h:mm:ss'); + return dateFormat(new Date(created_at), 'pp', locale); } function getColor(row) { diff --git a/lib/date.js b/lib/date.js index 50d623bb..e166563b 100644 --- a/lib/date.js +++ b/lib/date.js @@ -23,7 +23,10 @@ import { differenceInCalendarDays, differenceInCalendarMonths, differenceInCalendarYears, + format, } from 'date-fns'; +import { enUS } from 'date-fns/locale'; +import { dateLocales } from 'lib/lang'; export function getTimezone() { return moment.tz.guess(); @@ -150,3 +153,16 @@ export function getDateLength(startDate, endDate, unit) { const [diff] = dateFuncs[unit]; return diff(endDate, startDate) + 1; } + +export const customFormats = { + 'en-US': { + p: 'ha', + pp: 'h:mm:ss', + }, +}; + +export function dateFormat(date, str, locale = 'en-US') { + return format(date, customFormats?.[locale]?.[str] || str, { + locale: dateLocales[locale] || enUS, + }); +} diff --git a/lib/lang.js b/lib/lang.js index 3bb6ffe8..02ec1c3c 100644 --- a/lib/lang.js +++ b/lib/lang.js @@ -1,4 +1,3 @@ -import { format } from 'date-fns'; import { cs, da, @@ -118,11 +117,6 @@ export const dateLocales = { 'it-IT': it, }; -const timeFormats = { - // https://date-fns.org/v2.17.0/docs/format - 'en-US': 'ha', -}; - export const menuOptions = [ { label: '中文', value: 'zh-CN', display: 'cn' }, { label: '中文(繁體)', value: 'zh-TW', display: 'tw' }, @@ -153,11 +147,3 @@ export const menuOptions = [ { label: 'Türkçe', value: 'tr-TR', display: 'tr' }, { label: 'українська', value: 'uk-UA', display: 'uk' }, ]; - -export function dateFormat(date, str, locale) { - return format(date, str, { locale: dateLocales[locale] || enUS }); -} - -export function timeFormat(date, locale = 'en-US') { - return format(date, timeFormats[locale] || 'p', { locale: dateLocales[locale] }); -} From f3bf35189a3012370775ae5921c2a53d9eb9c96d Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 26 Feb 2021 22:46:44 -0800 Subject: [PATCH 15/15] Bump version. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5234bf2f..485cc221 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "umami", - "version": "1.13.0", + "version": "1.14.0", "description": "A simple, fast, website analytics alternative to Google Analytics. ", "author": "Mike Cao ", "license": "MIT", - "homepage": "https://github.com/mikecao/umami", + "homepage": "https://umami.is", "repository": { "type": "git", "url": "https://github.com/mikecao/umami.git"