Merge branch 'dev' of https://github.com/umami-software/umami into analytics

This commit is contained in:
Francis Cao 2024-07-25 12:33:35 -07:00
commit 3494ae46b7
190 changed files with 2039 additions and 1556 deletions

View File

@ -67,3 +67,111 @@ CREATE TABLE umami.session_data
engine = MergeTree
ORDER BY (website_id, session_id, data_key, created_at)
SETTINGS index_granularity = 8192;
-- stats hourly
CREATE TABLE umami.website_event_stats_hourly
(
website_id UUID,
session_id UUID,
visit_id UUID,
hostname LowCardinality(String),
browser LowCardinality(String),
os LowCardinality(String),
device LowCardinality(String),
screen LowCardinality(String),
language LowCardinality(String),
country LowCardinality(String),
subdivision1 LowCardinality(String),
city String,
entry_url AggregateFunction(argMin, String, DateTime('UTC')),
exit_url AggregateFunction(argMax, String, DateTime('UTC')),
url_path SimpleAggregateFunction(groupArrayArray, Array(String)),
url_query SimpleAggregateFunction(groupArrayArray, Array(String)),
referrer_domain SimpleAggregateFunction(groupArrayArray, Array(String)),
page_title SimpleAggregateFunction(groupArrayArray, Array(String)),
event_type UInt32,
event_name SimpleAggregateFunction(groupArrayArray, Array(String)),
views SimpleAggregateFunction(sum, UInt64),
min_time SimpleAggregateFunction(min, DateTime('UTC')),
max_time SimpleAggregateFunction(max, DateTime('UTC')),
created_at Datetime('UTC')
)
ENGINE = AggregatingMergeTree
PARTITION BY toYYYYMM(created_at)
ORDER BY (
website_id,
event_type,
toStartOfHour(created_at),
cityHash64(visit_id),
visit_id
)
SAMPLE BY cityHash64(visit_id);
CREATE MATERIALIZED VIEW umami.website_event_stats_hourly_mv
TO umami.website_event_stats_hourly
AS
SELECT
website_id,
session_id,
visit_id,
hostname,
browser,
os,
device,
screen,
language,
country,
subdivision1,
city,
entry_url,
exit_url,
url_paths as url_path,
url_query,
referrer_domain,
page_title,
event_type,
event_name,
views,
min_time,
max_time,
timestamp as created_at
FROM (SELECT
website_id,
session_id,
visit_id,
hostname,
browser,
os,
device,
screen,
language,
country,
subdivision1,
city,
argMinState(url_path, created_at) entry_url,
argMaxState(url_path, created_at) exit_url,
arrayFilter(x -> x != '', groupArray(url_path)) as url_paths,
arrayFilter(x -> x != '', groupArray(url_query)) url_query,
arrayFilter(x -> x != '', groupArray(referrer_domain)) referrer_domain,
arrayFilter(x -> x != '', groupArray(page_title)) page_title,
event_type,
if(event_type = 2, groupArray(event_name), []) event_name,
sumIf(1, event_type = 1) views,
min(created_at) min_time,
max(created_at) max_time,
toStartOfHour(created_at) timestamp
FROM umami.website_event
GROUP BY website_id,
session_id,
visit_id,
hostname,
browser,
os,
device,
screen,
language,
country,
subdivision1,
city,
event_type,
timestamp);

View File

@ -168,7 +168,7 @@ const config = {
destination: '/api/scripts/telemetry',
},
{
source: '/teams/:teamId/:path*',
source: '/teams/:teamId/:path((?!settings).*)*',
destination: '/:path*',
},
];

View File

