Skip to content

Commit 79b4c13

Browse files
committed
Merge branch 'dev' into analytics
2 parents 9ca5ce1 + d44be46 commit 79b4c13

10 files changed

Lines changed: 81 additions & 40 deletions

File tree

next.config.js renamed to next.config.mjs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
/* eslint-disable @typescript-eslint/no-var-requires */
2-
require('dotenv').config();
1+
import dotenv from 'dotenv';
2+
import { createRequire } from 'module';
3+
4+
dotenv.config();
5+
6+
const require = createRequire(import.meta.url);
37
const pkg = require('./package.json');
48

59
const TRACKER_SCRIPT = '/script.js';
@@ -12,6 +16,7 @@ const corsMaxAge = process.env.CORS_MAX_AGE;
1216
const defaultLocale = process.env.DEFAULT_LOCALE;
1317
const disableLogin = process.env.DISABLE_LOGIN;
1418
const disableUI = process.env.DISABLE_UI;
19+
const faviconURL = process.env.FAVICON_URL;
1520
const forceSSL = process.env.FORCE_SSL;
1621
const frameAncestors = process.env.ALLOWED_FRAME_URLS;
1722
const privateMode = process.env.PRIVATE_MODE;
@@ -180,17 +185,17 @@ if (cloudMode && cloudUrl) {
180185
}
181186

