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 e65608b6a..de6f9ce8a 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 5599bc2e2..e497a25e6 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 a512a5ad8..5234bf2f2 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 f17c195e8..675fce16f 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 e93f536e1..d3a70866c 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 b8b203c4c..b21b92b95 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 82f9c3ee5..518fa488e 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 63add2683..b5e7a65b5 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 6fb09a647..7c07d017f 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 0d14448b7..8f052fc31 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 8f052fc31..701a97d4a 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 701a97d4a..95421b227 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 25bb4a086..95eb00c37 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 2d51ab749..cbd4c9ba9 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 df23bde83..48f9f2659 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 ef736ee03..3e9b99250 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 b5e7a65b5..6f23e4c89 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 0414ff7ff..237f065ec 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 6279d338e..45950086c 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 c39038291..856e81e83 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 6f23e4c89..8324f6869 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 50d623bbb..e166563b7 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 3bb6ffe8b..02ec1c3cc 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 5234bf2f2..485cc221a 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"