Skip to content

Commit dce07a1

Browse files
author
Dmytro Radchuk
committed
Add UTM params support
1 parent 2b6ab8b commit dce07a1

File tree

7 files changed

+102
-17
lines changed

7 files changed

+102
-17
lines changed

dashboard/components/TopSourcesWidget/TopSourcesWidget.tsx

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
1-
import { BarList } from '@tremor/react'
1+
import { BarList, Flex, SelectBox, SelectBoxItem } from '@tremor/react'
22
import Widget from '../Widget'
33
import useTopSources from '../../lib/hooks/use-top-sources'
4+
import { OptionType } from '../../lib/types/options'
5+
import { UTMFilter } from '../../lib/types/top-sources'
46
import { formatNumber } from '../../lib/utils'
57
import { useMemo } from 'react'
68

9+
const utmFilterOptions: OptionType<UTMFilter>[] = [
10+
{ text: 'All', value: UTMFilter.All },
11+
{ text: 'UTM Medium', value: UTMFilter.Medium },
12+
{ text: 'UTM Source', value: UTMFilter.Source },
13+
{ text: 'UTM Campaign', value: UTMFilter.Campaign },
14+
{ text: 'UTM Content', value: UTMFilter.Content },
15+
{ text: 'UTM Term', value: UTMFilter.Term },
16+
]
17+
718
export default function TopSourcesWidget() {
8-
const { data, status, warning } = useTopSources()
19+
const { utmFilter, setUtmFilter, data, status, warning } = useTopSources()
20+
921
const chartData = useMemo(
1022
() =>
1123
(data?.data ?? []).map(d => ({
12-
name: d.referrer,
24+
name: (d.referrer || d.utm_filter) as string,
1325
value: d.visits,
1426
href: d.href,
1527
})),
@@ -18,7 +30,18 @@ export default function TopSourcesWidget() {
1830

1931
return (
2032
<Widget>
21-
<Widget.Title>Top Sources</Widget.Title>
33+
<Flex justifyContent="justify-between">
34+
<Widget.Title>Top Sources</Widget.Title>
35+
<SelectBox
36+
maxWidth="max-w-fit"
37+
value={utmFilter}
38+
onValueChange={setUtmFilter}
39+
>
40+
{utmFilterOptions.map(({ value, text }) => (
41+
<SelectBoxItem key={value} value={value} text={text} />
42+
))}
43+
</SelectBox>
44+
</Flex>
2245
<Widget.Content
2346
status={status}
2447
noData={!chartData?.length}
Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
1+
import { useState } from 'react'
12
import { queryPipe } from '../api'
2-
import { TopSource, TopSources } from '../types/top-sources'
3+
import { TopSource, TopSources, UTMFilter } from '../types/top-sources'
34
import useDateFilter from './use-date-filter'
45
import useQuery from './use-query'
56

67
async function getTopSources(
78
date_from?: string,
8-
date_to?: string
9+
date_to?: string,
10+
utm_filter?: UTMFilter
911
): Promise<TopSources> {
1012
const { data: queryData } = await queryPipe<TopSource>('top_sources', {
1113
limit: 8,
1214
date_from,
1315
date_to,
16+
utm_filter,
1417
})
1518

1619
const data: TopSource[] = [...queryData]
1720
.sort((a, b) => b.visits - a.visits)
18-
.map(({ referrer, visits }) => ({
19-
referrer: referrer || 'Direct',
21+
.map(({ referrer, utm_filter, visits }) => ({
22+
referrer: referrer || utm_filter || 'Direct',
2023
href: referrer ? `https://${referrer}` : undefined,
2124
visits,
2225
}))
23-
const refs = data.map(({ referrer }) => referrer)
26+
const refs = data.map(
27+
({ referrer, utm_filter }) => (referrer ?? utm_filter) as string
28+
)
2429
const visits = data.map(({ visits }) => visits)
2530

2631
return {
@@ -32,5 +37,16 @@ async function getTopSources(
3237

3338
export default function useTopSources() {
3439
const { startDate, endDate } = useDateFilter()
35-
return useQuery([startDate, endDate, 'topSources'], getTopSources)
40+
const [utmFilter, setUtmFilter] = useState<UTMFilter>(UTMFilter.All)
41+
const query = useQuery(
42+
[
43+
startDate,
44+
endDate,
45+
utmFilter === UTMFilter.All ? undefined : utmFilter,
46+
'topSources',
47+
],
48+
getTopSources
49+
)
50+
51+
return { utmFilter, setUtmFilter, ...query }
3652
}

dashboard/lib/types/top-sources.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export type TopSource = {
2-
referrer: string
2+
referrer?: string
3+
utm_filter?: string
34
visits: number
45
href?: string
56
}
@@ -9,3 +10,12 @@ export type TopSources = {
910
refs: string[]
1011
visits: number[]
1112
}
13+
14+
export enum UTMFilter {
15+
All = 'all',
16+
Source = 'utm_source',
17+
Medium = 'utm_medium',
18+
Campaign = 'utm_campaign',
19+
Term = 'utm_term',
20+
Content = 'utm_content',
21+
}

middleware/src/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(function(){
22
const timezones = {"Asia/Barnaul":"RU","Africa/Nouakchott":"MR","Africa/Lusaka":"ZM","Asia/Pyongyang":"KP","Europe/Bratislava":"SK","America/Belize":"BZ","America/Maceio":"BR","Pacific/Chuuk":"FM","Indian/Comoro":"KM","Pacific/Palau":"PW","Asia/Jakarta":"ID","Africa/Windhoek":"NA","America/Chihuahua":"MX","America/Nome":"US","Africa/Mbabane":"SZ","Africa/Porto-Novo":"BJ","Europe/San_Marino":"SM","Pacific/Fakaofo":"TK","America/Denver":"US","Europe/Belgrade":"RS","America/Indiana/Tell_City":"US","America/Fortaleza":"BR","America/Halifax":"CA","Europe/Bucharest":"RO","America/Indiana/Petersburg":"US","Europe/Kirov":"RU","Europe/Athens":"GR","America/Argentina/Ushuaia":"AR","Europe/Monaco":"MC","Europe/Vilnius":"LT","Europe/Copenhagen":"DK","Pacific/Kanton":"KI","America/Caracas":"VE","Asia/Almaty":"KZ","Europe/Paris":"FR","Africa/Blantyre":"MW","Asia/Muscat":"OM","America/North_Dakota/Beulah":"US","America/Matamoros":"MX","Asia/Irkutsk":"RU","America/Costa_Rica":"CR","America/Araguaina":"BR","Atlantic/Canary":"ES","America/Santo_Domingo":"DO","America/Vancouver":"CA","Africa/Addis_Ababa":"ET","Africa/Accra":"GH","Pacific/Kwajalein":"MH","Asia/Baghdad":"IQ","Australia/Adelaide":"AU","Australia/Hobart":"AU","America/Guayaquil":"EC","America/Argentina/Tucuman":"AR","Australia/Lindeman":"AU","America/New_York":"US","Pacific/Fiji":"FJ","America/Antigua":"AG","Africa/Casablanca":"MA","America/Paramaribo":"SR","Africa/Cairo":"EG","America/Cayenne":"GF","America/Detroit":"US","Antarctica/Syowa":"AQ","Africa/Douala":"CM","America/Argentina/La_Rioja":"AR","Africa/Lagos":"NG","America/St_Barthelemy":"BL","Asia/Nicosia":"CY","Asia/Macau":"MO","Europe/Riga":"LV","Asia/Ashgabat":"TM","Indian/Antananarivo":"MG","America/Argentina/San_Juan":"AR","Asia/Aden":"YE","Asia/Tomsk":"RU","America/Asuncion":"PY","Pacific/Bougainville":"PG","Asia/Vientiane":"LA","America/Mazatlan":"MX","Africa/Luanda":"AO","Europe/Oslo":"NO","Africa/Kinshasa":"CD","Europe/Warsaw":"PL","America/Grand_Turk":"TC","Asia/Seoul":"KR","Africa/Tripoli":"LY","America/St_Thomas":"VI","Asia/Kathmandu":"NP","Pacific/Pitcairn":"PN","Pacific/Nauru":"NR","America/Curacao":"CW","Asia/Kabul":"AF","Pacific/Tongatapu":"TO","Europe/Simferopol":"UA","Asia/Ust-Nera":"RU","Africa/Mogadishu":"SO","Indian/Mayotte":"YT","Pacific/Niue":"NU","America/Thunder_Bay":"CA","Atlantic/Azores":"PT","Pacific/Gambier":"PF","Europe/Stockholm":"SE","Africa/Libreville":"GA","America/Punta_Arenas":"CL","America/Guatemala":"GT","America/Noronha":"BR","Europe/Helsinki":"FI","Asia/Gaza":"PS","Pacific/Kosrae":"FM","America/Aruba":"AW","America/Nassau":"BS","Asia/Choibalsan":"MN","America/Winnipeg":"CA","America/Anguilla":"AI","Asia/Thimphu":"BT","Asia/Beirut":"LB","Atlantic/Faroe":"FO","Europe/Berlin":"DE","Europe/Amsterdam":"NL","Pacific/Honolulu":"US","America/Regina":"CA","America/Scoresbysund":"GL","Europe/Vienna":"AT","Europe/Tirane":"AL","Africa/El_Aaiun":"EH","America/Creston":"CA","Asia/Qostanay":"KZ","Asia/Ho_Chi_Minh":"VN","Europe/Samara":"RU","Europe/Rome":"IT","Australia/Eucla":"AU","America/El_Salvador":"SV","America/Chicago":"US","Africa/Abidjan":"CI","Asia/Kamchatka":"RU","Pacific/Tarawa":"KI","America/Santiago":"CL","America/Bahia":"BR","Indian/Christmas":"CX","Asia/Atyrau":"KZ","Asia/Dushanbe":"TJ","Europe/Ulyanovsk":"RU","America/Yellowknife":"CA","America/Recife":"BR","Australia/Sydney":"AU","America/Fort_Nelson":"CA","Pacific/Efate":"VU","Europe/Saratov":"RU","Africa/Banjul":"GM","Asia/Omsk":"RU","Europe/Ljubljana":"SI","Europe/Budapest":"HU","Europe/Astrakhan":"RU","America/Argentina/Buenos_Aires":"AR","Pacific/Chatham":"NZ","America/Argentina/Salta":"AR","Africa/Niamey":"NE","Asia/Pontianak":"ID","Indian/Reunion":"RE","Asia/Hong_Kong":"HK","Antarctica/McMurdo":"AQ","Africa/Malabo":"GQ","America/Los_Angeles":"US","America/Argentina/Cordoba":"AR","Pacific/Pohnpei":"FM","America/Tijuana":"MX","America/Campo_Grande":"BR","America/Dawson_Creek":"CA","Asia/Novosibirsk":"RU","Pacific/Pago_Pago":"AS","Asia/Jerusalem":"IL","Europe/Sarajevo":"BA","Africa/Freetown":"SL","Asia/Yekaterinburg":"RU","America/Juneau":"US","Africa/Ouagadougou":"BF","Africa/Monrovia":"LR","Europe/Kiev":"UA","America/Argentina/San_Luis":"AR","Asia/Tokyo":"JP","Asia/Qatar":"QA","America/La_Paz":"BO","America/Bogota":"CO","America/Thule":"GL","Asia/Manila":"PH","Asia/Hovd":"MN","Asia/Tehran":"IR","Atlantic/Madeira":"PT","America/Metlakatla":"US","Europe/Vatican":"VA","Asia/Bishkek":"KG","Asia/Dili":"TL","Antarctica/Palmer":"AQ","Atlantic/Cape_Verde":"CV","Indian/Chagos":"IO","America/Kentucky/Monticello":"US","Africa/Algiers":"DZ","Africa/Maseru":"LS","Asia/Kuala_Lumpur":"MY","Africa/Khartoum":"SD","America/Argentina/Rio_Gallegos":"AR","America/Blanc-Sablon":"CA","Africa/Maputo":"MZ","America/Tortola":"VG","Atlantic/Bermuda":"BM","America/Argentina/Catamarca":"AR","America/Cayman":"KY","America/Puerto_Rico":"PR","Pacific/Majuro":"MH","Europe/Busingen":"DE","Pacific/Midway":"UM","Indian/Cocos":"CC","Asia/Singapore":"SG","America/Boise":"US","America/Nuuk":"GL","America/Goose_Bay":"CA","Australia/Broken_Hill":"AU","Africa/Dar_es_Salaam":"TZ","Africa/Asmara":"ER","Asia/Samarkand":"UZ","Asia/Tbilisi":"GE","America/Argentina/Jujuy":"AR","America/Indiana/Winamac":"US","America/Porto_Velho":"BR","Asia/Magadan":"RU","Europe/Zaporozhye":"UA","Antarctica/Casey":"AQ","Asia/Shanghai":"CN","Pacific/Norfolk":"NF","Europe/Guernsey":"GG","Australia/Brisbane":"AU","Antarctica/DumontDUrville":"AQ","America/Havana":"CU","America/Atikokan":"CA","America/Mexico_City":"MX","America/Rankin_Inlet":"CA","America/Cuiaba":"BR","America/Resolute":"CA","Africa/Ceuta":"ES","Arctic/Longyearbyen":"SJ","Pacific/Guam":"GU","Asia/Damascus":"SY","Asia/Colombo":"LK","Asia/Yerevan":"AM","America/Montserrat":"MS","America/Belem":"BR","Europe/Kaliningrad":"RU","Atlantic/South_Georgia":"GS","Asia/Tashkent":"UZ","Asia/Kolkata":"IN","America/St_Johns":"CA","Asia/Srednekolymsk":"RU","Asia/Yakutsk":"RU","Europe/Prague":"CZ","Africa/Djibouti":"DJ","Asia/Dubai":"AE","Europe/Uzhgorod":"UA","America/Edmonton":"CA","Asia/Famagusta":"CY","America/Indiana/Knox":"US","Asia/Hebron":"PS","Asia/Taipei":"TW","Europe/London":"GB","Africa/Dakar":"SN","Australia/Darwin":"AU","America/Glace_Bay":"CA","Antarctica/Vostok":"AQ","America/Indiana/Vincennes":"US","America/Nipigon":"CA","Asia/Kuwait":"KW","Pacific/Guadalcanal":"SB","America/Toronto":"CA","Africa/Gaborone":"BW","Africa/Bujumbura":"BI","Africa/Lubumbashi":"CD","America/Merida":"MX","America/Marigot":"MF","Europe/Zagreb":"HR","Pacific/Easter":"CL","America/Santarem":"BR","Pacific/Noumea":"NC","America/Sitka":"US","Atlantic/Stanley":"FK","Pacific/Funafuti":"TV","America/Iqaluit":"CA","America/Rainy_River":"CA","America/Anchorage":"US","America/Lima":"PE","Asia/Baku":"AZ","America/Indiana/Vevay":"US","Asia/Ulaanbaatar":"MN","America/Managua":"NI","Asia/Krasnoyarsk":"RU","Asia/Qyzylorda":"KZ","America/Eirunepe":"BR","Europe/Podgorica":"ME","Europe/Chisinau":"MD","Europe/Mariehamn":"AX","Europe/Volgograd":"RU","Africa/Nairobi":"KE","Europe/Isle_of_Man":"IM","America/Menominee":"US","Africa/Harare":"ZW","Asia/Anadyr":"RU","America/Moncton":"CA","Indian/Maldives":"MV","America/Whitehorse":"CA","Antarctica/Mawson":"AQ","Europe/Madrid":"ES","America/Argentina/Mendoza":"AR","America/Manaus":"BR","Africa/Bangui":"CF","Indian/Mauritius":"MU","Africa/Tunis":"TN","Australia/Lord_Howe":"AU","America/Kentucky/Louisville":"US","America/North_Dakota/Center":"US","Asia/Novokuznetsk":"RU","Asia/Makassar":"ID","America/Port_of_Spain":"TT","America/Bahia_Banderas":"MX","Pacific/Auckland":"NZ","America/Sao_Paulo":"BR","Asia/Dhaka":"BD","America/Pangnirtung":"CA","Europe/Dublin":"IE","Asia/Brunei":"BN","Africa/Brazzaville":"CG","America/Montevideo":"UY","America/Jamaica":"JM","America/Indiana/Indianapolis":"US","America/Kralendijk":"BQ","Europe/Gibraltar":"GI","Pacific/Marquesas":"PF","Pacific/Apia":"WS","Europe/Jersey":"JE","America/Phoenix":"US","Africa/Ndjamena":"TD","Asia/Karachi":"PK","Africa/Kampala":"UG","Asia/Sakhalin":"RU","America/Martinique":"MQ","Europe/Moscow":"RU","Africa/Conakry":"GN","America/Barbados":"BB","Africa/Lome":"TG","America/Ojinaga":"MX","America/Tegucigalpa":"HN","Asia/Bangkok":"TH","Africa/Johannesburg":"ZA","Europe/Vaduz":"LI","Africa/Sao_Tome":"ST","America/Cambridge_Bay":"CA","America/Lower_Princes":"SX","America/Miquelon":"PM","America/St_Kitts":"KN","Australia/Melbourne":"AU","Europe/Minsk":"BY","Asia/Vladivostok":"RU","Europe/Sofia":"BG","Antarctica/Davis":"AQ","Pacific/Galapagos":"EC","America/North_Dakota/New_Salem":"US","Asia/Amman":"JO","Pacific/Wallis":"WF","America/Hermosillo":"MX","Pacific/Kiritimati":"KI","Antarctica/Macquarie":"AU","America/Guyana":"GY","Asia/Riyadh":"SA","Pacific/Tahiti":"PF","America/St_Vincent":"VC","America/Cancun":"MX","America/Grenada":"GD","Pacific/Wake":"UM","America/Dawson":"CA","Europe/Brussels":"BE","Indian/Kerguelen":"TF","America/Yakutat":"US","Indian/Mahe":"SC","Atlantic/Reykjavik":"IS","America/Panama":"PA","America/Guadeloupe":"GP","Europe/Malta":"MT","Antarctica/Troll":"AQ","Asia/Jayapura":"ID","Asia/Bahrain":"BH","Asia/Chita":"RU","Europe/Tallinn":"EE","Asia/Khandyga":"RU","America/Rio_Branco":"BR","Atlantic/St_Helena":"SH","Africa/Juba":"SS","America/Adak":"US","Pacific/Saipan":"MP","America/St_Lucia":"LC","America/Inuvik":"CA","Europe/Luxembourg":"LU","Africa/Bissau":"GW","Asia/Oral":"KZ","America/Boa_Vista":"BR","Europe/Skopje":"MK","America/Port-au-Prince":"HT","Pacific/Port_Moresby":"PG","Europe/Andorra":"AD","America/Indiana/Marengo":"US","Africa/Kigali":"RW","Africa/Bamako":"ML","America/Dominica":"DM","Asia/Aqtobe":"KZ","Europe/Istanbul":"TR","Pacific/Rarotonga":"CK","America/Danmarkshavn":"GL","Europe/Zurich":"CH","Asia/Yangon":"MM","America/Monterrey":"MX","Europe/Lisbon":"PT","Asia/Kuching":"MY","Antarctica/Rothera":"AQ","Australia/Perth":"AU","Asia/Phnom_Penh":"KH","America/Swift_Current":"CA","Asia/Aqtau":"KZ","Asia/Urumqi":"CN"};
33
const COOKIE_NAME = 'session-id';
4+
const UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
45
let DATASOURCE = 'analytics_events';
56

67
let proxy, token, host;
@@ -22,7 +23,7 @@
2223

2324
function _getSessionId() {
2425
let cookie = {};
25-
document.cookie.split(';').forEach(function(el) {
26+
document.cookie.split(';').forEach(el => {
2627
let [key,value] = el.split('=');
2728
cookie[key.trim()] = value;
2829
})
@@ -119,6 +120,9 @@
119120
// ignore error
120121
}
121122

123+
const searchParams = new URLSearchParams(window.location.search);
124+
const utmValues = UTM_PARAMS.map(param => ({ [param]: searchParams.get(param) ?? '' }))
125+
122126
// Wait a bit for SPA routers
123127
setTimeout(() => {
124128
_sendEvent('page_hit',{
@@ -127,7 +131,8 @@
127131
location: country,
128132
referrer: document.referrer,
129133
pathname: window.location.pathname,
130-
href: window.location.href
134+
href: window.location.href,
135+
...utmValues
131136
});
132137
}, 300);
133138
}

tinybird/endpoints/analytics_hits.pipe

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ SQL >
1818
JSONExtractString(payload, 'referrer') as referrer,
1919
JSONExtractString(payload, 'pathname') as pathname,
2020
JSONExtractString(payload, 'href') as href,
21+
JSONExtractString(payload, 'utm_source') as utm_source,
22+
JSONExtractString(payload, 'utm_medium') as utm_medium,
23+
JSONExtractString(payload, 'utm_campaign') as utm_campaign,
24+
JSONExtractString(payload, 'utm_term') as utm_term,
25+
JSONExtractString(payload, 'utm_content') as utm_content,
2126
lower(JSONExtractString(payload, 'user-agent')) as user_agent
2227
FROM
2328
analytics_events
@@ -34,6 +39,11 @@ SQL >
3439
referrer,
3540
pathname,
3641
href,
42+
utm_source,
43+
utm_medium,
44+
utm_campaign,
45+
utm_term,
46+
utm_content,
3747
case
3848
when match(user_agent, 'wget|ahrefsbot|curl|urllib|bitdiscovery|\+https://|googlebot') then 'bot'
3949
when match(user_agent, 'android') then 'mobile-android'

tinybird/endpoints/top_sources.pipe

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ DESCRIPTION >
1212
SQL >
1313
%
1414
select
15-
domainWithoutWWW(referrer) as referrer,
15+
{% if defined(utm_filter) %}
16+
{{column(utm_filter)}} as utm_filter,
17+
{% else %}
18+
domainWithoutWWW(referrer) as referrer,
19+
{% end %}
1620
uniqMerge(visits) as visits,
1721
countMerge(hits) as hits
1822
from
@@ -28,8 +32,15 @@ SQL >
2832
{% else %}
2933
and date <= today()
3034
{% end %}
35+
{% if defined(utm_filter) %}
36+
and {{column(utm_filter)}} != ''
37+
{% end %}
3138
group by
32-
referrer
39+
{% if defined(utm_filter) %}
40+
{{column(utm_filter)}}
41+
{% else %}
42+
referrer
43+
{% end %}
3344
order by
3445
visits desc
3546
limit {{Int32(skip, 0)}},{{Int32(limit, 50)}}

tinybird/pipes/analytics_sources.pipe

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ SQL >
1111
location,
1212
referrer,
1313
uniqState(session_id) AS visits,
14-
countState() AS hits
14+
countState() AS hits,
15+
utm_source,
16+
utm_medium,
17+
utm_campaign,
18+
utm_term,
19+
utm_content
1520
from
1621
analytics_hits
1722
where
@@ -21,7 +26,12 @@ SQL >
2126
device,
2227
browser,
2328
location,
24-
referrer
29+
referrer,
30+
utm_source,
31+
utm_medium,
32+
utm_campaign,
33+
utm_term,
34+
utm_content
2535

2636
TYPE materialized
2737
DATASOURCE analytics_sources_mv

0 commit comments

Comments
 (0)