add tags table and filters logic

This commit is contained in:
Francis Cao 2024-10-15 16:46:57 -07:00
parent bffb98cd51
commit 8759ba9916
15 changed files with 108 additions and 30 deletions

View File

@ -1,6 +1,6 @@
-- add tag column -- add tag column
ALTER TABLE umami.website_event ADD COLUMN "tag" String AFTER "event_name"; 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 -- update materialized view
DROP TABLE umami.website_event_stats_hourly_mv; DROP TABLE umami.website_event_stats_hourly_mv;
@ -58,7 +58,7 @@ FROM (SELECT
sumIf(1, event_type = 1) views, sumIf(1, event_type = 1) views,
min(created_at) min_time, min(created_at) min_time,
max(created_at) max_time, max(created_at) max_time,
tag, arrayFilter(x -> x != '', groupArray(tag)) tag,
toStartOfHour(created_at) timestamp toStartOfHour(created_at) timestamp
FROM umami.website_event FROM umami.website_event
GROUP BY website_id, GROUP BY website_id,
@ -74,5 +74,4 @@ GROUP BY website_id,
subdivision1, subdivision1,
city, city,
event_type, event_type,
tag,
timestamp); timestamp);

View File

@ -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 BrowsersTable from 'components/metrics/BrowsersTable';
import CountriesTable from 'components/metrics/CountriesTable';
import RegionsTable from 'components/metrics/RegionsTable';
import CitiesTable from 'components/metrics/CitiesTable'; import CitiesTable from 'components/metrics/CitiesTable';
import CountriesTable from 'components/metrics/CountriesTable';
import DevicesTable from 'components/metrics/DevicesTable'; 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 LanguagesTable from 'components/metrics/LanguagesTable';
import OSTable from 'components/metrics/OSTable'; import OSTable from 'components/metrics/OSTable';
import PagesTable from 'components/metrics/PagesTable'; import PagesTable from 'components/metrics/PagesTable';
import QueryParametersTable from 'components/metrics/QueryParametersTable'; import QueryParametersTable from 'components/metrics/QueryParametersTable';
import ReferrersTable from 'components/metrics/ReferrersTable'; 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 ScreenTable from 'components/metrics/ScreenTable';
import EventsTable from 'components/metrics/EventsTable'; import TagsTable from 'components/metrics/TagsTable';
import SideNav from 'components/layout/SideNav'; import { Dropdown, Icon, Icons, Item, Text } from 'react-basics';
import { useNavigation, useMessages, useLocale } from 'components/hooks';
import LinkButton from 'components/common/LinkButton';
import styles from './WebsiteExpandedView.module.css'; import styles from './WebsiteExpandedView.module.css';
const views = { const views = {
@ -34,6 +35,7 @@ const views = {
language: LanguagesTable, language: LanguagesTable,
event: EventsTable, event: EventsTable,
query: QueryParametersTable, query: QueryParametersTable,
tag: TagsTable,
}; };
export default function WebsiteExpandedView({ export default function WebsiteExpandedView({
@ -117,6 +119,11 @@ export default function WebsiteExpandedView({
label: formatMessage(labels.hosts), label: formatMessage(labels.hosts),
url: renderUrl({ view: 'host' }), url: renderUrl({ view: 'host' }),
}, },
{
key: 'tag',
label: formatMessage(labels.tags),
url: renderUrl({ view: 'tag' }),
},
]; ];
const DetailsComponent = views[view] || (() => null); const DetailsComponent = views[view] || (() => null);

View File

@ -1,24 +1,25 @@
import { useState } from 'react';
import SideNav from 'components/layout/SideNav';
import { useDateRange, useMessages, useNavigation } from 'components/hooks'; 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 { 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 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 { getCompareDate } from 'lib/date';
import { formatNumber } from 'lib/format'; 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'; import styles from './WebsiteCompareTables.module.css';
const views = { const views = {
@ -35,6 +36,7 @@ const views = {
language: LanguagesTable, language: LanguagesTable,
event: EventsTable, event: EventsTable,
query: QueryParametersTable, query: QueryParametersTable,
tag: TagsTable,
}; };
export function WebsiteCompareTables({ websiteId }: { websiteId: string }) { export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
@ -109,6 +111,16 @@ export function WebsiteCompareTables({ websiteId }: { websiteId: string }) {
label: formatMessage(labels.queryParameters), label: formatMessage(labels.queryParameters),
url: renderUrl({ view: 'query' }), 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 }) => { const renderChange = ({ x, y }) => {

View File

@ -15,6 +15,7 @@ export function useFields() {
{ name: 'region', type: 'string', label: formatMessage(labels.region) }, { name: 'region', type: 'string', label: formatMessage(labels.region) },
{ name: 'city', type: 'string', label: formatMessage(labels.city) }, { name: 'city', type: 'string', label: formatMessage(labels.city) },
{ name: 'host', type: 'string', label: formatMessage(labels.host) }, { name: 'host', type: 'string', label: formatMessage(labels.host) },
{ name: 'tag', type: 'string', label: formatMessage(labels.tag) },
]; ];
return { fields }; return { fields };

View File

@ -7,7 +7,21 @@ export function useFilterParams(websiteId: string) {
const { startDate, endDate, unit } = dateRange; const { startDate, endDate, unit } = dateRange;
const { timezone, toUtc } = useTimezone(); const { timezone, toUtc } = useTimezone();
const { 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(); } = useNavigation();
return { return {
@ -27,5 +41,6 @@ export function useFilterParams(websiteId: string) {
region, region,
city, city,
event, event,
tag,
}; };
} }

View File

@ -98,6 +98,7 @@ export const labels = defineMessages({
devices: { id: 'label.devices', defaultMessage: 'Devices' }, devices: { id: 'label.devices', defaultMessage: 'Devices' },
countries: { id: 'label.countries', defaultMessage: 'Countries' }, countries: { id: 'label.countries', defaultMessage: 'Countries' },
languages: { id: 'label.languages', defaultMessage: 'Languages' }, languages: { id: 'label.languages', defaultMessage: 'Languages' },
tags: { id: 'label.tags', defaultMessage: 'Tags' },
count: { id: 'label.count', defaultMessage: 'Count' }, count: { id: 'label.count', defaultMessage: 'Count' },
average: { id: 'label.average', defaultMessage: 'Average' }, average: { id: 'label.average', defaultMessage: 'Average' },
sum: { id: 'label.sum', defaultMessage: 'Sum' }, sum: { id: 'label.sum', defaultMessage: 'Sum' },
@ -220,6 +221,7 @@ export const labels = defineMessages({
browser: { id: 'label.browser', defaultMessage: 'Browser' }, browser: { id: 'label.browser', defaultMessage: 'Browser' },
device: { id: 'label.device', defaultMessage: 'Device' }, device: { id: 'label.device', defaultMessage: 'Device' },
pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' }, pageTitle: { id: 'label.pageTitle', defaultMessage: 'Page title' },
tag: { id: 'label.tag', defaultMessage: 'Tag' },
day: { id: 'label.day', defaultMessage: 'Day' }, day: { id: 'label.day', defaultMessage: 'Day' },
date: { id: 'label.date', defaultMessage: 'Date' }, date: { id: 'label.date', defaultMessage: 'Date' },
pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' }, pageOf: { id: 'label.page-of', defaultMessage: 'Page {current} of {total}' },

View File

@ -25,7 +25,7 @@ export function HostsTable(props: MetricsTableProps) {
{...props} {...props}
title={formatMessage(labels.hosts)} title={formatMessage(labels.hosts)}
type="host" type="host"
metric={formatMessage(labels.views)} metric={formatMessage(labels.visitors)}
renderLabel={renderLink} renderLabel={renderLink}
/> />
</> </>

View File

@ -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 (
<Flexbox alignItems="center">
<FilterLink id="tag" value={tag} label={!tag && formatMessage(labels.none)} />
</Flexbox>
);
};
return (
<>
<MetricsTable
{...props}
title={formatMessage(labels.tags)}
type="tag"
metric={formatMessage(labels.views)}
renderLabel={renderLink}
/>
</>
);
}
export default TagsTable;

View File

@ -33,7 +33,7 @@ export const FILTER_REFERRERS = 'filter-referrers';
export const FILTER_PAGES = 'filter-pages'; export const FILTER_PAGES = 'filter-pages';
export const UNIT_TYPES = ['year', 'month', 'hour', 'day', 'minute']; 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 = [ export const SESSION_COLUMNS = [
'browser', 'browser',
@ -63,6 +63,7 @@ export const FILTER_COLUMNS = {
city: 'city', city: 'city',
language: 'language', language: 'language',
event: 'event_name', event: 'event_name',
tag: 'tag',
}; };
export const COLLECTION_TYPE = { export const COLLECTION_TYPE = {

View File

@ -179,6 +179,7 @@ export interface QueryFilters {
language?: string; language?: string;
event?: string; event?: string;
search?: string; search?: string;
tag?: string;
} }
export interface QueryOptions { export interface QueryOptions {

View File

@ -24,6 +24,7 @@ export interface WebsiteEventsRequestQuery {
country?: string; country?: string;
region: string; region: string;
city?: string; city?: string;
tag?: string;
} }
const schema = { const schema = {
@ -43,6 +44,7 @@ const schema = {
country: yup.string(), country: yup.string(),
region: yup.string(), region: yup.string(),
city: yup.string(), city: yup.string(),
tag: yup.string(),
}), }),
}; };

View File

@ -29,6 +29,7 @@ export interface WebsiteMetricsRequestQuery {
limit?: number; limit?: number;
offset?: number; offset?: number;
search?: string; search?: string;
tag?: string;
} }
const schema = { const schema = {
@ -53,6 +54,7 @@ const schema = {
limit: yup.number(), limit: yup.number(),
offset: yup.number(), offset: yup.number(),
search: yup.string(), search: yup.string(),
tag: yup.string(),
}), }),
}; };

View File

@ -25,6 +25,7 @@ export interface WebsitePageviewRequestQuery {
country?: string; country?: string;
region: string; region: string;
city?: string; city?: string;
tag?: string;
compare?: string; compare?: string;
} }
@ -45,6 +46,7 @@ const schema = {
country: yup.string(), country: yup.string(),
region: yup.string(), region: yup.string(),
city: yup.string(), city: yup.string(),
tag: yup.string(),
compare: yup.string(), compare: yup.string(),
}), }),
}; };

View File

@ -23,6 +23,7 @@ export interface WebsiteSessionStatsRequestQuery {
country?: string; country?: string;
region?: string; region?: string;
city?: string; city?: string;
tag?: string;
} }
const schema = { const schema = {
@ -42,6 +43,7 @@ const schema = {
country: yup.string(), country: yup.string(),
region: yup.string(), region: yup.string(),
city: yup.string(), city: yup.string(),
tag: yup.string(),
}), }),
}; };

View File

@ -24,6 +24,7 @@ export interface WebsiteStatsRequestQuery {
country?: string; country?: string;
region?: string; region?: string;
city?: string; city?: string;
tag?: string;
compare?: string; compare?: string;
} }
@ -44,6 +45,7 @@ const schema = {
country: yup.string(), country: yup.string(),
region: yup.string(), region: yup.string(),
city: yup.string(), city: yup.string(),
tag: yup.string(),
compare: yup.string(), compare: yup.string(),
}), }),
}; };