}
>
);
}
diff --git a/components/metrics/BarChart.module.css b/components/metrics/BarChart.module.css
index 593bff91..850d1ea7 100644
--- a/components/metrics/BarChart.module.css
+++ b/components/metrics/BarChart.module.css
@@ -1,10 +1,21 @@
.chart {
position: relative;
height: 400px;
+ overflow: hidden;
+}
+
+.tooltip {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.tooltip .value {
+ text-transform: lowercase;
}
@media only screen and (max-width: 992px) {
.chart {
- height: 200px;
+ /*height: 200px;*/
}
}
diff --git a/components/metrics/ChartTooltip.js b/components/metrics/ChartTooltip.js
deleted file mode 100644
index c409f462..00000000
--- a/components/metrics/ChartTooltip.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { StatusLight } from 'react-basics';
-import styles from './ChartTooltip.module.css';
-import ReactTooltip from 'react-tooltip';
-
-export default function ChartTooltip({ chartId, tooltip }) {
- if (!tooltip) {
- return null;
- }
-
- const { title, value, label, labelColor } = tooltip;
-
- return (
-
- );
-}
diff --git a/components/metrics/EventsChart.js b/components/metrics/EventsChart.js
index db647c2f..6ccab9ab 100644
--- a/components/metrics/EventsChart.js
+++ b/components/metrics/EventsChart.js
@@ -33,12 +33,12 @@ export default function EventsChart({ websiteId, className, token }) {
if (!data) return [];
if (isLoading) return data;
- const map = data.reduce((obj, { x, t, y }) => {
+ const map = data.reduce((obj, { x, y }) => {
if (!obj[x]) {
obj[x] = [];
}
- obj[x].push({ t, y });
+ obj[x].push({ x, y });
return obj;
}, {});
@@ -76,7 +76,6 @@ export default function EventsChart({ websiteId, className, token }) {
return (
-
+ handleCloseFilter(key)}>
+
+ {`${key}`} = {`${safeDecodeURI(params[key])}`}
+
+
+
+
);
})}
diff --git a/components/metrics/FilterTags.module.css b/components/metrics/FilterTags.module.css
index 50ae60a0..1c8458ac 100644
--- a/components/metrics/FilterTags.module.css
+++ b/components/metrics/FilterTags.module.css
@@ -1,11 +1,22 @@
.filters {
display: flex;
- justify-content: flex-start;
- align-items: flex-start;
+ align-items: center;
+ gap: 10px;
}
.tag {
- text-align: center;
- margin-bottom: 10px;
- margin-right: 20px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 10px;
+ font-size: var(--font-size-sm);
+ border: 1px solid var(--base600);
+ border-radius: var(--border-radius);
+ line-height: 30px;
+ padding: 0 8px;
+ cursor: pointer;
+}
+
+.tag:hover {
+ background: var(--base75);
}
diff --git a/components/metrics/Legend.module.css b/components/metrics/Legend.module.css
index e78bf609..b079e67f 100644
--- a/components/metrics/Legend.module.css
+++ b/components/metrics/Legend.module.css
@@ -2,13 +2,13 @@
display: flex;
justify-content: center;
flex-wrap: wrap;
- margin-top: 10px;
+ padding: 10px 0;
}
.label {
display: flex;
align-items: center;
- font-size: var(--font-size-xs);
+ font-size: var(--font-size-sm);
cursor: pointer;
}
diff --git a/components/metrics/MetricsTable.js b/components/metrics/MetricsTable.js
index 258ea1e8..945f7796 100644
--- a/components/metrics/MetricsTable.js
+++ b/components/metrics/MetricsTable.js
@@ -40,7 +40,7 @@ export default function MetricsTable({
const { data, isLoading, isFetched, error } = useQuery(
[
- 'websites:mnetrics',
+ 'websites:metrics',
{ websiteId, type, modified, url, referrer, os, browser, device, country },
],
() =>
diff --git a/components/metrics/PageviewsChart.js b/components/metrics/PageviewsChart.js
index f040f2eb..47a085f0 100644
--- a/components/metrics/PageviewsChart.js
+++ b/components/metrics/PageviewsChart.js
@@ -25,12 +25,16 @@ export default function PageviewsChart({
const primaryColor = colord(THEME_COLORS[theme].primary);
return {
views: {
- background: primaryColor.alpha(0.4).toRgbString(),
- border: primaryColor.alpha(0.5).toRgbString(),
+ hoverBackgroundColor: primaryColor.alpha(0.7).toRgbString(),
+ backgroundColor: primaryColor.alpha(0.4).toRgbString(),
+ borderColor: primaryColor.alpha(0.7).toRgbString(),
+ hoverBorderColor: primaryColor.toRgbString(),
},
visitors: {
- background: primaryColor.alpha(0.6).toRgbString(),
- border: primaryColor.alpha(0.7).toRgbString(),
+ hoverBackgroundColor: primaryColor.alpha(0.9).toRgbString(),
+ backgroundColor: primaryColor.alpha(0.6).toRgbString(),
+ borderColor: primaryColor.alpha(0.9).toRgbString(),
+ hoverBorderColor: primaryColor.toRgbString(),
},
};
}, [theme]);
@@ -50,30 +54,28 @@ export default function PageviewsChart({
return null;
}
+ const datasets = [
+ {
+ label: formatMessage(labels.uniqueVisitors),
+ data: data.sessions,
+ borderWidth: 1,
+ ...colors.visitors,
+ },
+ {
+ label: formatMessage(labels.pageViews),
+ data: data.pageviews,
+ borderWidth: 1,
+ ...colors.views,
+ },
+ ];
+
return (
{
return referrer ? (
@@ -34,14 +16,12 @@ export default function ReferrersTable({ websiteId, showFilters, ...props }) {
return (
<>
- {showFilters && }
>
diff --git a/components/metrics/WebsiteChart.module.css b/components/metrics/WebsiteChart.module.css
index 05bfcbb8..3a3d4718 100644
--- a/components/metrics/WebsiteChart.module.css
+++ b/components/metrics/WebsiteChart.module.css
@@ -7,7 +7,7 @@
.chart {
position: relative;
- padding-bottom: 10px;
+ overflow: hidden;
}
.title {
diff --git a/components/pages/realtime/RealtimeLog.js b/components/pages/realtime/RealtimeLog.js
index 06b7537e..be7aeb8d 100644
--- a/components/pages/realtime/RealtimeLog.js
+++ b/components/pages/realtime/RealtimeLog.js
@@ -5,7 +5,7 @@ import { FixedSizeList } from 'react-window';
import firstBy from 'thenby';
import FilterButtons from 'components/common/FilterButtons';
import NoData from 'components/common/NoData';
-import { getDeviceMessage, labels, messages } from 'components/messages';
+import { labels, messages } from 'components/messages';
import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames';
import { BROWSERS } from 'lib/constants';
@@ -102,7 +102,7 @@ export default function RealtimeLog({ data, websiteDomain }) {
country: {countryNames[country] || formatMessage(labels.unknown)},
browser: {BROWSERS[browser]},
os: {os},
- device: {formatMessage(getDeviceMessage(device))},
+ device: {formatMessage(labels[device] || labels.unknown)},
}}
/>
);
diff --git a/components/pages/settings/teams/TeamWebsitesTable.js b/components/pages/settings/teams/TeamWebsitesTable.js
index a596204c..52141523 100644
--- a/components/pages/settings/teams/TeamWebsitesTable.js
+++ b/components/pages/settings/teams/TeamWebsitesTable.js
@@ -17,14 +17,14 @@ import { labels } from 'components/messages';
import useUser from 'hooks/useUser';
import useApi from 'hooks/useApi';
-export default function TeamWebsitesTable({ teamId, data = [], onSave }) {
+export default function TeamWebsitesTable({ data = [], onSave }) {
const { formatMessage } = useIntl();
const { user } = useUser();
const { del, useMutation } = useApi();
- const { mutate } = useMutation(data => del(`/teamWebsites/${data.teamWebsiteId}`));
+ const { mutate } = useMutation(({ teamWebsiteId }) => del(`/teamWebsites/${teamWebsiteId}`));
const columns = [
- { name: 'name', label: formatMessage(labels.name), style: { flex: 2 } },
+ { name: 'name', label: formatMessage(labels.name) },
{ name: 'domain', label: formatMessage(labels.domain) },
{ name: 'action', label: ' ' },
];
diff --git a/db/clickhouse/schema.sql b/db/clickhouse/schema.sql
index 5cac11ef..e8251194 100644
--- a/db/clickhouse/schema.sql
+++ b/db/clickhouse/schema.sql
@@ -19,8 +19,11 @@ CREATE TABLE event
subdivision2 LowCardinality(String),
city String,
--pageview
- url String,
- referrer String,
+ url_path String,
+ url_query String,
+ referrer_path String,
+ referrer_query String,
+ referrer_domain String,
page_title String,
--event
event_type UInt32,
@@ -48,8 +51,11 @@ CREATE TABLE event_queue (
subdivision2 LowCardinality(String),
city String,
--pageview
- url String,
- referrer String,
+ url_path String,
+ url_query String,
+ referrer_path String,
+ referrer_query String,
+ referrer_domain String,
page_title String,
--event
event_type UInt32,
@@ -79,8 +85,11 @@ SELECT website_id,
subdivision1,
subdivision2,
city,
- url,
- referrer,
+ url_path,
+ url_query,
+ referrer_path,
+ referrer_query,
+ referrer_domain,
page_title,
event_type,
event_name,
diff --git a/db/mysql/migrations/01_init/migration.sql b/db/mysql/migrations/01_init/migration.sql
index a083d03a..eacbc38d 100644
--- a/db/mysql/migrations/01_init/migration.sql
+++ b/db/mysql/migrations/01_init/migration.sql
@@ -61,8 +61,11 @@ CREATE TABLE `website_event` (
`website_id` VARCHAR(36) NOT NULL,
`session_id` VARCHAR(36) NOT NULL,
`created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
- `url` VARCHAR(500) NOT NULL,
- `referrer` VARCHAR(500) NULL,
+ `url_path` VARCHAR(500) NOT NULL,
+ `url_query` VARCHAR(500) NULL,
+ `referrer_path` VARCHAR(500) NULL,
+ `referrer_query` VARCHAR(500) NULL,
+ `referrer_domain` VARCHAR(500) NULL,
`page_title` VARCHAR(500) NULL,
`event_type` INTEGER UNSIGNED NOT NULL DEFAULT 1,
`event_name` VARCHAR(50) NULL,
@@ -79,14 +82,12 @@ CREATE TABLE `website_event` (
CREATE TABLE `team` (
`team_id` VARCHAR(36) NOT NULL,
`name` VARCHAR(50) NOT NULL,
- `user_id` VARCHAR(36) NOT NULL,
`access_code` VARCHAR(50) NULL,
`created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
`updated_at` TIMESTAMP(0) NULL,
UNIQUE INDEX `team_team_id_key`(`team_id`),
UNIQUE INDEX `team_access_code_key`(`access_code`),
- INDEX `team_user_id_idx`(`user_id`),
INDEX `team_access_code_idx`(`access_code`),
PRIMARY KEY (`team_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
@@ -110,13 +111,11 @@ CREATE TABLE `team_user` (
CREATE TABLE `team_website` (
`team_website_id` VARCHAR(36) NOT NULL,
`team_id` VARCHAR(36) NOT NULL,
- `user_id` VARCHAR(36) NOT NULL,
`website_id` VARCHAR(36) NOT NULL,
`created_at` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
UNIQUE INDEX `team_website_team_website_id_key`(`team_website_id`),
INDEX `team_website_team_id_idx`(`team_id`),
- INDEX `team_website_user_id_idx`(`user_id`),
INDEX `team_website_website_id_idx`(`website_id`),
PRIMARY KEY (`team_website_id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
diff --git a/db/mysql/schema.prisma b/db/mysql/schema.prisma
index a47ff428..1f182716 100644
--- a/db/mysql/schema.prisma
+++ b/db/mysql/schema.prisma
@@ -19,7 +19,6 @@ model User {
teamUser TeamUser[]
Website Website[]
- teamWebsite TeamWebsite[]
@@map("user")
}
@@ -65,15 +64,18 @@ model Website {
}
model WebsiteEvent {
- id String @id() @map("event_id") @db.VarChar(36)
- websiteId String @map("website_id") @db.VarChar(36)
- sessionId String @map("session_id") @db.VarChar(36)
- createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
- url String @db.VarChar(500)
- referrer String? @db.VarChar(500)
- pageTitle String? @map("page_title") @db.VarChar(500)
- eventType Int @default(1) @map("event_type") @db.UnsignedInt
- eventName String? @map("event_name") @db.VarChar(50)
+ id String @id() @map("event_id") @db.VarChar(36)
+ websiteId String @map("website_id") @db.VarChar(36)
+ sessionId String @map("session_id") @db.VarChar(36)
+ createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
+ urlPath String @map("url_path") @db.VarChar(500)
+ urlQuery String? @map("url_query") @db.VarChar(500)
+ referrerPath String? @map("referrer_path") @db.VarChar(500)
+ referrerQuery String? @map("referrer_query") @db.VarChar(500)
+ referrerDomain String? @map("referrer_domain") @db.VarChar(500)
+ pageTitle String? @map("page_title") @db.VarChar(500)
+ eventType Int @default(1) @map("event_type") @db.UnsignedInt
+ eventName String? @map("event_name") @db.VarChar(50)
@@index([createdAt])
@@index([sessionId])
@@ -86,7 +88,6 @@ model WebsiteEvent {
model Team {
id String @id() @unique() @map("team_id") @db.VarChar(36)
name String @db.VarChar(50)
- userId String @map("user_id") @db.VarChar(36)
accessCode String? @unique @map("access_code") @db.VarChar(50)
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @map("updated_at") @db.Timestamp(0)
@@ -94,7 +95,6 @@ model Team {
teamUsers TeamUser[]
teamWebsite TeamWebsite[]
- @@index([userId])
@@index([accessCode])
@@map("team")
}
@@ -118,16 +118,13 @@ model TeamUser {
model TeamWebsite {
id String @id() @unique() @map("team_website_id") @db.VarChar(36)
teamId String @map("team_id") @db.VarChar(36)
- userId String @map("user_id") @db.VarChar(36)
websiteId String @map("website_id") @db.VarChar(36)
createdAt DateTime? @default(now()) @map("created_at") @db.Timestamp(0)
team Team @relation(fields: [teamId], references: [id])
- user User @relation(fields: [userId], references: [id])
website Website @relation(fields: [websiteId], references: [id])
@@index([teamId])
- @@index([userId])
@@index([websiteId])
@@map("team_website")
}
diff --git a/db/postgresql/migrations/07_remove_user_id/migration.sql b/db/postgresql/migrations/07_remove_user_id/migration.sql
index 63122f49..ff92f545 100644
--- a/db/postgresql/migrations/07_remove_user_id/migration.sql
+++ b/db/postgresql/migrations/07_remove_user_id/migration.sql
@@ -15,5 +15,4 @@ DROP INDEX "team_website_user_id_idx";
ALTER TABLE "team" DROP COLUMN "user_id";
-- AlterTable
-ALTER TABLE "team_website" DROP COLUMN "user_id",
-ADD COLUMN "userId" UUID;
+ALTER TABLE "team_website" DROP COLUMN "user_id";
\ No newline at end of file
diff --git a/db/postgresql/migrations/08_split_url_referrer/migration.sql b/db/postgresql/migrations/08_split_url_referrer/migration.sql
new file mode 100644
index 00000000..7129d87a
--- /dev/null
+++ b/db/postgresql/migrations/08_split_url_referrer/migration.sql
@@ -0,0 +1,16 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `referrer` on the `website_event` table. All the data in the column will be lost.
+ - You are about to drop the column `url` on the `website_event` table. All the data in the column will be lost.
+ - Added the required column `url_path` to the `website_event` table without a default value. This is not possible if the table is not empty.
+
+*/
+-- AlterTable
+ALTER TABLE "website_event" DROP COLUMN "referrer",
+DROP COLUMN "url",
+ADD COLUMN "referrer_domain" VARCHAR(500),
+ADD COLUMN "referrer_path" VARCHAR(500),
+ADD COLUMN "referrer_query" VARCHAR(500),
+ADD COLUMN "url_path" VARCHAR(500) NOT NULL,
+ADD COLUMN "url_query" VARCHAR(500);
diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma
index be3be8f1..82b22c64 100644
--- a/db/postgresql/schema.prisma
+++ b/db/postgresql/schema.prisma
@@ -64,15 +64,18 @@ model Website {
}
model WebsiteEvent {
- id String @id() @map("event_id") @db.Uuid
- websiteId String @map("website_id") @db.Uuid
- sessionId String @map("session_id") @db.Uuid
- createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
- url String @db.VarChar(500)
- referrer String? @db.VarChar(500)
- pageTitle String? @map("page_title") @db.VarChar(500)
- eventType Int @default(1) @map("event_type") @db.Integer
- eventName String? @map("event_name") @db.VarChar(50)
+ id String @id() @map("event_id") @db.Uuid
+ websiteId String @map("website_id") @db.Uuid
+ sessionId String @map("session_id") @db.Uuid
+ createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6)
+ urlPath String @map("url_path") @db.VarChar(500)
+ urlQuery String? @map("url_query") @db.VarChar(500)
+ referrerPath String? @map("referrer_path") @db.VarChar(500)
+ referrerQuery String? @map("referrer_query") @db.VarChar(500)
+ referrerDomain String? @map("referrer_domain") @db.VarChar(500)
+ pageTitle String? @map("page_title") @db.VarChar(500)
+ eventType Int @default(1) @map("event_type") @db.Integer
+ eventName String? @map("event_name") @db.VarChar(50)
@@index([createdAt])
@@index([sessionId])
diff --git a/hooks/useDateRange.js b/hooks/useDateRange.js
index 16f3818c..a9896065 100644
--- a/hooks/useDateRange.js
+++ b/hooks/useDateRange.js
@@ -1,4 +1,3 @@
-import { parseISO } from 'date-fns';
import { parseDateRange } from 'lib/date';
import { setItem } from 'next-basics';
import { DATE_RANGE_CONFIG, DEFAULT_DATE_RANGE } from 'lib/constants';
diff --git a/lib/clickhouse.ts b/lib/clickhouse.ts
index a3030eeb..073a92e7 100644
--- a/lib/clickhouse.ts
+++ b/lib/clickhouse.ts
@@ -74,6 +74,9 @@ function getFilterQuery(filters = {}, params = {}) {
switch (key) {
case 'url':
+ arr.push(`and url_path = {${key}:String}`);
+ params[key] = filter;
+ break;
case 'pageTitle':
case 'os':
case 'browser':
@@ -92,18 +95,20 @@ function getFilterQuery(filters = {}, params = {}) {
break;
case 'referrer':
- arr.push(`and referrer ILIKE {${key}:String}`);
- params[key] = `%${filter}`;
+ arr.push(`and referrer_domain= {${key}:String}`);
+ params[key] = filter;
break;
case 'domain':
- arr.push(`and referrer NOT ILIKE {${key}:String}`);
- arr.push(`and referrer NOT ILIKE '/%'`);
+ arr.push(`and referrer_domain NOT ILIKE {${key}:String}`);
+ arr.push(`and referrer_domain NOT ILIKE '/%'`);
params[key] = `%://${filter}/%`;
break;
case 'query':
- arr.push(`and url like '%?%'`);
+ arr.push(`and url_query= {${key}:String}`);
+ params[key] = filter;
+ break;
}
return arr;
diff --git a/lib/date.js b/lib/date.js
index b1273603..38e1cc61 100644
--- a/lib/date.js
+++ b/lib/date.js
@@ -181,18 +181,18 @@ export function getDateArray(data, startDate, endDate, unit) {
const n = diff(endDate, startDate) + 1;
function findData(t) {
- const x = data.find(e => {
- return normalize(getDateFromString(e.t)).getTime() === t.getTime();
+ const d = data.find(({ x }) => {
+ return normalize(getDateFromString(x)).getTime() === t.getTime();
});
- return x?.y || 0;
+ return d?.y || 0;
}
for (let i = 0; i < n; i++) {
const t = normalize(add(startDate, i));
const y = findData(t);
- arr.push({ ...data[i], t, y });
+ arr.push({ x: t, y });
}
return arr;
diff --git a/lib/filters.js b/lib/filters.js
index 7e247220..9681a802 100644
--- a/lib/filters.js
+++ b/lib/filters.js
@@ -1,30 +1,10 @@
export const urlFilter = data => {
- const isValidUrl = url => {
- return url !== '' && url !== null;
- };
-
- const cleanUrl = url => {
- try {
- const { pathname } = new URL(url, location.origin);
-
- return pathname;
- } catch {
- return null;
- }
- };
-
const map = data.reduce((obj, { x, y }) => {
- if (!isValidUrl(x)) {
- return obj;
- }
-
- const url = cleanUrl(x);
-
- if (url) {
- if (!obj[url]) {
- obj[url] = y;
+ if (x) {
+ if (!obj[x]) {
+ obj[x] = y;
} else {
- obj[url] += y;
+ obj[x] += y;
}
}
diff --git a/lib/prisma.ts b/lib/prisma.ts
index 4461f044..20a5e4a6 100644
--- a/lib/prisma.ts
+++ b/lib/prisma.ts
@@ -74,6 +74,9 @@ function getFilterQuery(filters = {}, params = []): string {
switch (key) {
case 'url':
+ arr.push(`and url_path=$${params.length + 1}`);
+ params.push(decodeURIComponent(filter));
+ break;
case 'os':
case 'pageTitle':
case 'browser':
@@ -92,18 +95,20 @@ function getFilterQuery(filters = {}, params = []): string {
break;
case 'referrer':
- arr.push(`and referrer like $${params.length + 1}`);
- params.push(`%${decodeURIComponent(filter)}%`);
+ arr.push(`and referrer_domain=$${params.length + 1}`);
+ params.push(decodeURIComponent(filter));
break;
case 'domain':
- arr.push(`and referrer not like $${params.length + 1}`);
- arr.push(`and referrer not like '/%'`);
+ arr.push(`and referrer_domain not like $${params.length + 1}`);
+ arr.push(`and referrer_domain not like '/%'`);
params.push(`%://${filter}/%`);
break;
case 'query':
- arr.push(`and url like '%?%'`);
+ arr.push(`and url_query=$${params.length + 1}`);
+ params.push(decodeURIComponent(filter));
+ break;
}
return arr;
diff --git a/package.json b/package.json
index 16af05ae..bd4b67ec 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,8 @@
"@umami/prisma-client": "^0.2.0",
"@umami/redis-client": "^0.2.0",
"chalk": "^4.1.1",
- "chart.js": "^2.9.4",
+ "chart.js": "^4.2.1",
+ "chartjs-adapter-date-fns": "^3.0.0",
"classnames": "^2.3.1",
"clickhouse": "^2.5.0",
"colord": "^2.9.2",
@@ -93,13 +94,12 @@
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"react": "^18.2.0",
- "react-basics": "^0.71.0",
+ "react-basics": "^0.73.0",
"react-beautiful-dnd": "^13.1.0",
"react-dom": "^18.2.0",
"react-intl": "^5.24.7",
"react-simple-maps": "^2.3.0",
"react-spring": "^9.4.4",
- "react-tooltip": "^4.2.21",
"react-use-measure": "^2.0.4",
"react-window": "^1.8.6",
"request-ip": "^3.3.0",
diff --git a/pages/_app.js b/pages/_app.js
index ec14d084..ad9e71ac 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -10,6 +10,7 @@ import 'styles/locale.css';
import 'styles/index.css';
import '@fontsource/inter/400.css';
import '@fontsource/inter/700.css';
+import 'chartjs-adapter-date-fns';
import Script from 'next/script';
const client = new QueryClient({
diff --git a/pages/api/send.ts b/pages/api/send.ts
index 639549f0..eab9fdfd 100644
--- a/pages/api/send.ts
+++ b/pages/api/send.ts
@@ -34,8 +34,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
const { type, payload } = getJsonBody(req);
- const { referrer, eventName, eventData, pageTitle } = payload;
- let { url } = payload;
+ const { url, referrer, eventName, eventData, pageTitle } = payload;
// Validate eventData is JSON
if (eventData && !(typeof eventData === 'object' && !Array.isArray(eventData))) {
@@ -88,17 +87,41 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => {
const session = req.session;
+ let urlPath = url.split('?')[0];
+ const urlQuery = url.split('?')[1];
+ let referrerPath;
+ let referrerQuery;
+ let referrerDomain;
+
+ try {
+ const newRef = new URL(referrer);
+ referrerPath = newRef.pathname;
+ referrerDomain = newRef.hostname;
+ referrerQuery = newRef.search.substring(1);
+ } catch {
+ referrerPath = referrer.split('?')[0];
+ referrerQuery = referrer.split('?')[1];
+ }
+
if (process.env.REMOVE_TRAILING_SLASH) {
- url = url.replace(/\/$/, '');
+ urlPath = urlPath.replace(/\/$/, '');
}
if (type === 'pageview') {
- await savePageView({ ...session, url, referrer, pageTitle });
+ await savePageView({
+ ...session,
+ urlPath,
+ urlQuery,
+ referrerPath,
+ referrerQuery,
+ referrerDomain,
+ pageTitle,
+ });
} else if (type === 'event') {
await saveEvent({
...session,
- url,
- referrer,
+ urlPath,
+ urlQuery,
pageTitle,
eventName,
eventData,
diff --git a/pages/api/websites/[id]/metrics.ts b/pages/api/websites/[id]/metrics.ts
index c547daee..ad57790e 100644
--- a/pages/api/websites/[id]/metrics.ts
+++ b/pages/api/websites/[id]/metrics.ts
@@ -6,7 +6,17 @@ import { NextApiResponse } from 'next';
import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics';
import { getPageviewMetrics, getSessionMetrics, getWebsite } from 'queries';
-const sessionColumns = ['browser', 'os', 'device', 'screen', 'country', 'language'];
+const sessionColumns = [
+ 'browser',
+ 'os',
+ 'device',
+ 'screen',
+ 'country',
+ 'language',
+ 'subdivision1',
+ 'subdivision2',
+ 'city',
+];
const pageviewColumns = ['url', 'referrer', 'query', 'pageTitle'];
function getTable(type) {
@@ -26,12 +36,17 @@ function getTable(type) {
}
function getColumn(type) {
- if (type === 'event') {
- return 'event_name';
- }
- if (type === 'query') {
- return 'url';
+ switch (type) {
+ case 'url':
+ return 'url_path';
+ case 'referrer':
+ return 'referrer_domain';
+ case 'event':
+ return 'event_name';
+ case 'query':
+ return 'url_query';
}
+
return type;
}
diff --git a/queries/analytics/event/saveEvent.ts b/queries/analytics/event/saveEvent.ts
index 27c5de00..88fd9f2c 100644
--- a/queries/analytics/event/saveEvent.ts
+++ b/queries/analytics/event/saveEvent.ts
@@ -9,8 +9,8 @@ import { saveEventData } from '../eventData/saveEventData';
export async function saveEvent(args: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
pageTitle?: string;
eventName?: string;
eventData?: any;
@@ -34,21 +34,21 @@ export async function saveEvent(args: {
async function relationalQuery(data: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
pageTitle?: string;
eventName?: string;
eventData?: any;
}) {
- const { websiteId, id: sessionId, url, eventName, referrer, pageTitle } = data;
+ const { websiteId, id: sessionId, urlPath, urlQuery, eventName, pageTitle } = data;
return prisma.client.websiteEvent.create({
data: {
id: uuid(),
websiteId,
sessionId,
- url: url?.substring(0, URL_LENGTH),
- referrer: referrer?.substring(0, URL_LENGTH),
+ urlPath: urlPath?.substring(0, URL_LENGTH),
+ urlQuery: urlQuery?.substring(0, URL_LENGTH),
pageTitle: pageTitle,
eventType: EVENT_TYPE.customEvent,
eventName: eventName?.substring(0, EVENT_NAME_LENGTH),
@@ -59,8 +59,8 @@ async function relationalQuery(data: {
async function clickhouseQuery(data: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
pageTitle?: string;
eventName?: string;
eventData?: any;
@@ -78,7 +78,8 @@ async function clickhouseQuery(data: {
const {
websiteId,
id: sessionId,
- url,
+ urlPath,
+ urlQuery,
pageTitle,
eventName,
eventData,
@@ -101,7 +102,8 @@ async function clickhouseQuery(data: {
subdivision1: subdivision1 ? subdivision1 : null,
subdivision2: subdivision2 ? subdivision2 : null,
city: city ? city : null,
- url: url?.substring(0, URL_LENGTH),
+ urlPath: urlPath?.substring(0, URL_LENGTH),
+ urlQuery: urlQuery?.substring(0, URL_LENGTH),
page_title: pageTitle,
event_type: EVENT_TYPE.customEvent,
event_name: eventName?.substring(0, EVENT_NAME_LENGTH),
diff --git a/queries/analytics/pageview/getPageviewStats.ts b/queries/analytics/pageview/getPageviewStats.ts
index b2d86b33..273151aa 100644
--- a/queries/analytics/pageview/getPageviewStats.ts
+++ b/queries/analytics/pageview/getPageviewStats.ts
@@ -50,7 +50,7 @@ async function relationalQuery(
const { filterQuery, joinSession } = parseFilters(filters, params);
return rawQuery(
- `select ${getDateQuery('website_event.created_at', unit, timezone)} t,
+ `select ${getDateQuery('website_event.created_at', unit, timezone)} x,
count(${count !== '*' ? `${count}${sessionKey}` : count}) y
from website_event
${joinSession}
@@ -83,7 +83,7 @@ async function clickhouseQuery(
return rawQuery(
`select
- ${getDateStringQuery('g.t', unit)} as t,
+ ${getDateStringQuery('g.t', unit)} as x,
g.y as y
from
(select
diff --git a/queries/analytics/pageview/savePageView.ts b/queries/analytics/pageview/savePageView.ts
index 845a73ad..2122f51d 100644
--- a/queries/analytics/pageview/savePageView.ts
+++ b/queries/analytics/pageview/savePageView.ts
@@ -8,8 +8,11 @@ import { uuid } from 'lib/crypto';
export async function savePageView(args: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
+ referrerPath?: string;
+ referrerQuery?: string;
+ referrerDomain?: string;
pageTitle?: string;
hostname?: string;
browser?: string;
@@ -31,19 +34,34 @@ export async function savePageView(args: {
async function relationalQuery(data: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
+ referrerPath?: string;
+ referrerQuery?: string;
+ referrerDomain?: string;
pageTitle?: string;
}) {
- const { websiteId, id: sessionId, url, referrer, pageTitle } = data;
+ const {
+ websiteId,
+ id: sessionId,
+ urlPath,
+ urlQuery,
+ referrerPath,
+ referrerQuery,
+ referrerDomain,
+ pageTitle,
+ } = data;
return prisma.client.websiteEvent.create({
data: {
id: uuid(),
websiteId,
sessionId,
- url: url?.substring(0, URL_LENGTH),
- referrer: referrer?.substring(0, URL_LENGTH),
+ urlPath: urlPath?.substring(0, URL_LENGTH),
+ urlQuery: urlQuery?.substring(0, URL_LENGTH),
+ referrerPath: referrerPath?.substring(0, URL_LENGTH),
+ referrerQuery: referrerQuery?.substring(0, URL_LENGTH),
+ referrerDomain: referrerDomain?.substring(0, URL_LENGTH),
pageTitle: pageTitle,
eventType: EVENT_TYPE.pageView,
},
@@ -53,8 +71,11 @@ async function relationalQuery(data: {
async function clickhouseQuery(data: {
id: string;
websiteId: string;
- url: string;
- referrer?: string;
+ urlPath: string;
+ urlQuery?: string;
+ referrerPath?: string;
+ referrerQuery?: string;
+ referrerDomain?: string;
pageTitle?: string;
hostname?: string;
browser?: string;
@@ -70,8 +91,11 @@ async function clickhouseQuery(data: {
const {
websiteId,
id: sessionId,
- url,
- referrer,
+ urlPath,
+ urlQuery,
+ referrerPath,
+ referrerQuery,
+ referrerDomain,
pageTitle,
country,
subdivision1,
@@ -91,8 +115,11 @@ async function clickhouseQuery(data: {
subdivision1: subdivision1 ? subdivision1 : null,
subdivision2: subdivision2 ? subdivision2 : null,
city: city ? city : null,
- url: url?.substring(0, URL_LENGTH),
- referrer: referrer?.substring(0, URL_LENGTH),
+ urlPath: urlPath?.substring(0, URL_LENGTH),
+ urlQuery: urlQuery?.substring(0, URL_LENGTH),
+ referrerPath: referrerPath?.substring(0, URL_LENGTH),
+ referrerQuery: referrerQuery?.substring(0, URL_LENGTH),
+ referrerDomain: referrerDomain?.substring(0, URL_LENGTH),
page_title: pageTitle,
event_type: EVENT_TYPE.pageView,
created_at: getDateFormat(new Date()),
diff --git a/rollup.tracker.config.js b/rollup.tracker.config.js
index 07b1e00c..f4e7223c 100644
--- a/rollup.tracker.config.js
+++ b/rollup.tracker.config.js
@@ -6,7 +6,7 @@ import { terser } from 'rollup-plugin-terser';
export default {
input: 'tracker/index.js',
output: {
- file: 'public/umami.js',
+ file: 'public/script.js',
format: 'iife',
},
plugins: [
diff --git a/yarn.lock b/yarn.lock
index 55d0b6e3..ede5b85e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1760,6 +1760,11 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@kurkle/color@^0.3.0":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.2.tgz#5acd38242e8bde4f9986e7913c8fdf49d3aa199f"
+ integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
+
"@netlify/esbuild-android-64@0.14.39":
version "0.14.39"
resolved "https://registry.yarnpkg.com/@netlify/esbuild-android-64/-/esbuild-android-64-0.14.39.tgz#7bd30aba94a92351d2c5e25e178ceb824f3c2f99"
@@ -3393,28 +3398,17 @@ chalk@^4.0.0, chalk@^4.1.1, chalk@^4.1.2:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-chart.js@^2.9.4:
- version "2.9.4"
- resolved "https://registry.npmjs.org/chart.js/-/chart.js-2.9.4.tgz"
- integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A==
+chart.js@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.2.1.tgz#d2bd5c98e9a0ae35408975b638f40513b067ba1d"
+ integrity sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==
dependencies:
- chartjs-color "^2.1.0"
- moment "^2.10.2"
+ "@kurkle/color" "^0.3.0"
-chartjs-color-string@^0.6.0:
- version "0.6.0"
- resolved "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz"
- integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==
- dependencies:
- color-name "^1.0.0"
-
-chartjs-color@^2.1.0:
- version "2.4.1"
- resolved "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz"
- integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==
- dependencies:
- chartjs-color-string "^0.6.0"
- color-convert "^1.9.3"
+chartjs-adapter-date-fns@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz#c25f63c7f317c1f96f9a7c44bd45eeedb8a478e5"
+ integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==
chokidar@^3.5.3:
version "3.5.3"
@@ -3509,7 +3503,7 @@ cluster-key-slot@^1.1.0:
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
-color-convert@^1.9.0, color-convert@^1.9.3:
+color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -6177,7 +6171,7 @@ moment-timezone@^0.5.35:
dependencies:
moment "^2.29.4"
-"moment@>= 2.9.0", moment@^2.10.2, moment@^2.29.4:
+"moment@>= 2.9.0", moment@^2.29.4:
version "2.29.4"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
@@ -7142,10 +7136,10 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-basics@^0.71.0:
- version "0.71.0"
- resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.71.0.tgz#317ab58cdbadd4ba36b233cc64dec2a64fe0f1b8"
- integrity sha512-DLhx9bweJz2JG0lETnRrjQNeLL/pmyBqd0SFLM+VXaw8+6SnFheVhQ1Q/W8UerNRsN2oLGH4Hg1XuULG0JlrgA==
+react-basics@^0.73.0:
+ version "0.73.0"
+ resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.73.0.tgz#9555563f3407ac417dc833dfca47588123d55535"
+ integrity sha512-eEK8yWWrXO7JATBlPKBfFQlD1hNZoNeEtlYNx+QjOCLKu1qjClutP5nXWHmX4gHE97XFwUKzbTU35NkNEy5C0w==
dependencies:
classnames "^2.3.1"
date-fns "^2.29.3"
@@ -7258,14 +7252,6 @@ react-spring@^9.5.5:
"@react-spring/web" "~9.5.5"
"@react-spring/zdog" "~9.5.5"
-react-tooltip@^4.2.21:
- version "4.5.1"
- resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.5.1.tgz#77eccccdf16adec804132e558ec20ca5783b866a"
- integrity sha512-Zo+CSFUGXar1uV+bgXFFDe7VeS2iByeIp5rTgTcc2HqtuOS5D76QapejNNfx320MCY91TlhTQat36KGFTqgcvw==
- dependencies:
- prop-types "^15.8.1"
- uuid "^7.0.3"
-
react-use-measure@^2.0.4:
version "2.1.1"
resolved "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.1.tgz"
@@ -8641,11 +8627,6 @@ uuid@3.4.0, uuid@^3.3.2:
resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-uuid@^7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
- integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
-
uuid@^8.3.0, uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"