182187
/** @type {import('next').NextConfig} */
183-
const config = {
188+
export default {
184189
reactStrictMode: false,
185190
env: {
186191
basePath,
187192
cloudMode,
188193
cloudUrl,
189-
configUrl: '/config',
190194
currentVersion: pkg.version,
191195
defaultLocale,
192196
disableLogin,
193197
disableUI,
198+
faviconURL,
194199
privateMode,
195200
},
196201
basePath,
@@ -237,5 +242,3 @@ const config = {
237242
return [...redirects];
238243
},
239244
};
240-
241-
module.exports = config;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "umami",
33
"version": "2.18.0",
4-
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
4+
"description": "A modern, privacy-focused alternative to Google Analytics.",
55
"author": "Umami Software, Inc. <hello@umami.is>",
66
"license": "MIT",
77
"homepage": "https://umami.is",

src/app/(main)/websites/[websiteId]/events/EventsPage.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,33 @@ import EventsDataTable from './EventsDataTable';
44
import EventsMetricsBar from './EventsMetricsBar';
55
import EventsChart from '@/components/metrics/EventsChart';
66
import { GridRow } from '@/components/layout/Grid';
7-
import MetricsTable from '@/components/metrics/MetricsTable';
7+
import EventsTable from '@/components/metrics/EventsTable';
88
import { useMessages } from '@/components/hooks';
99
import { Item, Tabs } from 'react-basics';
1010
import { useState } from 'react';
1111
import EventProperties from './EventProperties';
1212

1313
export default function EventsPage({ websiteId }) {
14+
const [label, setLabel] = useState(null);
1415
const [tab, setTab] = useState('activity');
1516
const { formatMessage, labels } = useMessages();
1617

18+
const handleLabelClick = (value: string) => {
19+
setLabel(value !== label ? value : '');
20+
};
21+
1722
return (
1823
<>
1924
<WebsiteHeader websiteId={websiteId} />
2025
<EventsMetricsBar websiteId={websiteId} />
2126
<GridRow columns="two-one">
22-
<EventsChart websiteId={websiteId} />
23-
<MetricsTable
27+
<EventsChart websiteId={websiteId} focusLabel={label} />
28+
<EventsTable
2429
websiteId={websiteId}
2530
type="event"
2631
title={formatMessage(labels.events)}
2732
metric={formatMessage(labels.actions)}
33+
onLabelClick={handleLabelClick}
2834
/>
2935
</GridRow>
3036
<div>

src/app/api/send/route.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export async function POST(request: Request) {
5555
title,
5656
tag,
5757
timestamp,
58-
id = '',
58+
id,
5959
} = payload;
6060

6161
// Cache check
@@ -101,7 +101,7 @@ export async function POST(request: Request) {
101101
const sessionSalt = hash(startOfMonth(createdAt).toUTCString());
102102
const visitSalt = hash(startOfHour(createdAt).toUTCString());
103103

104-
const sessionId = uuid(websiteId, ip, userAgent, sessionSalt, id);
104+
const sessionId = id ? uuid(websiteId, id) : uuid(websiteId, ip, userAgent, sessionSalt);
105105

106106
// Find session
107107
if (!clickhouse.enabled && !cache?.sessionId) {
@@ -148,6 +148,10 @@ export async function POST(request: Request) {
148148
const urlQuery = currentUrl.search.substring(1);
149149
const urlDomain = currentUrl.hostname.replace(/^www./, '');
150150

151+
let referrerPath: string;
152+
let referrerQuery: string;
153+
let referrerDomain: string;
154+
151155
// UTM Params
152156
const utmSource = currentUrl.searchParams.get('utm_source');
153157
const utmMedium = currentUrl.searchParams.get('utm_medium');
@@ -167,10 +171,6 @@ export async function POST(request: Request) {
167171
urlPath = urlPath.replace(/(.+)\/$/, '$1');
168172
}
169173

170-
let referrerPath: string;
171-
let referrerQuery: string;
172-
let referrerDomain: string;
173-
174174
if (referrer) {
175175
const referrerUrl = new URL(referrer, base);
176176

src/components/charts/Chart.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function Chart({
3434
className,
3535
chartOptions,
3636
}: ChartProps) {
37-
const canvas = useRef();
37+
const canvas = useRef(null);
3838
const chart = useRef(null);
3939
const [legendItems, setLegendItems] = useState([]);
4040

@@ -86,7 +86,7 @@ export function Chart({
8686
dataset.data = data?.datasets[index]?.data;
8787

8888
if (chart.current.legend.legendItems[index]) {
89-
chart.current.legend.legendItems[index].text = data?.datasets[index]?.label;
89+
chart.current.legend.legendItems[index].text = data.datasets[index]?.label;
9090
}
9191
}
9292
});
@@ -95,6 +95,12 @@ export function Chart({
9595
}
9696
}
9797

98+
if (data.focusLabel !== null) {
99+
chart.current.data.datasets.forEach(ds => {
100+
ds.hidden = data.focusLabel ? ds.label !== data.focusLabel : false;
101+
});
102+
}
103+
98104
chart.current.options = options;
99105

100106
// Allow config changes before update
@@ -105,16 +111,6 @@ export function Chart({
105111
setLegendItems(chart.current.legend.legendItems);
106112
};
107113

108-
useEffect(() => {
109-
if (data) {
110-
if (!chart.current) {
111-
createChart(data);
112-
} else {
113-
updateChart(data);
114-
}
115-
}
116-
}, [data, options]);
117-
118114
const handleLegendClick = (item: LegendItem) => {
119115
if (type === 'bar') {
120116
const { datasetIndex } = item;
@@ -136,6 +132,16 @@ export function Chart({
136132
setLegendItems(chart.current.legend.legendItems);
137133
};
138134

135+
useEffect(() => {
136+
if (data) {
137+
if (!chart.current) {
138+
createChart(data);
139+
} else {
140+
updateChart(data);
141+
}
142+
}
143+
}, [data, options]);
144+
139145
return (
140146
<>
141147
<div className={classNames(styles.chart, className)}>

src/components/common/Favicon.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GROUPED_DOMAINS } from '@/lib/constants';
1+
import { FAVICON_URL, GROUPED_DOMAINS } from '@/lib/constants';
22

33
function getHostName(url: string) {
44
const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?([^:/\n?=]+)/im);
@@ -10,10 +10,10 @@ export function Favicon({ domain, ...props }) {
1010
return null;
1111
}
1212

13+
const url = process.env.faviconURL || FAVICON_URL;
1314
const hostName = domain ? getHostName(domain) : null;
14-
const src = hostName
15-
? `https://icons.duckduckgo.com/ip3/${GROUPED_DOMAINS[hostName]?.domain || hostName}.ico`
16-
: null;
15+
const domainName = GROUPED_DOMAINS[hostName]?.domain || hostName;
16+
const src = hostName ? url.replace(/\{\{\s*domain\s*}}/, domainName) : null;
1717

1818
return hostName ? <img src={src} width={16} height={16} alt="" {...props} /> : null;
1919
}

src/components/metrics/EventsChart.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1+
import { useMemo, useState, useEffect } from 'react';
12
import { colord } from 'colord';
23
import BarChart from '@/components/charts/BarChart';
34
import { useDateRange, useLocale, useWebsiteEventsSeries } from '@/components/hooks';
45
import { renderDateLabels } from '@/lib/charts';
56
import { CHART_COLORS } from '@/lib/constants';
6-
import { useMemo } from 'react';
77

88
export interface EventsChartProps {
99
websiteId: string;
1010
className?: string;
11+
focusLabel?: string;
1112
}
1213

13-
export function EventsChart({ websiteId, className }: EventsChartProps) {
14+
export function EventsChart({ websiteId, className, focusLabel }: EventsChartProps) {
1415
const {
1516
dateRange: { startDate, endDate, unit, value },
1617
} = useDateRange(websiteId);
1718
const { locale } = useLocale();
1819
const { data, isLoading } = useWebsiteEventsSeries(websiteId);
20+
const [label, setLabel] = useState<string>(focusLabel);
1921

2022
const chartData = useMemo(() => {
2123
if (!data) return [];
@@ -42,8 +44,15 @@ export function EventsChart({ websiteId, className }: EventsChartProps) {
4244
borderWidth: 1,
4345
};
4446
}),
47+
focusLabel,
4548
};
46-
}, [data, startDate, endDate, unit]);
49+
}, [data, startDate, endDate, unit, focusLabel]);
50+
51+
useEffect(() => {
52+
if (label !== focusLabel) {
53+
setLabel(focusLabel);
54+
}
55+
}, [focusLabel]);
4756

4857
return (
4958
<BarChart

src/components/metrics/EventsTable.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
11
import MetricsTable, { MetricsTableProps } from './MetricsTable';
22
import { useMessages } from '@/components/hooks';
33

4-
export function EventsTable(props: MetricsTableProps) {
4+
export interface EventsTableProps extends MetricsTableProps {
5+
onLabelClick?: (value: string) => void;
6+
}
7+
8+
export function EventsTable({ onLabelClick, ...props }: EventsTableProps) {
59
const { formatMessage, labels } = useMessages();
610

7-
function handleDataLoad(data: any) {
11+
const handleDataLoad = (data: any) => {
812
props.onDataLoad?.(data);
9-
}
13+
};
14+
15+
const renderLabel = ({ x: label }) => {
16+
if (onLabelClick) {
17+
return (
18+
<div onClick={() => onLabelClick(label)} style={{ cursor: 'pointer' }}>
19+
{label}
20+
</div>
21+
);
22+
}
23+
24+
return label;
25+
};
1026

1127
return (
1228
<MetricsTable
@@ -15,6 +31,7 @@ export function EventsTable(props: MetricsTableProps) {
1531
type="event"
1632
metric={formatMessage(labels.actions)}
1733
onDataLoad={handleDataLoad}
34+
renderLabel={renderLabel}
1835
/>
1936
);
2037
}

src/lib/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable no-unused-vars */
21
export const CURRENT_VERSION = process.env.currentVersion;
32
export const AUTH_TOKEN = 'umami.auth';
43
export const LOCALE_CONFIG = 'umami.locale';
@@ -12,6 +11,7 @@ export const HOMEPAGE_URL = 'https://umami.is';
1211
export const REPO_URL = 'https://github.com/umami-software/umami';
1312
export const UPDATES_URL = 'https://api.umami.is/v1/updates';
1413
export const TELEMETRY_PIXEL = 'https://i.umami.is/a.png';
14+
export const FAVICON_URL = 'https://icons.duckduckgo.com/ip3/{{domain}}.ico';
1515

1616
export const DEFAULT_LOCALE = process.env.defaultLocale || 'en-US';
1717
export const DEFAULT_THEME = 'light';

src/queries/sql/events/getWebsiteEvents.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters, pagePar
5252
limit 1000)
5353
select * from events
5454
`,
55-
{ ...params, query: `%${search}%` },
55+
{ ...params, search: `%${search}%` },
5656
pageParams,
5757
);
5858
}

0 commit comments

Comments
 (0)