@ -1,6 +1,6 @@
{
"name": "umami",
"version": "2.12.0",
"version": "2.12.1",
"description": "A simple, fast, privacy-focused alternative to Google Analytics.",
"author": "Umami Software, Inc. <hello@umami.is>",
"license": "MIT",
@ -64,9 +64,9 @@
".next/cache"
],
"dependencies": {
"@clickhouse/client": "^1.0.2",
"@clickhouse/client": "^1.3.0",
"@fontsource/inter": "^4.5.15",
"@prisma/client": "5.14.0",
"@prisma/client": "5.16.2",
"@prisma/extension-read-replicas": "^0.3.0",
"@react-spring/web": "^9.7.3",
"@tanstack/react-query": "^5.28.6",
@ -98,11 +98,11 @@
"maxmind": "^4.3.6",
"md5": "^2.3.0",
"moment-timezone": "^0.5.35",
"next": "14.2.3",
"next": "14.2.4",
"next-basics": "^0.39.0",
"node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5",
"prisma": "5.14.0",
"prisma": "5.16.2",
"react": "^18.2.0",
"react-basics": "^0.123.0",
"react-beautiful-dnd": "^13.1.0",
@ -175,6 +175,6 @@
"tar": "^6.1.2",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.1",
"typescript": "^5.4.3"
"typescript": "^5.5.3"
}
}

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -152,7 +152,7 @@
"label.compare": [
{
"type": 0,
"value": "Compare"
"value": "Comparar"
}
],
"label.confirm": [
@ -182,7 +182,7 @@
"label.count": [
{
"type": 0,
"value": "Count"
"value": "Recompte"
}
],
"label.countries": [
@ -236,7 +236,7 @@
"label.current": [
{
"type": 0,
"value": "Current"
"value": "Actual"
}
],
"label.current-password": [
@ -398,13 +398,13 @@
"label.end-step": [
{
"type": 0,
"value": "End Step"
"value": "Pas Final"
}
],
"label.entry": [
{
"type": 0,
"value": "Entry URL"
"value": "URL d'entrada"
}
],
"label.event": [
@ -428,7 +428,7 @@
"label.exit": [
{
"type": 0,
"value": "Exit URL"
"value": "URL de sortida"
}
],
"label.false": [
@ -488,19 +488,19 @@
"label.goal": [
{
"type": 0,
"value": "Goal"
"value": "Meta"
}
],
"label.goals": [
{
"type": 0,
"value": "Goals"
"value": "Metes"
}
],
"label.goals-description": [
{
"type": 0,
"value": "Track your goals for pageviews and events."
"value": "Feu un seguiment de les seves metes per a pàgines vistes i esdeveniments."
}
],
"label.greater-than": [
@ -518,13 +518,13 @@
"label.host": [
{
"type": 0,
"value": "Host"
"value": "Amfitrió"
}
],
"label.hosts": [
{
"type": 0,
"value": "Hosts"
"value": "Amfitrions"
}
],
"label.insights": [
@ -578,13 +578,13 @@
"label.journey": [
{
"type": 0,
"value": "Journey"
"value": "Trajecte"
}
],
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Entengui com naveguen els usuaris pel seu lloc web."
}
],
"label.language": [
@ -777,7 +777,7 @@
"value": [
{
"type": 0,
"value": "record"
"value": "registre"
}
]
},
@ -785,7 +785,7 @@
"value": [
{
"type": 0,
"value": "records"
"value": "registres"
}
]
}
@ -874,19 +874,19 @@
"label.previous": [
{
"type": 0,
"value": "Previous"
"value": "Anterior"
}
],
"label.previous-period": [
{
"type": 0,
"value": "Previous period"
"value": "Període anterior"
}
],
"label.previous-year": [
{
"type": 0,
"value": "Previous year"
"value": "Any anterior"
}
],
"label.profile": [
@ -898,7 +898,7 @@
"label.property": [
{
"type": 0,
"value": "Property"
"value": "Propietat"
}
],
"label.queries": [
@ -1090,7 +1090,7 @@
"label.start-step": [
{
"type": 0,
"value": "Start Step"
"value": "Pas inicial"
}
],
"label.steps": [
@ -1360,7 +1360,7 @@
"label.views-per-visit": [
{
"type": 0,
"value": "Views per visit"
"value": "Vistes per visita"
}
],
"label.visit-duration": [
@ -1462,7 +1462,7 @@
"message.collected-data": [
{
"type": 0,
"value": "Collected data"
"value": "Dades recol·lectades"
}
],
"message.confirm-delete": [
@ -1790,7 +1790,7 @@
"message.visitors-dropped-off": [
{
"type": 0,
"value": "Els visitants han sortit"
"value": "Visitants han sortit"
}
]
}

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -32,13 +32,13 @@
"label.add-member": [
{
"type": 0,
"value": "Add member"
"value": "Mitglied hinzufügen"
}
],
"label.add-step": [
{
"type": 0,
"value": "Add step"
"value": "Schritt hinzufügen"
}
],
"label.add-website": [
@ -104,7 +104,7 @@
"label.breakdown": [
{
"type": 0,
"value": "Breakdown"
"value": "Aufschlüsselung"
}
],
"label.browser": [
@ -152,7 +152,7 @@
"label.compare": [
{
"type": 0,
"value": "Compare"
"value": "Vergleich"
}
],
"label.confirm": [
@ -182,7 +182,7 @@
"label.count": [
{
"type": 0,
"value": "Count"
"value": "Anzahl"
}
],
"label.countries": [
@ -200,7 +200,7 @@
"label.create": [
{
"type": 0,
"value": "Create"
"value": "Erstellen"
}
],
"label.create-report": [
@ -230,13 +230,13 @@
"label.created-by": [
{
"type": 0,
"value": "Created By"
"value": "Erstellt von"
}
],
"label.current": [
{
"type": 0,
"value": "Current"
"value": "Aktuell"
}
],
"label.current-password": [
@ -296,7 +296,7 @@
"label.delete-report": [
{
"type": 0,
"value": "Delete report"
"value": "Bericht löschen"
}
],
"label.delete-team": [
@ -386,7 +386,7 @@
"label.edit-member": [
{
"type": 0,
"value": "Edit member"
"value": "Mitglied bearbeiten"
}
],
"label.enable-share-url": [
@ -398,7 +398,7 @@
"label.end-step": [
{
"type": 0,
"value": "End Step"
"value": "Schritt beenden"
}
],
"label.entry": [
@ -482,25 +482,25 @@
"label.funnel-description": [
{
"type": 0,
"value": "Understand the conversion and drop-off rate of users."
"value": "Verstehe die Konversions- und Dropoffrate von Nutzern."
}
],
"label.goal": [
{
"type": 0,
"value": "Goal"
"value": "Ziel"
}
],
"label.goals": [
{
"type": 0,
"value": "Goals"
"value": "Ziele"
}
],
"label.goals-description": [
{
"type": 0,
"value": "Track your goals for pageviews and events."
"value": "Verfolgen Sie Ihre Ziele für Aufrufe und Events."
}
],
"label.greater-than": [
@ -536,7 +536,7 @@
"label.insights-description": [
{
"type": 0,
"value": "Dive deeper into your data by using segments and filters."
"value": "Tauchen Sie tiefer in Ihre Daten mit Filtern und Segmenten ein."
}
],
"label.is": [
@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Verstehen Sie, wie Nutzer Ihre Website navigieren."
}
],
"label.language": [
@ -686,7 +686,7 @@
"label.manage": [
{
"type": 0,
"value": "Manage"
"value": "Verwalten"
}
],
"label.manager": [
@ -704,7 +704,7 @@
"label.member": [
{
"type": 0,
"value": "Member"
"value": "Mitglied"
}
],
"label.members": [
@ -734,7 +734,7 @@
"label.my-account": [
{
"type": 0,
"value": "My account"
"value": "Mein Konto"
}
],
"label.my-websites": [
@ -874,19 +874,19 @@
"label.previous": [
{
"type": 0,
"value": "Previous"
"value": "Vorherige"
}
],
"label.previous-period": [
{
"type": 0,
"value": "Previous period"
"value": "Vorheriger Zeitraum"
}
],
"label.previous-year": [
{
"type": 0,
"value": "Previous year"
"value": "Vorheriges Jahr"
}
],
"label.profile": [
@ -898,7 +898,7 @@
"label.property": [
{
"type": 0,
"value": "Property"
"value": "Besitz"
}
],
"label.queries": [
@ -970,7 +970,7 @@
"label.remove-member": [
{
"type": 0,
"value": "Remove member"
"value": "Mitglied entfernen"
}
],
"label.reports": [
@ -1006,7 +1006,7 @@
"label.retention-description": [
{
"type": 0,
"value": "Measure your website stickiness by tracking how often users return."
"value": "Messen Sie die Presenz Ihrer Website, indem Sie tracken wie oft Nutzer zurückkehren."
}
],
"label.role": [
@ -1036,13 +1036,13 @@
"label.search": [
{
"type": 0,
"value": "Search"
"value": "Suche"
}
],
"label.select": [
{
"type": 0,
"value": "Select"
"value": "Auswählen"
}
],
"label.select-date": [
@ -1054,7 +1054,7 @@
"label.select-role": [
{
"type": 0,
"value": "Select role"
"value": "Rolle auswählen"
}
],
"label.select-website": [
@ -1090,13 +1090,13 @@
"label.start-step": [
{
"type": 0,
"value": "Start Step"
"value": "Schritt starten"
}
],
"label.steps": [
{
"type": 0,
"value": "Steps"
"value": "Schritte"
}
],
"label.sum": [
@ -1126,7 +1126,7 @@
"label.team-manager": [
{
"type": 0,
"value": "Team manager"
"value": "Team-Manager"
}
],
"label.team-member": [
@ -1234,13 +1234,13 @@
"label.transfer": [
{
"type": 0,
"value": "Transfer"
"value": "Übertragung"
}
],
"label.transfer-website": [
{
"type": 0,
"value": "Transfer website"
"value": "Website übertragen"
}
],
"label.true": [
@ -1324,7 +1324,7 @@
"label.utm-description": [
{
"type": 0,
"value": "Track your campaigns through UTM parameters."
"value": "Tracken Sie Ihre Kampagnen mit Hilfe von UTM Parametern."
}
],
"label.value": [
@ -1360,7 +1360,7 @@
"label.views-per-visit": [
{
"type": 0,
"value": "Views per visit"
"value": "Aufrufe pro Besuch"
}
],
"label.visit-duration": [
@ -1378,7 +1378,7 @@
"label.visits": [
{
"type": 0,
"value": "Visits"
"value": "Besuche"
}
],
"label.website": [
@ -1414,7 +1414,7 @@
"message.action-confirmation": [
{
"type": 0,
"value": "Type "
"value": "Tippen Sie "
},
{
"type": 1,
@ -1422,7 +1422,7 @@
},
{
"type": 0,
"value": " in the box below to confirm."
"value": " in das untenliegende Feld, um zu bestätigen."
}
],
"message.active-users": [
@ -1462,7 +1462,7 @@
"message.collected-data": [
{
"type": 0,
"value": "Collected data"
"value": "Gesammelte Daten"
}
],
"message.confirm-delete": [
@ -1496,7 +1496,7 @@
"message.confirm-remove": [
{
"type": 0,
"value": "Are you sure you want to remove "
"value": "Sind Sie sicher, dass Sie "
},
{
"type": 1,
@ -1504,7 +1504,7 @@
},
{
"type": 0,
"value": "?"
"value": " entfernen möchten?"
}
],
"message.confirm-reset": [
@ -1524,7 +1524,7 @@
"message.delete-team-warning": [
{
"type": 0,
"value": "Deleting a team will also delete all team websites."
"value": "Alle zugehörigen Websiten werden ebenfalls gelöscht."
}
],
"message.delete-website-warning": [
@ -1708,25 +1708,25 @@
"message.transfer-team-website-to-user": [
{
"type": 0,
"value": "Transfer this website to your account?"
"value": "Möchten Sie diese Website auf Ihr Konto übertragen?"
}
],
"message.transfer-user-website-to-team": [
{
"type": 0,
"value": "Select the team to transfer this website to."
"value": "Wählen Sie das Team, auf das die Website übertragen wird."
}
],
"message.transfer-website": [
{
"type": 0,
"value": "Transfer website ownership to your account or another team."
"value": "Übertragen Sie den Besitz der Website auf Ihren Account oder ein anderes Team."
}
],
"message.triggered-event": [
{
"type": 0,
"value": "Triggered event"
"value": "Event ausgelöst"
}
],
"message.user-deleted": [
@ -1738,7 +1738,7 @@
"message.viewed-page": [
{
"type": 0,
"value": "Viewed page"
"value": "Seite besucht"
}
],
"message.visitor-log": [
@ -1778,7 +1778,7 @@
"message.visitors-dropped-off": [
{
"type": 0,
"value": "Visitors dropped off"
"value": "Besucher haben die Seite verlassen"
}
]
}

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

File diff suppressed because it is too large Load Diff

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -152,7 +152,7 @@
"label.compare": [
{
"type": 0,
"value": "Compare"
"value": "Porównaj"
}
],
"label.confirm": [
@ -182,7 +182,7 @@
"label.count": [
{
"type": 0,
"value": "Count"
"value": "Liczba"
}
],
"label.countries": [
@ -236,7 +236,7 @@
"label.current": [
{
"type": 0,
"value": "Current"
"value": "Aktualny"
}
],
"label.current-password": [
@ -398,7 +398,7 @@
"label.end-step": [
{
"type": 0,
"value": "End Step"
"value": "Krok końcowy"
}
],
"label.entry": [
@ -428,7 +428,7 @@
"label.exit": [
{
"type": 0,
"value": "Exit URL"
"value": "URL wyjściowy"
}
],
"label.false": [
@ -488,13 +488,13 @@
"label.goal": [
{
"type": 0,
"value": "Goal"
"value": "Cel"
}
],
"label.goals": [
{
"type": 0,
"value": "Goals"
"value": "Cele"
}
],
"label.goals-description": [
@ -524,7 +524,7 @@
"label.hosts": [
{
"type": 0,
"value": "Hosts"
"value": "Hosty"
}
],
"label.insights": [
@ -578,13 +578,13 @@
"label.journey": [
{
"type": 0,
"value": "Journey"
"value": "Droga"
}
],
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Zrozum, w jaki sposób użytkownicy poruszają się po Twojej witrynie."
}
],
"label.language": [
@ -874,19 +874,19 @@
"label.previous": [
{
"type": 0,
"value": "Previous"
"value": "Poprzedni"
}
],
"label.previous-period": [
{
"type": 0,
"value": "Previous period"
"value": "Poprzedni okres"
}
],
"label.previous-year": [
{
"type": 0,
"value": "Previous year"
"value": "Poprzedni rok"
}
],
"label.profile": [
@ -928,7 +928,7 @@
"label.referrer": [
{
"type": 0,
"value": "Referrer"
"value": "Źródło odsyłające"
}
],
"label.referrers": [
@ -970,7 +970,7 @@
"label.remove-member": [
{
"type": 0,
"value": "Remove member"
"value": "Usuń członka"
}
],
"label.reports": [
@ -1090,7 +1090,7 @@
"label.start-step": [
{
"type": 0,
"value": "Start Step"
"value": "Krok startowy"
}
],
"label.steps": [
@ -1378,7 +1378,7 @@
"label.visits": [
{
"type": 0,
"value": "Odwiedząjący"
"value": "Wizyty"
}
],
"label.website": [
@ -1462,7 +1462,7 @@
"message.collected-data": [
{
"type": 0,
"value": "Collected data"
"value": "Zebrane dane"
}
],
"message.confirm-delete": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -566,7 +566,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -152,7 +152,7 @@
"label.compare": [
{
"type": 0,
"value": "Compare"
"value": "比较"
}
],
"label.confirm": [
@ -182,7 +182,7 @@
"label.count": [
{
"type": 0,
"value": "Count"
"value": "统计"
}
],
"label.countries": [
@ -236,7 +236,7 @@
"label.current": [
{
"type": 0,
"value": "Current"
"value": "目前"
}
],
"label.current-password": [
@ -398,13 +398,13 @@
"label.end-step": [
{
"type": 0,
"value": "End Step"
"value": "结束步骤"
}
],
"label.entry": [
{
"type": 0,
"value": "Entry URL"
"value": "入口 URL"
}
],
"label.event": [
@ -428,7 +428,7 @@
"label.exit": [
{
"type": 0,
"value": "Exit URL"
"value": "退出 URL"
}
],
"label.false": [
@ -488,19 +488,19 @@
"label.goal": [
{
"type": 0,
"value": "Goal"
"value": "目标"
}
],
"label.goals": [
{
"type": 0,
"value": "Goals"
"value": "目标"
}
],
"label.goals-description": [
{
"type": 0,
"value": "Track your goals for pageviews and events."
"value": "跟踪页面浏览量和事件的目标。"
}
],
"label.greater-than": [
@ -518,13 +518,13 @@
"label.host": [
{
"type": 0,
"value": "Host"
"value": "主机"
}
],
"label.hosts": [
{
"type": 0,
"value": "Hosts"
"value": "主机"
}
],
"label.insights": [
@ -578,13 +578,13 @@
"label.journey": [
{
"type": 0,
"value": "Journey"
"value": "用户浏览轨迹"
}
],
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "了解用户如何浏览网站。"
}
],
"label.language": [
@ -692,7 +692,7 @@
"label.manager": [
{
"type": 0,
"value": "Manager"
"value": "管理者"
}
],
"label.max": [
@ -798,7 +798,7 @@
"label.ok": [
{
"type": 0,
"value": "OK"
"value": "好的"
}
],
"label.os": [
@ -882,19 +882,19 @@
"label.previous": [
{
"type": 0,
"value": "Previous"
"value": "先前"
}
],
"label.previous-period": [
{
"type": 0,
"value": "Previous period"
"value": "上一时期"
}
],
"label.previous-year": [
{
"type": 0,
"value": "Previous year"
"value": "上一年"
}
],
"label.profile": [
@ -906,7 +906,7 @@
"label.property": [
{
"type": 0,
"value": "Property"
"value": "属性"
}
],
"label.queries": [
@ -1098,7 +1098,7 @@
"label.start-step": [
{
"type": 0,
"value": "Start Step"
"value": "开始步骤"
}
],
"label.steps": [
@ -1134,7 +1134,7 @@
"label.team-manager": [
{
"type": 0,
"value": "Team manager"
"value": "团队管理者"
}
],
"label.team-member": [
@ -1450,7 +1450,7 @@
"message.collected-data": [
{
"type": 0,
"value": "Collected data"
"value": "已收集的数据"
}
],
"message.confirm-delete": [

View File

@ -584,7 +584,7 @@
"label.journey-description": [
{
"type": 0,
"value": "Understand how users nagivate through your website."
"value": "Understand how users navigate through your website."
}
],
"label.language": [

View File

@ -44,7 +44,7 @@ async function checkConnection() {
success('Database connection successful.');
} catch (e) {
throw new Error('Unable to connect to the database.');
throw new Error('Unable to connect to the database: ' + e.message);
}
}

View File

@ -2,7 +2,6 @@ require('dotenv').config();
const cli = require('next/dist/cli/next-start');
cli.nextStart({
'--port': process.env.PORT || 3000,
'--hostname': process.env.HOSTNAME || '0.0.0.0',
_: [],
port: process.env.PORT || 3000,
hostname: process.env.HOSTNAME || '0.0.0.0',
});

View File

@ -21,8 +21,12 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
router.push(`/console/${value}`);
}
function handleClick() {
window['umami'].track({ url: '/page-view', referrer: 'https://www.google.com' });
function handleRunScript() {
window['umami'].track(props => ({
...props,
url: '/page-view',
referrer: 'https://www.google.com',
}));
window['umami'].track('track-event-no-data');
window['umami'].track('track-event-with-data', {
test: 'test-data',
@ -44,7 +48,7 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
});
}
function handleIdentifyClick() {
function handleRunIdentify() {
window['umami'].identify({
userId: 123,
name: 'brian',
@ -145,10 +149,10 @@ export function TestConsole({ websiteId }: { websiteId: string }) {
</div>
<div className={styles.group}>
<div className={styles.header}>Javascript events</div>
<Button id="manual-button" variant="primary" onClick={handleClick}>
<Button id="manual-button" variant="primary" onClick={handleRunScript}>
Run script
</Button>
<Button id="manual-button" variant="primary" onClick={handleIdentifyClick}>
<Button id="manual-button" variant="primary" onClick={handleRunIdentify}>
Run identify
</Button>
</div>

View File

@ -51,6 +51,12 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean })
url: renderTeamUrl('/reports/journey'),
icon: <Path />,
},
// {
// title: formatMessage(labels.revenue),
// description: formatMessage(labels.revenueDescription),
// url: renderTeamUrl('/reports/revenue'),
// icon: <Path />,
// },
];
return (

View File

@ -34,6 +34,10 @@
background-color: var(--base100);
}
.step:last-child::before {
display: none;
}
.card {
display: grid;
gap: 20px;

View File

@ -121,7 +121,7 @@
}
.name {
font-weight: 500;
max-width: 200px;
}
.count {

View File

@ -1,5 +1,5 @@
import { useContext, useMemo, useState } from 'react';
import { TooltipPopup } from 'react-basics';
import { TextOverflow, TooltipPopup } from 'react-basics';
import { firstBy } from 'thenby';
import classNames from 'classnames';
import { useEscapeKey, useMessages } from 'components/hooks';
@ -191,7 +191,9 @@ export default function JourneyView() {
})}
onClick={() => handleClick(name, columnIndex, paths)}
>
<div className={styles.name}>{name}</div>
<div className={styles.name} title={name}>
<TextOverflow> {name}</TextOverflow>
</div>
<TooltipPopup label={dropOffPercent} disabled={!selected}>
<div className={styles.count} title={nodeCount}>
{formatLongNumber(nodeCount)}

View File

@ -1,8 +1,13 @@
import TeamProvider from './TeamProvider';
import { Metadata } from 'next';
import TeamSettingsLayout from './settings/TeamSettingsLayout';
export default function ({ children, params: { teamId } }) {
return <TeamProvider teamId={teamId}>{children}</TeamProvider>;
return (
<TeamProvider teamId={teamId}>
<TeamSettingsLayout>{children}</TeamSettingsLayout>
</TeamProvider>
);
}
export const metadata: Metadata = {

View File

@ -0,0 +1,29 @@
'use client';
import { ReactNode } from 'react';
import { useMessages, useTeamUrl } from 'components/hooks';
import MenuLayout from 'components/layout/MenuLayout';
export default function TeamSettingsLayout({ children }: { children: ReactNode }) {
const { formatMessage, labels } = useMessages();
const { teamId } = useTeamUrl();
const items = [
{
key: 'team',
label: formatMessage(labels.team),
url: `/teams/${teamId}/settings/team`,
},
{
key: 'websites',
label: formatMessage(labels.websites),
url: `/teams/${teamId}/settings/websites`,
},
{
key: 'members',
label: formatMessage(labels.members),
url: `/teams/${teamId}/settings/members`,
},
].filter(n => n);
return <MenuLayout items={items}>{children}</MenuLayout>;
}

View File

@ -5,7 +5,7 @@ import PageHeader from 'components/layout/PageHeader';
import { ROLES } from 'lib/constants';
import { useContext, useState } from 'react';
import { Flexbox, Item, Tabs } from 'react-basics';
import TeamLeaveButton from '../../TeamLeaveButton';
import TeamLeaveButton from 'app/(main)/settings/teams/TeamLeaveButton';
import TeamManage from './TeamManage';
import TeamEditForm from './TeamEditForm';

View File

@ -11,7 +11,7 @@ import {
} from 'react-basics';
import { getRandomChars } from 'next-basics';
import { useContext, useRef, useState } from 'react';
import { useApi, useMessages } from 'components/hooks';
import { useApi, useMessages, useModified } from 'components/hooks';
import { TeamContext } from 'app/(main)/teams/[teamId]/TeamProvider';
const generateId = () => getRandomChars(16);
@ -26,12 +26,14 @@ export function TeamEditForm({ teamId, allowEdit }: { teamId: string; allowEdit?
const ref = useRef(null);
const [accessCode, setAccessCode] = useState(team.accessCode);
const { showToast } = useToasts();
const { touch } = useModified();
const cloudMode = !!process.env.cloudMode;
const handleSubmit = async (data: any) => {
mutate(data, {
onSuccess: async () => {
ref.current.reset(data);
touch('teams');
showToast({ message: formatMessage(messages.saved), variant: 'success' });
},
});

View File

@ -0,0 +1,5 @@
import Page from 'app/(main)/settings/websites/[websiteId]/page';
export default function ({ params }) {
return <Page params={params} />;
}

View File

@ -45,10 +45,15 @@ export function WebsiteHeader({
icon: <Icons.Reports />,
path: '/reports',
},
// {
// label: formatMessage(labels.sessions),
// icon: <Icons.User />,
// path: '/sessions',
// },
{
label: formatMessage(labels.eventData),
label: formatMessage(labels.events),
icon: <Icons.Nodes />,
path: '/event-data',
path: '/events',
},
];

View File

@ -54,7 +54,7 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
},
];
const getTime = ({ timestamp }) => format(timestamp * 1000, 'h:mm:ss');
const getTime = ({ createdAt }) => format(new Date(createdAt), 'h:mm:ss');
const getColor = ({ id, sessionId }) => stringToColor(sessionId || id);
@ -144,9 +144,12 @@ export function RealtimeLog({ data }: { data: RealtimeData }) {
const { events, visitors } = data;
let logs = [
...events.map(e => ({ __type: e.eventName ? TYPE_EVENT : TYPE_PAGEVIEW, ...e })),
...events.map(e => ({
__type: e.eventName ? TYPE_EVENT : TYPE_PAGEVIEW,
...e,
})),
...visitors.map(v => ({ __type: TYPE_SESSION, ...v })),
].sort(thenby.firstBy('timestamp', -1));
].sort(thenby.firstBy('createdAt', -1));
if (search) {
logs = logs.filter(({ eventName, urlPath, browser, os, country, device }) => {

View File

@ -0,0 +1,5 @@
export function SessionMetricsBar({ websiteId }: { websiteId: string }) {
return <h1>{websiteId}</h1>;
}
export default SessionMetricsBar;

View File

@ -0,0 +1,25 @@
import { useSessions } from 'components/hooks';
import SessionsTable from './SessionsTable';
import DataTable from 'components/common/DataTable';
import { ReactNode } from 'react';
export default function SessionsDataTable({
websiteId,
children,
}: {
websiteId?: string;
teamId?: string;
children?: ReactNode;
}) {
const queryResult = useSessions(websiteId);
if (queryResult?.result?.data?.length === 0) {
return children;
}
return (
<DataTable queryResult={queryResult} allowSearch={false}>
{({ data }) => <SessionsTable data={data} showDomain={!websiteId} />}
</DataTable>
);
}

View File

@ -0,0 +1,14 @@
'use client';
import WebsiteHeader from '../WebsiteHeader';
import SessionsDataTable from './SessionsDataTable';
export function SessionsPage({ websiteId }) {
return (
<>
<WebsiteHeader websiteId={websiteId} />
<SessionsDataTable websiteId={websiteId} />
</>
);
}
export default SessionsPage;

View File

@ -0,0 +1,31 @@
import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
import { useFormat, useMessages } from 'components/hooks';
import { formatDistanceToNow } from 'date-fns';
export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) {
const { formatMessage, labels } = useMessages();
const breakpoint = useBreakpoint();
const { formatValue } = useFormat();
return (
<GridTable data={data} cardMode={['xs', 'sm', 'md'].includes(breakpoint)}>
<GridColumn name="id" label="ID" />
<GridColumn name="country" label={formatMessage(labels.country)}>
{row => formatValue(row.country, 'country')}
</GridColumn>
<GridColumn name="city" label={formatMessage(labels.city)} />
<GridColumn name="browser" label={formatMessage(labels.browser)}>
{row => formatValue(row.browser, 'browser')}
</GridColumn>
<GridColumn name="os" label={formatMessage(labels.os)} />
<GridColumn name="device" label={formatMessage(labels.device)}>
{row => formatValue(row.device, 'device')}
</GridColumn>
<GridColumn name="createdAt" label={formatMessage(labels.created)}>
{row => formatDistanceToNow(new Date(row.createdAt))}
</GridColumn>
</GridTable>
);
}
export default SessionsTable;

View File

@ -0,0 +1,10 @@
import SessionsPage from './SessionsPage';
import { Metadata } from 'next';
export default function ({ params: { websiteId } }) {
return <SessionsPage websiteId={websiteId} />;
}
export const metadata: Metadata = {
title: 'Sessions',
};

View File

@ -0,0 +1,28 @@
import { CURRENT_VERSION, TELEMETRY_PIXEL } from 'lib/constants';
export async function GET() {
if (
process.env.NODE_ENV !== 'production' &&
process.env.DISABLE_TELEMETRY &&
process.env.PRIVATE_MODE
) {
const script = `
(()=>{const i=document.createElement('img');
i.setAttribute('src','${TELEMETRY_PIXEL}?v=${CURRENT_VERSION}');
i.setAttribute('style','width:0;height:0;position:absolute;pointer-events:none;');
document.body.appendChild(i);})();
`;
return new Response(script.replace(/\s\s+/g, ''), {
headers: {
'content-type': 'text/javascript',
},
});
}
return new Response('/* telemetry disabled */', {
headers: {
'content-type': 'text/javascript',
},
});
}

View File

@ -79,18 +79,20 @@ export function Chart({
};
const updateChart = (data: any) => {
if (data.datasets.length === chart.current.data.datasets.length) {
chart.current.data.datasets.forEach((dataset: { data: any }, index: string | number) => {
if (data?.datasets[index]) {
dataset.data = data?.datasets[index]?.data;
if (data.datasets) {
if (data.datasets.length === chart.current.data.datasets.length) {
chart.current.data.datasets.forEach((dataset: { data: any }, index: string | number) => {
if (data?.datasets[index]) {
dataset.data = data?.datasets[index]?.data;
if (chart.current.legend.legendItems[index]) {
chart.current.legend.legendItems[index].text = data?.datasets[index]?.label;
if (chart.current.legend.legendItems[index]) {
chart.current.legend.legendItems[index].text = data?.datasets[index]?.label;
}
}
}
});
} else {
chart.current.data.datasets = data.datasets;
});
} else {
chart.current.data.datasets = data.datasets;
}
}
chart.current.options = options;

Some files were not shown because too many files have changed in this diff Show More