diff --git a/db/clickhouse/migrations/04_add_tag.sql b/db/clickhouse/migrations/04_add_tag.sql
index cfe0de20..7ffc4995 100644
--- a/db/clickhouse/migrations/04_add_tag.sql
+++ b/db/clickhouse/migrations/04_add_tag.sql
@@ -1,6 +1,6 @@
-- add tag column
ALTER TABLE umami.website_event ADD COLUMN "tag" String AFTER "event_name";
-ALTER TABLE umami.website_event_stats_hourly ADD COLUMN "tag" String AFTER "max_time";
+ALTER TABLE umami.website_event_stats_hourly ADD COLUMN "tag" SimpleAggregateFunction(groupArrayArray, Array(String)) AFTER "max_time";
-- update materialized view
DROP TABLE umami.website_event_stats_hourly_mv;
@@ -58,7 +58,7 @@ FROM (SELECT
sumIf(1, event_type = 1) views,
min(created_at) min_time,
max(created_at) max_time,
- tag,
+ arrayFilter(x -> x != '', groupArray(tag)) tag,
toStartOfHour(created_at) timestamp
FROM umami.website_event
GROUP BY website_id,
@@ -74,5 +74,4 @@ GROUP BY website_id,
subdivision1,
city,
event_type,
- tag,
timestamp);
\ No newline at end of file
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx
index 86a7717f..95e718b4 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx
@@ -1,20 +1,21 @@
-import { Icons, Icon, Text, Dropdown, Item } from 'react-basics';
+import LinkButton from 'components/common/LinkButton';
+import { useLocale, useMessages, useNavigation } from 'components/hooks';
+import SideNav from 'components/layout/SideNav';
import BrowsersTable from 'components/metrics/BrowsersTable';
-import CountriesTable from 'components/metrics/CountriesTable';
-import RegionsTable from 'components/metrics/RegionsTable';
import CitiesTable from 'components/metrics/CitiesTable';
+import CountriesTable from 'components/metrics/CountriesTable';
import DevicesTable from 'components/metrics/DevicesTable';
+import EventsTable from 'components/metrics/EventsTable';
+import HostsTable from 'components/metrics/HostsTable';
import LanguagesTable from 'components/metrics/LanguagesTable';
import OSTable from 'components/metrics/OSTable';
import PagesTable from 'components/metrics/PagesTable';
import QueryParametersTable from 'components/metrics/QueryParametersTable';
import ReferrersTable from 'components/metrics/ReferrersTable';
-import HostsTable from 'components/metrics/HostsTable';
+import RegionsTable from 'components/metrics/RegionsTable';
import ScreenTable from 'components/metrics/ScreenTable';
-import EventsTable from 'components/metrics/EventsTable';
-import SideNav from 'components/layout/SideNav';
-import { useNavigation, useMessages, useLocale } from 'components/hooks';
-import LinkButton from 'components/common/LinkButton';
+import TagsTable from 'components/metrics/TagsTable';
+import { Dropdown, Icon, Icons, Item, Text } from 'react-basics';
import styles from './WebsiteExpandedView.module.css';
const views = {
@@ -34,6 +35,7 @@ const views = {
language: LanguagesTable,
event: EventsTable,
query: QueryParametersTable,
+ tag: TagsTable,
};
export default function WebsiteExpandedView({
@@ -117,6 +119,11 @@ export default function WebsiteExpandedView({
label: formatMessage(labels.hosts),
url: renderUrl({ view: 'host' }),
},
+ {
+ key: 'tag',
+ label: formatMessage(labels.tags),
+ url: renderUrl({ view: 'tag' }),
+ },
];
const DetailsComponent = views[view] || (() => null);
diff --git a/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx
index 1b21103d..af5a06d4 100644
--- a/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx
+++ b/src/app/(main)/websites/[websiteId]/compare/WebsiteCompareTables.tsx
@@ -1,24 +1,25 @@
-import { useState } from 'react';
-import SideNav from 'components/layout/SideNav';
import { useDateRange, useMessages, useNavigation } from 'components/hooks';
-import PagesTable from 'components/metrics/PagesTable';
-import ReferrersTable from 'components/metrics/ReferrersTable';
-import BrowsersTable from 'components/metrics/BrowsersTable';
-import OSTable from 'components/metrics/OSTable';
-import DevicesTable from 'components/metrics/DevicesTable';
-import ScreenTable from 'components/metrics/ScreenTable';
-import CountriesTable from 'components/metrics/CountriesTable';
-import RegionsTable from 'components/metrics/RegionsTable';
-import CitiesTable from 'components/metrics/CitiesTable';
-import LanguagesTable from 'components/metrics/LanguagesTable';
-import EventsTable from 'components/metrics/EventsTable';
-import QueryParametersTable from 'components/metrics/QueryParametersTable';
import { Grid, GridRow } from 'components/layout/Grid';
+import SideNav from 'components/layout/SideNav';
+import BrowsersTable from 'components/metrics/BrowsersTable';
+import ChangeLabel from 'components/metrics/ChangeLabel';
+import CitiesTable from 'components/metrics/CitiesTable';
+import CountriesTable from 'components/metrics/CountriesTable';
+import DevicesTable from 'components/metrics/DevicesTable';
+import EventsTable from 'components/metrics/EventsTable';
+import LanguagesTable from 'components/metrics/LanguagesTable';
import MetricsTable from 'components/metrics/MetricsTable';
-import useStore from 'store/websites';
+import OSTable from 'components/metrics/OSTable';
+import PagesTable from 'components/metrics/PagesTable';
+import QueryParametersTable from 'components/metrics/QueryParametersTable';
+import ReferrersTable from 'components/metrics/ReferrersTable';
+import RegionsTable from 'components/metrics/RegionsTable';
+import ScreenTable from 'components/metrics/ScreenTable';
+import TagsTable from 'components/metrics/TagsTable';
import { getCompareDate } from 'lib/date';
import { formatNumber } from 'lib/format';
-import ChangeLabel from 'components/metrics/ChangeLabel';
+import { useState } from 'react';
+import useStore from 'store/websites';
import styles from './WebsiteCompareTables.module.css';
const views = {
@@ -35,6 +36,7 @@ const views = {
language: LanguagesTable,
event: EventsTable,
query: QueryParametersTable,
+ tag: TagsTable,
};
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
@@ -109,6 +111,16 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
label: formatMessage(labels.queryParameters),
url: renderUrl({ view: 'query' }),
},
+ {
+ key: 'host',
+ label: formatMessage(labels.hosts),
+ url: renderUrl({ view: 'host' }),
+ },
+ {
+ key: 'tag',
+ label: formatMessage(labels.tags),
+ url: renderUrl({ view: 'tag' }),
+ },
];
const renderChange = ({ x, y }) => {
diff --git a/src/components/hooks/useFields.ts b/src/components/hooks/useFields.ts
index e6fc54b3..859ca1ce 100644
--- a/src/components/hooks/useFields.ts
+++ b/src/components/hooks/useFields.ts
@@ -15,6 +15,7 @@ export function useFields() {
{ name: 'region', type: 'string', label: formatMessage(labels.region) },
{ name: 'city', type: 'string', label: formatMessage(labels.city) },
{ name: 'host', type: 'string', label: formatMessage(labels.host) },
+ { name: 'tag', type: 'string', label: formatMessage(labels.tag) },
];
return { fields };
diff --git a/src/components/hooks/useFilterParams.ts b/src/components/hooks/useFilterParams.ts
index 525f3492..55deed14 100644
--- a/src/components/hooks/useFilterParams.ts
+++ b/src/components/hooks/useFilterParams.ts
@@ -7,7 +7,21 @@ export function useFilterParams(websiteId: string) {
const { startDate, endDate, unit } = dateRange;
const { timezone, toUtc } = useTimezone();
const {
- query: { url, referrer, title, query, host, os, browser, device, country, region, city, event },
+ query: {
+ url,
+ referrer,
+ title,
+ query,
+ host,
+ os,
+ browser,
+ device,
+ country,
+ region,
+ city,
+ event,
+ tag,
+ },
} = useNavigation();
return {
@@ -27,5 +41,6 @@ export function useFilterParams(websiteId: string) {
region,
city,
event,
+ tag,
};
}
diff --git a/src/components/messages.ts b/src/components/messages.ts
index 1d2cc9b2..688dd11d 100644
--- a/src/components/messages.ts
+++ b/src/components/messages.ts
@@ -98,6 +98,7 @@ export const labels = defineMessages({
devices: { id: 'label.devices', defaultMessage: 'Devices' },
countries: { id: 'label.countries', defaultMessage: 'Countries' },
languages: { id: 'label.languages', defaultMessage: 'Languages' },
+ tags: { id: 'label.tags', defaultMessage: 'Tags' },
count: { id: 'label.count', defaultMessage: 'Count' },
average: { id: 'label.average', defaultMessage: 'Average' },
sum: { id: 'label.sum', defaultMessage: 'Sum' },
@@ -220,6 +221,7 @@ export const labels = defineMessages({
browser: { id: 'label.browser', defaultMessage: 'Browser' },
device: { id: 'label.device', defaultMessage: 'Device' },
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
+ tag: { id: 'label.tag', defaultMessage: 'Tag' },
day: { id: 'label.day', defaultMessage: 'Day' },
date: { id: 'label.date', defaultMessage: 'Date' },
pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' },
diff --git a/src/components/metrics/HostsTable.tsx b/src/components/metrics/HostsTable.tsx
index d3a0f3bf..45147eac 100644
--- a/src/components/metrics/HostsTable.tsx
+++ b/src/components/metrics/HostsTable.tsx
@@ -25,7 +25,7 @@ export function HostsTable(props: MetricsTableProps) {
{...props}
title={formatMessage(labels.hosts)}
type="host"
- metric={formatMessage(labels.views)}
+ metric={formatMessage(labels.visitors)}
renderLabel={renderLink}
/>
>
diff --git a/src/components/metrics/TagsTable.tsx b/src/components/metrics/TagsTable.tsx
new file mode 100644
index 00000000..a1130bb4
--- /dev/null
+++ b/src/components/metrics/TagsTable.tsx
@@ -0,0 +1,30 @@
+import MetricsTable, { MetricsTableProps } from './MetricsTable';
+import FilterLink from 'components/common/FilterLink';
+import { useMessages } from 'components/hooks';
+import { Flexbox } from 'react-basics';
+
+export function TagsTable(props: MetricsTableProps) {
+ const { formatMessage, labels } = useMessages();
+
+ const renderLink = ({ x: tag }) => {
+ return (
+
+
+
+ );
+ };
+
+ return (
+ <>
+
+ >
+ );
+}
+
+export default TagsTable;
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 5d3a9776..7f8acf88 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -33,7 +33,7 @@ export const FILTER_REFERRERS = 'filter-referrers';
export const FILTER_PAGES = 'filter-pages';
export const UNIT_TYPES = ['year', 'month', 'hour', 'day', 'minute'];
-export const EVENT_COLUMNS = ['url', 'entry', 'exit', 'referrer', 'title', 'query', 'event'];
+export const EVENT_COLUMNS = ['url', 'entry', 'exit', 'referrer', 'title', 'query', 'event', 'tag'];
export const SESSION_COLUMNS = [
'browser',
@@ -63,6 +63,7 @@ export const FILTER_COLUMNS = {
city: 'city',
language: 'language',
event: 'event_name',
+ tag: 'tag',
};
export const COLLECTION_TYPE = {
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 7fabbfc8..615882ef 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -179,6 +179,7 @@ export interface QueryFilters {
language?: string;
event?: string;
search?: string;
+ tag?: string;
}
export interface QueryOptions {
diff --git a/src/pages/api/websites/[websiteId]/events/series.ts b/src/pages/api/websites/[websiteId]/events/series.ts
index 08cade12..6d67a264 100644
--- a/src/pages/api/websites/[websiteId]/events/series.ts
+++ b/src/pages/api/websites/[websiteId]/events/series.ts
@@ -24,6 +24,7 @@ export interface WebsiteEventsRequestQuery {
country?: string;
region: string;
city?: string;
+ tag?: string;
}
const schema = {
@@ -43,6 +44,7 @@ const schema = {
country: yup.string(),
region: yup.string(),
city: yup.string(),
+ tag: yup.string(),
}),
};
diff --git a/src/pages/api/websites/[websiteId]/metrics.ts b/src/pages/api/websites/[websiteId]/metrics.ts
index 3dac217b..1996a61a 100644
--- a/src/pages/api/websites/[websiteId]/metrics.ts
+++ b/src/pages/api/websites/[websiteId]/metrics.ts
@@ -29,6 +29,7 @@ export interface WebsiteMetricsRequestQuery {
limit?: number;
offset?: number;
search?: string;
+ tag?: string;
}
const schema = {
@@ -53,6 +54,7 @@ const schema = {
limit: yup.number(),
offset: yup.number(),
search: yup.string(),
+ tag: yup.string(),
}),
};
diff --git a/src/pages/api/websites/[websiteId]/pageviews.ts b/src/pages/api/websites/[websiteId]/pageviews.ts
index badb8a47..c3b6b797 100644
--- a/src/pages/api/websites/[websiteId]/pageviews.ts
+++ b/src/pages/api/websites/[websiteId]/pageviews.ts
@@ -25,6 +25,7 @@ export interface WebsitePageviewRequestQuery {
country?: string;
region: string;
city?: string;
+ tag?: string;
compare?: string;
}
@@ -45,6 +46,7 @@ const schema = {
country: yup.string(),
region: yup.string(),
city: yup.string(),
+ tag: yup.string(),
compare: yup.string(),
}),
};
diff --git a/src/pages/api/websites/[websiteId]/sessions/stats.ts b/src/pages/api/websites/[websiteId]/sessions/stats.ts
index a522bd6b..fe92ce6f 100644
--- a/src/pages/api/websites/[websiteId]/sessions/stats.ts
+++ b/src/pages/api/websites/[websiteId]/sessions/stats.ts
@@ -23,6 +23,7 @@ export interface WebsiteSessionStatsRequestQuery {
country?: string;
region?: string;
city?: string;
+ tag?: string;
}
const schema = {
@@ -42,6 +43,7 @@ const schema = {
country: yup.string(),
region: yup.string(),
city: yup.string(),
+ tag: yup.string(),
}),
};
diff --git a/src/pages/api/websites/[websiteId]/stats.ts b/src/pages/api/websites/[websiteId]/stats.ts
index 9ca84c74..dfc9198d 100644
--- a/src/pages/api/websites/[websiteId]/stats.ts
+++ b/src/pages/api/websites/[websiteId]/stats.ts
@@ -24,6 +24,7 @@ export interface WebsiteStatsRequestQuery {
country?: string;
region?: string;
city?: string;
+ tag?: string;
compare?: string;
}
@@ -44,6 +45,7 @@ const schema = {
country: yup.string(),
region: yup.string(),
city: yup.string(),
+ tag: yup.string(),
compare: yup.string(),
}),
};